@@ -40,67 +40,73 @@ inline manticore::Executor& executor() {
4040
4141/* *
4242 * @tparam Function_ Function to be executed.
43+ * @tparam Index_ Integer type for the job indices.
4344 *
44- * @param njobs Number of jobs to be executed.
4545 * @param fun Function to run in each thread.
4646 * This is a lambda that should accept three arguments:
47- * - Integer containing the thread ID
47+ * - Integer containing the thread ID.
4848 * - Integer specifying the index of the first job to be executed in a thread.
4949 * - Integer specifying the number of jobs to be executed in a thread.
50+ * @param njobs Number of jobs to be executed.
5051 * @param nthreads Number of threads to parallelize over.
5152 *
53+ * This function is a drop-in replacement for `tatami::parallelize()`.
5254 * The series of integers from 0 to `njobs - 1` is split into `nthreads` contiguous ranges.
5355 * Each range is used as input to `fun` within the corresponding thread.
5456 * It is assumed that the execution of any given job is independent of the next.
5557 *
5658 * This function is only available if `TATAMI_R_PARALLELIZE_UNKNOWN` is defined.
5759 */
58- template <class Function_ >
59- void parallelize (Function_ fun, size_t njobs, size_t nthreads) {
60- if (nthreads == 1 || njobs == 1 ) {
60+ template <class Function_ , class Index_ >
61+ void parallelize (Function_ fun, Index_ njobs, int nthreads) {
62+ if (njobs == 0 ) {
63+ return ;
64+ }
65+
66+ if (nthreads <= 1 || njobs == 1 ) {
6167 fun (0 , 0 , njobs);
6268 return ;
6369 }
6470
71+ Index_ jobs_per_worker = njobs / nthreads;
72+ int remainder = njobs % nthreads;
73+ if (jobs_per_worker == 0 ) {
74+ jobs_per_worker = 1 ;
75+ remainder = 0 ;
76+ nthreads = njobs;
77+ }
78+
6579 auto & mexec = executor ();
6680 mexec.initialize (nthreads, " failed to execute R command" );
6781
68- size_t jobs_per_worker = (njobs / nthreads) + (njobs % nthreads > 0 );
69- size_t start = 0 ;
70-
7182 std::vector<std::thread> runners;
7283 runners.reserve (nthreads);
73- std::vector<std::string > errors (nthreads);
84+ std::vector<std::exception_ptr > errors (nthreads);
7485
75- for (size_t w = 0 ; w < nthreads; ++w) {
76- if (start == njobs) {
77- mexec.finish_thread (false );
78- continue ;
79- }
80- size_t end = start + std::min (njobs - start, jobs_per_worker);
86+ Index_ start = 0 ;
87+ for (int w = 0 ; w < nthreads; ++w) {
88+ Index_ length = jobs_per_worker + (w < remainder);
8189
82- runners.emplace_back ([&](size_t id, size_t s, size_t l) -> void {
90+ runners.emplace_back ([&](int id, Index_ s, Index_ l) {
8391 try {
8492 fun (id, s, l);
85- } catch (std::exception& x) {
86- // No throw here, we need to make sure we mark the
87- // thread as being completed so that the main loop can quit.
88- errors[id] = x.what ();
93+ } catch (...) {
94+ errors[id] = std::current_exception ();
8995 }
9096 mexec.finish_thread ();
91- }, w, start, end - start );
97+ }, w, start, length );
9298
93- start = end ;
99+ start += length ;
94100 }
95101
96102 mexec.listen ();
97103 for (auto & x : runners) {
98104 x.join ();
99105 }
100106
101- for (auto err : errors) {
102- if (! err. empty () ) {
103- throw std::runtime_error (err);
107+ for (const auto & err : errors) {
108+ if (err) {
109+ std::rethrow_exception (err);
104110 }
105111 }
106112}
0 commit comments