|
| 1 | +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> |
| 2 | +<html> |
| 3 | +<head> |
| 4 | +<meta http-equiv="content-type" content="text/html; charset=UTF-8"> |
| 5 | +<title>/Volumes/Unix/unix-files.noindex/ntl-new/ntl-9.6.0/doc/BasicThreadPool.cpp.html</title> |
| 6 | +<meta name="Generator" content="Vim/7.3"> |
| 7 | +<meta name="plugin-version" content="vim7.3_v6"> |
| 8 | +<meta name="syntax" content="cpp"> |
| 9 | +<meta name="settings" content="use_css"> |
| 10 | +<style type="text/css"> |
| 11 | +<!-- |
| 12 | +pre { font-family: monospace; color: #000000; background-color: #ffffff; } |
| 13 | +body { font-family: monospace; color: #000000; background-color: #ffffff; } |
| 14 | +.Constant { color: #ff8c00; } |
| 15 | +.Statement { color: #b03060; font-weight: bold; } |
| 16 | +.Type { color: #008b00; font-weight: bold; } |
| 17 | +.Comment { color: #0000ee; font-style: italic; } |
| 18 | +--> |
| 19 | +</style> |
| 20 | +</head> |
| 21 | +<body> |
| 22 | +<pre> |
| 23 | + |
| 24 | + |
| 25 | +<span class="Comment">/*</span><span class="Comment">***********************************************************************</span> |
| 26 | + |
| 27 | +<span class="Comment">MODULE: BasicThreadPool</span> |
| 28 | + |
| 29 | +<span class="Comment">SUMMARY:</span> |
| 30 | + |
| 31 | +<span class="Comment">Some simple thread pooling.</span> |
| 32 | + |
| 33 | +<span class="Comment">You create a thread pool by constructing a BasicThreadPool object.</span> |
| 34 | +<span class="Comment">For example:</span> |
| 35 | + |
| 36 | +<span class="Comment"> long nthreads = 4;</span> |
| 37 | +<span class="Comment"> BasicThreadPool pool(nthreads);</span> |
| 38 | + |
| 39 | +<span class="Comment">creates a thread pool of 4 threads. These threads will exist</span> |
| 40 | +<span class="Comment">until the destructor for pool is called. </span> |
| 41 | + |
| 42 | +<span class="Comment">The simplest way to use a thread pools is as follows.</span> |
| 43 | +<span class="Comment">Suppose you have a task that consists of N subtasks,</span> |
| 44 | +<span class="Comment">indexed 0..N-1. Then you can write:</span> |
| 45 | + |
| 46 | + |
| 47 | +<span class="Comment"> pool.exec_range(N, </span> |
| 48 | +<span class="Comment"> [&](long first, long last) {</span> |
| 49 | +<span class="Comment"> for (long i = first; i < last; i++) {</span> |
| 50 | +<span class="Comment"> ... code to process subtask i ...</span> |
| 51 | +<span class="Comment"> }</span> |
| 52 | +<span class="Comment"> }</span> |
| 53 | +<span class="Comment"> );</span> |
| 54 | + |
| 55 | +<span class="Comment">The second argument to exec_range is a C++11 "lambda".</span> |
| 56 | +<span class="Comment">The "[&]" indicates that all local variables in the calling</span> |
| 57 | +<span class="Comment">context are captured by reference, so the lambda body can </span> |
| 58 | +<span class="Comment">reference all visible local variables directly. C++11 provides</span> |
| 59 | +<span class="Comment">other methods for capturing local variables.</span> |
| 60 | + |
| 61 | +<span class="Comment">As a more concrete example, we could parallelize the following</span> |
| 62 | +<span class="Comment">calculation:</span> |
| 63 | + |
| 64 | +<span class="Comment"> void mul(ZZ *x, const ZZ *a, const ZZ *b, long n) </span> |
| 65 | +<span class="Comment"> {</span> |
| 66 | +<span class="Comment"> for (long i = 0; i < n; i++)</span> |
| 67 | +<span class="Comment"> mul(x[i], a[i], b[i]);</span> |
| 68 | +<span class="Comment"> }</span> |
| 69 | +<span class="Comment"> </span> |
| 70 | +<span class="Comment">as follows:</span> |
| 71 | + |
| 72 | +<span class="Comment"> void mul(ZZ *x, const ZZ *a, const ZZ *b, long n, </span> |
| 73 | +<span class="Comment"> BasicThreadPool *pool) </span> |
| 74 | +<span class="Comment"> {</span> |
| 75 | +<span class="Comment"> pool->exec_range(n,</span> |
| 76 | +<span class="Comment"> [&](long first, long last) {</span> |
| 77 | +<span class="Comment"> for (long i = first; i < last; i++)</span> |
| 78 | +<span class="Comment"> mul(x[i], a[i], b[i]); </span> |
| 79 | +<span class="Comment"> } );</span> |
| 80 | +<span class="Comment"> }</span> |
| 81 | + |
| 82 | + |
| 83 | +<span class="Comment">As another example, we could parallelize the following</span> |
| 84 | +<span class="Comment">calculation:</span> |
| 85 | + |
| 86 | +<span class="Comment"> void mul(ZZ_p *x, const ZZ_p *a, const ZZ_p *b, long n) </span> |
| 87 | +<span class="Comment"> {</span> |
| 88 | +<span class="Comment"> for (long i = 0; i < n; i++)</span> |
| 89 | +<span class="Comment"> mul(x[i], a[i], b[i]);</span> |
| 90 | +<span class="Comment"> }</span> |
| 91 | +<span class="Comment"> </span> |
| 92 | +<span class="Comment">as follows:</span> |
| 93 | + |
| 94 | +<span class="Comment"> void mul(ZZ_p *x, const ZZ_p *a, const ZZ_p *b, long n, </span> |
| 95 | +<span class="Comment"> BasicThreadPool *pool) </span> |
| 96 | +<span class="Comment"> {</span> |
| 97 | +<span class="Comment"> ZZ_pContext context;</span> |
| 98 | +<span class="Comment"> context.save();</span> |
| 99 | +<span class="Comment"> </span> |
| 100 | +<span class="Comment"> pool->exec_range(n,</span> |
| 101 | +<span class="Comment"> [&](long first, long last) {</span> |
| 102 | +<span class="Comment"> context.restore();</span> |
| 103 | +<span class="Comment"> for (long i = first; i < last; i++)</span> |
| 104 | +<span class="Comment"> mul(x[i], a[i], b[i]); </span> |
| 105 | +<span class="Comment"> } );</span> |
| 106 | +<span class="Comment"> }</span> |
| 107 | + |
| 108 | +<span class="Comment">This illustrates a simple and efficient means for ensuring that</span> |
| 109 | +<span class="Comment">all threads are working with the same ZZ_p modulus.</span> |
| 110 | + |
| 111 | +<span class="Comment">====================================================================</span> |
| 112 | + |
| 113 | +<span class="Comment">A lower-level interface is also provided.</span> |
| 114 | +<span class="Comment">One can write:</span> |
| 115 | + |
| 116 | +<span class="Comment"> pool.exec_index(n,</span> |
| 117 | +<span class="Comment"> [&](long index) {</span> |
| 118 | +<span class="Comment"> ... code to process index i ...</span> |
| 119 | +<span class="Comment"> }</span> |
| 120 | +<span class="Comment"> );</span> |
| 121 | + |
| 122 | +<span class="Comment">This will activate n threads with indices 0..n-1, and execute the given code on</span> |
| 123 | +<span class="Comment">each index. The parameter n must be in the range 0..nthreads, otherwise an</span> |
| 124 | +<span class="Comment">error is raised.</span> |
| 125 | + |
| 126 | +<span class="Comment">This lower-level interface is useful in some cases, especially when memory is</span> |
| 127 | +<span class="Comment">managed in some special way. For convenience, a method is provided to break</span> |
| 128 | +<span class="Comment">subtasks up into smaller, almost-equal-sized groups of subtasks:</span> |
| 129 | + |
| 130 | +<span class="Comment"> Vec<long> pvec;</span> |
| 131 | +<span class="Comment"> long n = pool.SplitProblems(N, pvec);</span> |
| 132 | + |
| 133 | +<span class="Comment">can be used for this. N is the number of subtasks, indexed 0..N-1. This</span> |
| 134 | +<span class="Comment">method will compute n as needed by exec_index, and the range of subtasks to be</span> |
| 135 | +<span class="Comment">processed by a given index in the range 0..n-1 is pvec[index]..pvec[index+1]-1</span> |
| 136 | +<span class="Comment">Thus, the logic of exec_range example can be written using the lower-level</span> |
| 137 | +<span class="Comment">exec_index interface as follows:</span> |
| 138 | + |
| 139 | +<span class="Comment"> </span> |
| 140 | +<span class="Comment"> Vec<long> pvec;</span> |
| 141 | +<span class="Comment"> long n = pool.SplitProblems(N, pvec);</span> |
| 142 | +<span class="Comment"> pool.exec_index(n,</span> |
| 143 | +<span class="Comment"> [&](long index) {</span> |
| 144 | +<span class="Comment"> long first = pvec[index];</span> |
| 145 | +<span class="Comment"> long last = pvec[index+1];</span> |
| 146 | +<span class="Comment"> for (long i = first; i < last; i++) {</span> |
| 147 | +<span class="Comment"> ... code to process subtask i ...</span> |
| 148 | +<span class="Comment"> }</span> |
| 149 | +<span class="Comment"> }</span> |
| 150 | +<span class="Comment"> );</span> |
| 151 | + |
| 152 | +<span class="Comment">However, with this approach, memory or other resources can be assigned to each</span> |
| 153 | +<span class="Comment">index = 0..n-1, and managed externally. </span> |
| 154 | + |
| 155 | +<span class="Comment">====================================================================</span> |
| 156 | + |
| 157 | +<span class="Comment">NOTES:</span> |
| 158 | + |
| 159 | +<span class="Comment">When one activates a thread pool with nthreads threads, the *current* thread</span> |
| 160 | +<span class="Comment">(the one activating the pool) will also participate in the computation. This</span> |
| 161 | +<span class="Comment">means that the thread pool only contains nthreads-1 other threads.</span> |
| 162 | + |
| 163 | +<span class="Comment">If, during an activation, any thread throws an exception, it will be caught and</span> |
| 164 | +<span class="Comment">rethrown in the activating thread when all the threads complete. If more than</span> |
| 165 | +<span class="Comment">one thread throws an exception, the first one that is caught is the one that is</span> |
| 166 | +<span class="Comment">rethrown.</span> |
| 167 | + |
| 168 | +<span class="Comment">Methods are also provided for adding, deleting, and moving threads in and among</span> |
| 169 | +<span class="Comment">thread pools.</span> |
| 170 | + |
| 171 | +<span class="Comment">If NTL_THREADS=off, the corresponding header file may be included,</span> |
| 172 | +<span class="Comment">by the BasicThreadPool class is not defined.</span> |
| 173 | + |
| 174 | +<span class="Comment">THREAD BOOSTING:</span> |
| 175 | + |
| 176 | +<span class="Comment">While users are free to use a thread pool as they wish, NTL can be enabled so</span> |
| 177 | +<span class="Comment">that it *internally* uses a thread pool to speed up certain computations. This</span> |
| 178 | +<span class="Comment">is currently a work in progress. To use this feature, NTL should be configured</span> |
| 179 | +<span class="Comment">with NTL_THREAD_BOOST=on. The user can then initialize the (thread local)</span> |
| 180 | +<span class="Comment">variable NTLThreadPool, either directly or via the convenience function</span> |
| 181 | +<span class="Comment">SetNumThreads (see below).</span> |
| 182 | + |
| 183 | +<span class="Comment">NTL applications may use the NTLThreadPool themselves: the logic is designed so</span> |
| 184 | +<span class="Comment">that if this pool is already activated when entering a thread-boosted routine,</span> |
| 185 | +<span class="Comment">the thread-boosting is temporarily disabled. This means that an application</span> |
| 186 | +<span class="Comment">can seamlessly use higer-level parallization when possible (which is usually</span> |
| 187 | +<span class="Comment">more effective) or rely on NTL's internal parallelization at a lower leve.</span> |
| 188 | + |
| 189 | + |
| 190 | +<span class="Comment">**************************************************************************</span><span class="Comment">*/</span> |
| 191 | + |
| 192 | + |
| 193 | +<span class="Type">class</span> BasicThreadPool { |
| 194 | +<span class="Statement">private</span>: |
| 195 | + |
| 196 | + BasicThreadPool(<span class="Type">const</span> BasicThreadPool&); <span class="Comment">// disabled</span> |
| 197 | + <span class="Type">void</span> <span class="Statement">operator</span>=(<span class="Type">const</span> BasicThreadPool&); <span class="Comment">// disabled</span> |
| 198 | + |
| 199 | +<span class="Statement">public</span>: |
| 200 | + |
| 201 | + BasicThreadPool(<span class="Type">long</span> nthreads); |
| 202 | + <span class="Comment">// creates a pool with nthreads threads, including the current thread</span> |
| 203 | + <span class="Comment">// (so nthreads-1 other threads get created)</span> |
| 204 | + |
| 205 | + <span class="Type">template</span><<span class="Type">class</span> Fct> |
| 206 | + <span class="Type">void</span> exec_index(<span class="Type">long</span> cnt, Fct fct); |
| 207 | + <span class="Comment">// activate by index (see example usage above)</span> |
| 208 | + |
| 209 | + <span class="Type">template</span><<span class="Type">class</span> Fct> |
| 210 | + <span class="Type">void</span> exec_range(<span class="Type">long</span> sz, Fct fct); |
| 211 | + <span class="Comment">// activate by range (see example usage above)</span> |
| 212 | + |
| 213 | + <span class="Type">long</span> SplitProblems(<span class="Type">long</span> nproblems, Vec<<span class="Type">long</span>>& pvec) <span class="Type">const</span>; |
| 214 | + <span class="Comment">// splits nproblems problems among (at most) nthreads threads.</span> |
| 215 | + <span class="Comment">// returns the actual number of threads nt to be used, and </span> |
| 216 | + <span class="Comment">// initializes pvec to have length nt+1, so that for t = 0..nt-1,</span> |
| 217 | + <span class="Comment">// thread t processes subproblems pvec[t]..pvec[t+1]-1</span> |
| 218 | + |
| 219 | + <span class="Type">long</span> NumThreads() <span class="Type">const</span>; |
| 220 | + <span class="Comment">// return number of threads (including current thread)</span> |
| 221 | + |
| 222 | + <span class="Type">bool</span> active() <span class="Type">const</span>; |
| 223 | + <span class="Comment">// indicates an activation is in process</span> |
| 224 | + |
| 225 | + <span class="Type">void</span> add(<span class="Type">long</span> n = <span class="Constant">1</span>); |
| 226 | + <span class="Comment">// add n threads to the pool</span> |
| 227 | + |
| 228 | + <span class="Type">void</span> remove(<span class="Type">long</span> n = <span class="Constant">1</span>); |
| 229 | + <span class="Comment">// remove n threads from the pool</span> |
| 230 | + |
| 231 | + <span class="Type">void</span> move(BasicThreadPool& other, <span class="Type">long</span> n = <span class="Constant">1</span>) |
| 232 | + <span class="Comment">// move n threads from other pool to this pool</span> |
| 233 | + |
| 234 | +}; |
| 235 | + |
| 236 | + |
| 237 | + |
| 238 | +<span class="Comment">// THREAD BOOSTING FEATURES:</span> |
| 239 | + |
| 240 | +<span class="Type">extern</span> thread_local BasicThreadPool *NTLThreadPool; |
| 241 | +<span class="Comment">// pool used internally by NTL with NTL_THREAD_BOOST=on</span> |
| 242 | + |
| 243 | +<span class="Type">void</span> SetNumThreads(<span class="Type">long</span> n); |
| 244 | +<span class="Comment">// convenience routine to set NTLThreadPool (created using new)</span> |
| 245 | +<span class="Comment">// If NTL_THREADS=off, then this is still defined, but does nothing</span> |
| 246 | + |
| 247 | + |
| 248 | + |
| 249 | +</pre> |
| 250 | +</body> |
| 251 | +</html> |
0 commit comments