iter_ull.c revision 1.1.1.1.4.2 1 1.1.1.1.4.2 yamt /* Copyright (C) 2005, 2008, 2009 Free Software Foundation, Inc.
2 1.1.1.1.4.2 yamt Contributed by Richard Henderson <rth (at) redhat.com>.
3 1.1.1.1.4.2 yamt
4 1.1.1.1.4.2 yamt This file is part of the GNU OpenMP Library (libgomp).
5 1.1.1.1.4.2 yamt
6 1.1.1.1.4.2 yamt Libgomp is free software; you can redistribute it and/or modify it
7 1.1.1.1.4.2 yamt under the terms of the GNU General Public License as published by
8 1.1.1.1.4.2 yamt the Free Software Foundation; either version 3, or (at your option)
9 1.1.1.1.4.2 yamt any later version.
10 1.1.1.1.4.2 yamt
11 1.1.1.1.4.2 yamt Libgomp is distributed in the hope that it will be useful, but WITHOUT ANY
12 1.1.1.1.4.2 yamt WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13 1.1.1.1.4.2 yamt FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14 1.1.1.1.4.2 yamt more details.
15 1.1.1.1.4.2 yamt
16 1.1.1.1.4.2 yamt Under Section 7 of GPL version 3, you are granted additional
17 1.1.1.1.4.2 yamt permissions described in the GCC Runtime Library Exception, version
18 1.1.1.1.4.2 yamt 3.1, as published by the Free Software Foundation.
19 1.1.1.1.4.2 yamt
20 1.1.1.1.4.2 yamt You should have received a copy of the GNU General Public License and
21 1.1.1.1.4.2 yamt a copy of the GCC Runtime Library Exception along with this program;
22 1.1.1.1.4.2 yamt see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
23 1.1.1.1.4.2 yamt <http://www.gnu.org/licenses/>. */
24 1.1.1.1.4.2 yamt
25 1.1.1.1.4.2 yamt /* This file contains routines for managing work-share iteration, both
26 1.1.1.1.4.2 yamt for loops and sections. */
27 1.1.1.1.4.2 yamt
28 1.1.1.1.4.2 yamt #include "libgomp.h"
29 1.1.1.1.4.2 yamt #include <stdlib.h>
30 1.1.1.1.4.2 yamt
31 1.1.1.1.4.2 yamt typedef unsigned long long gomp_ull;
32 1.1.1.1.4.2 yamt
33 1.1.1.1.4.2 yamt /* This function implements the STATIC scheduling method. The caller should
34 1.1.1.1.4.2 yamt iterate *pstart <= x < *pend. Return zero if there are more iterations
35 1.1.1.1.4.2 yamt to perform; nonzero if not. Return less than 0 if this thread had
36 1.1.1.1.4.2 yamt received the absolutely last iteration. */
37 1.1.1.1.4.2 yamt
38 1.1.1.1.4.2 yamt int
39 1.1.1.1.4.2 yamt gomp_iter_ull_static_next (gomp_ull *pstart, gomp_ull *pend)
40 1.1.1.1.4.2 yamt {
41 1.1.1.1.4.2 yamt struct gomp_thread *thr = gomp_thread ();
42 1.1.1.1.4.2 yamt struct gomp_team *team = thr->ts.team;
43 1.1.1.1.4.2 yamt struct gomp_work_share *ws = thr->ts.work_share;
44 1.1.1.1.4.2 yamt unsigned long nthreads = team ? team->nthreads : 1;
45 1.1.1.1.4.2 yamt
46 1.1.1.1.4.2 yamt if (thr->ts.static_trip == -1)
47 1.1.1.1.4.2 yamt return -1;
48 1.1.1.1.4.2 yamt
49 1.1.1.1.4.2 yamt /* Quick test for degenerate teams and orphaned constructs. */
50 1.1.1.1.4.2 yamt if (nthreads == 1)
51 1.1.1.1.4.2 yamt {
52 1.1.1.1.4.2 yamt *pstart = ws->next_ull;
53 1.1.1.1.4.2 yamt *pend = ws->end_ull;
54 1.1.1.1.4.2 yamt thr->ts.static_trip = -1;
55 1.1.1.1.4.2 yamt return ws->next_ull == ws->end_ull;
56 1.1.1.1.4.2 yamt }
57 1.1.1.1.4.2 yamt
58 1.1.1.1.4.2 yamt /* We interpret chunk_size zero as "unspecified", which means that we
59 1.1.1.1.4.2 yamt should break up the iterations such that each thread makes only one
60 1.1.1.1.4.2 yamt trip through the outer loop. */
61 1.1.1.1.4.2 yamt if (ws->chunk_size_ull == 0)
62 1.1.1.1.4.2 yamt {
63 1.1.1.1.4.2 yamt gomp_ull n, q, i, s0, e0, s, e;
64 1.1.1.1.4.2 yamt
65 1.1.1.1.4.2 yamt if (thr->ts.static_trip > 0)
66 1.1.1.1.4.2 yamt return 1;
67 1.1.1.1.4.2 yamt
68 1.1.1.1.4.2 yamt /* Compute the total number of iterations. */
69 1.1.1.1.4.2 yamt if (__builtin_expect (ws->mode, 0) == 0)
70 1.1.1.1.4.2 yamt n = (ws->end_ull - ws->next_ull + ws->incr_ull - 1) / ws->incr_ull;
71 1.1.1.1.4.2 yamt else
72 1.1.1.1.4.2 yamt n = (ws->next_ull - ws->end_ull - ws->incr_ull - 1) / -ws->incr_ull;
73 1.1.1.1.4.2 yamt i = thr->ts.team_id;
74 1.1.1.1.4.2 yamt
75 1.1.1.1.4.2 yamt /* Compute the "zero-based" start and end points. That is, as
76 1.1.1.1.4.2 yamt if the loop began at zero and incremented by one. */
77 1.1.1.1.4.2 yamt q = n / nthreads;
78 1.1.1.1.4.2 yamt q += (q * nthreads != n);
79 1.1.1.1.4.2 yamt s0 = q * i;
80 1.1.1.1.4.2 yamt e0 = s0 + q;
81 1.1.1.1.4.2 yamt if (e0 > n)
82 1.1.1.1.4.2 yamt e0 = n;
83 1.1.1.1.4.2 yamt
84 1.1.1.1.4.2 yamt /* Notice when no iterations allocated for this thread. */
85 1.1.1.1.4.2 yamt if (s0 >= e0)
86 1.1.1.1.4.2 yamt {
87 1.1.1.1.4.2 yamt thr->ts.static_trip = 1;
88 1.1.1.1.4.2 yamt return 1;
89 1.1.1.1.4.2 yamt }
90 1.1.1.1.4.2 yamt
91 1.1.1.1.4.2 yamt /* Transform these to the actual start and end numbers. */
92 1.1.1.1.4.2 yamt s = s0 * ws->incr_ull + ws->next_ull;
93 1.1.1.1.4.2 yamt e = e0 * ws->incr_ull + ws->next_ull;
94 1.1.1.1.4.2 yamt
95 1.1.1.1.4.2 yamt *pstart = s;
96 1.1.1.1.4.2 yamt *pend = e;
97 1.1.1.1.4.2 yamt thr->ts.static_trip = (e0 == n ? -1 : 1);
98 1.1.1.1.4.2 yamt return 0;
99 1.1.1.1.4.2 yamt }
100 1.1.1.1.4.2 yamt else
101 1.1.1.1.4.2 yamt {
102 1.1.1.1.4.2 yamt gomp_ull n, s0, e0, i, c, s, e;
103 1.1.1.1.4.2 yamt
104 1.1.1.1.4.2 yamt /* Otherwise, each thread gets exactly chunk_size iterations
105 1.1.1.1.4.2 yamt (if available) each time through the loop. */
106 1.1.1.1.4.2 yamt
107 1.1.1.1.4.2 yamt if (__builtin_expect (ws->mode, 0) == 0)
108 1.1.1.1.4.2 yamt n = (ws->end_ull - ws->next_ull + ws->incr_ull - 1) / ws->incr_ull;
109 1.1.1.1.4.2 yamt else
110 1.1.1.1.4.2 yamt n = (ws->next_ull - ws->end_ull - ws->incr_ull - 1) / -ws->incr_ull;
111 1.1.1.1.4.2 yamt i = thr->ts.team_id;
112 1.1.1.1.4.2 yamt c = ws->chunk_size_ull;
113 1.1.1.1.4.2 yamt
114 1.1.1.1.4.2 yamt /* Initial guess is a C sized chunk positioned nthreads iterations
115 1.1.1.1.4.2 yamt in, offset by our thread number. */
116 1.1.1.1.4.2 yamt s0 = (thr->ts.static_trip * (gomp_ull) nthreads + i) * c;
117 1.1.1.1.4.2 yamt e0 = s0 + c;
118 1.1.1.1.4.2 yamt
119 1.1.1.1.4.2 yamt /* Detect overflow. */
120 1.1.1.1.4.2 yamt if (s0 >= n)
121 1.1.1.1.4.2 yamt return 1;
122 1.1.1.1.4.2 yamt if (e0 > n)
123 1.1.1.1.4.2 yamt e0 = n;
124 1.1.1.1.4.2 yamt
125 1.1.1.1.4.2 yamt /* Transform these to the actual start and end numbers. */
126 1.1.1.1.4.2 yamt s = s0 * ws->incr_ull + ws->next_ull;
127 1.1.1.1.4.2 yamt e = e0 * ws->incr_ull + ws->next_ull;
128 1.1.1.1.4.2 yamt
129 1.1.1.1.4.2 yamt *pstart = s;
130 1.1.1.1.4.2 yamt *pend = e;
131 1.1.1.1.4.2 yamt
132 1.1.1.1.4.2 yamt if (e0 == n)
133 1.1.1.1.4.2 yamt thr->ts.static_trip = -1;
134 1.1.1.1.4.2 yamt else
135 1.1.1.1.4.2 yamt thr->ts.static_trip++;
136 1.1.1.1.4.2 yamt return 0;
137 1.1.1.1.4.2 yamt }
138 1.1.1.1.4.2 yamt }
139 1.1.1.1.4.2 yamt
140 1.1.1.1.4.2 yamt
141 1.1.1.1.4.2 yamt /* This function implements the DYNAMIC scheduling method. Arguments are
142 1.1.1.1.4.2 yamt as for gomp_iter_ull_static_next. This function must be called with
143 1.1.1.1.4.2 yamt ws->lock held. */
144 1.1.1.1.4.2 yamt
145 1.1.1.1.4.2 yamt bool
146 1.1.1.1.4.2 yamt gomp_iter_ull_dynamic_next_locked (gomp_ull *pstart, gomp_ull *pend)
147 1.1.1.1.4.2 yamt {
148 1.1.1.1.4.2 yamt struct gomp_thread *thr = gomp_thread ();
149 1.1.1.1.4.2 yamt struct gomp_work_share *ws = thr->ts.work_share;
150 1.1.1.1.4.2 yamt gomp_ull start, end, chunk, left;
151 1.1.1.1.4.2 yamt
152 1.1.1.1.4.2 yamt start = ws->next_ull;
153 1.1.1.1.4.2 yamt if (start == ws->end_ull)
154 1.1.1.1.4.2 yamt return false;
155 1.1.1.1.4.2 yamt
156 1.1.1.1.4.2 yamt chunk = ws->chunk_size_ull;
157 1.1.1.1.4.2 yamt left = ws->end_ull - start;
158 1.1.1.1.4.2 yamt if (__builtin_expect (ws->mode & 2, 0))
159 1.1.1.1.4.2 yamt {
160 1.1.1.1.4.2 yamt if (chunk < left)
161 1.1.1.1.4.2 yamt chunk = left;
162 1.1.1.1.4.2 yamt }
163 1.1.1.1.4.2 yamt else
164 1.1.1.1.4.2 yamt {
165 1.1.1.1.4.2 yamt if (chunk > left)
166 1.1.1.1.4.2 yamt chunk = left;
167 1.1.1.1.4.2 yamt }
168 1.1.1.1.4.2 yamt end = start + chunk;
169 1.1.1.1.4.2 yamt
170 1.1.1.1.4.2 yamt ws->next_ull = end;
171 1.1.1.1.4.2 yamt *pstart = start;
172 1.1.1.1.4.2 yamt *pend = end;
173 1.1.1.1.4.2 yamt return true;
174 1.1.1.1.4.2 yamt }
175 1.1.1.1.4.2 yamt
176 1.1.1.1.4.2 yamt
177 1.1.1.1.4.2 yamt #if defined HAVE_SYNC_BUILTINS && defined __LP64__
178 1.1.1.1.4.2 yamt /* Similar, but doesn't require the lock held, and uses compare-and-swap
179 1.1.1.1.4.2 yamt instead. Note that the only memory value that changes is ws->next_ull. */
180 1.1.1.1.4.2 yamt
181 1.1.1.1.4.2 yamt bool
182 1.1.1.1.4.2 yamt gomp_iter_ull_dynamic_next (gomp_ull *pstart, gomp_ull *pend)
183 1.1.1.1.4.2 yamt {
184 1.1.1.1.4.2 yamt struct gomp_thread *thr = gomp_thread ();
185 1.1.1.1.4.2 yamt struct gomp_work_share *ws = thr->ts.work_share;
186 1.1.1.1.4.2 yamt gomp_ull start, end, nend, chunk;
187 1.1.1.1.4.2 yamt
188 1.1.1.1.4.2 yamt end = ws->end_ull;
189 1.1.1.1.4.2 yamt chunk = ws->chunk_size_ull;
190 1.1.1.1.4.2 yamt
191 1.1.1.1.4.2 yamt if (__builtin_expect (ws->mode & 1, 1))
192 1.1.1.1.4.2 yamt {
193 1.1.1.1.4.2 yamt gomp_ull tmp = __sync_fetch_and_add (&ws->next_ull, chunk);
194 1.1.1.1.4.2 yamt if (__builtin_expect (ws->mode & 2, 0) == 0)
195 1.1.1.1.4.2 yamt {
196 1.1.1.1.4.2 yamt if (tmp >= end)
197 1.1.1.1.4.2 yamt return false;
198 1.1.1.1.4.2 yamt nend = tmp + chunk;
199 1.1.1.1.4.2 yamt if (nend > end)
200 1.1.1.1.4.2 yamt nend = end;
201 1.1.1.1.4.2 yamt *pstart = tmp;
202 1.1.1.1.4.2 yamt *pend = nend;
203 1.1.1.1.4.2 yamt return true;
204 1.1.1.1.4.2 yamt }
205 1.1.1.1.4.2 yamt else
206 1.1.1.1.4.2 yamt {
207 1.1.1.1.4.2 yamt if (tmp <= end)
208 1.1.1.1.4.2 yamt return false;
209 1.1.1.1.4.2 yamt nend = tmp + chunk;
210 1.1.1.1.4.2 yamt if (nend < end)
211 1.1.1.1.4.2 yamt nend = end;
212 1.1.1.1.4.2 yamt *pstart = tmp;
213 1.1.1.1.4.2 yamt *pend = nend;
214 1.1.1.1.4.2 yamt return true;
215 1.1.1.1.4.2 yamt }
216 1.1.1.1.4.2 yamt }
217 1.1.1.1.4.2 yamt
218 1.1.1.1.4.2 yamt start = ws->next_ull;
219 1.1.1.1.4.2 yamt while (1)
220 1.1.1.1.4.2 yamt {
221 1.1.1.1.4.2 yamt gomp_ull left = end - start;
222 1.1.1.1.4.2 yamt gomp_ull tmp;
223 1.1.1.1.4.2 yamt
224 1.1.1.1.4.2 yamt if (start == end)
225 1.1.1.1.4.2 yamt return false;
226 1.1.1.1.4.2 yamt
227 1.1.1.1.4.2 yamt if (__builtin_expect (ws->mode & 2, 0))
228 1.1.1.1.4.2 yamt {
229 1.1.1.1.4.2 yamt if (chunk < left)
230 1.1.1.1.4.2 yamt chunk = left;
231 1.1.1.1.4.2 yamt }
232 1.1.1.1.4.2 yamt else
233 1.1.1.1.4.2 yamt {
234 1.1.1.1.4.2 yamt if (chunk > left)
235 1.1.1.1.4.2 yamt chunk = left;
236 1.1.1.1.4.2 yamt }
237 1.1.1.1.4.2 yamt nend = start + chunk;
238 1.1.1.1.4.2 yamt
239 1.1.1.1.4.2 yamt tmp = __sync_val_compare_and_swap (&ws->next_ull, start, nend);
240 1.1.1.1.4.2 yamt if (__builtin_expect (tmp == start, 1))
241 1.1.1.1.4.2 yamt break;
242 1.1.1.1.4.2 yamt
243 1.1.1.1.4.2 yamt start = tmp;
244 1.1.1.1.4.2 yamt }
245 1.1.1.1.4.2 yamt
246 1.1.1.1.4.2 yamt *pstart = start;
247 1.1.1.1.4.2 yamt *pend = nend;
248 1.1.1.1.4.2 yamt return true;
249 1.1.1.1.4.2 yamt }
250 1.1.1.1.4.2 yamt #endif /* HAVE_SYNC_BUILTINS */
251 1.1.1.1.4.2 yamt
252 1.1.1.1.4.2 yamt
253 1.1.1.1.4.2 yamt /* This function implements the GUIDED scheduling method. Arguments are
254 1.1.1.1.4.2 yamt as for gomp_iter_ull_static_next. This function must be called with the
255 1.1.1.1.4.2 yamt work share lock held. */
256 1.1.1.1.4.2 yamt
257 1.1.1.1.4.2 yamt bool
258 1.1.1.1.4.2 yamt gomp_iter_ull_guided_next_locked (gomp_ull *pstart, gomp_ull *pend)
259 1.1.1.1.4.2 yamt {
260 1.1.1.1.4.2 yamt struct gomp_thread *thr = gomp_thread ();
261 1.1.1.1.4.2 yamt struct gomp_work_share *ws = thr->ts.work_share;
262 1.1.1.1.4.2 yamt struct gomp_team *team = thr->ts.team;
263 1.1.1.1.4.2 yamt gomp_ull nthreads = team ? team->nthreads : 1;
264 1.1.1.1.4.2 yamt gomp_ull n, q;
265 1.1.1.1.4.2 yamt gomp_ull start, end;
266 1.1.1.1.4.2 yamt
267 1.1.1.1.4.2 yamt if (ws->next_ull == ws->end_ull)
268 1.1.1.1.4.2 yamt return false;
269 1.1.1.1.4.2 yamt
270 1.1.1.1.4.2 yamt start = ws->next_ull;
271 1.1.1.1.4.2 yamt if (__builtin_expect (ws->mode, 0) == 0)
272 1.1.1.1.4.2 yamt n = (ws->end_ull - start) / ws->incr_ull;
273 1.1.1.1.4.2 yamt else
274 1.1.1.1.4.2 yamt n = (start - ws->end_ull) / -ws->incr_ull;
275 1.1.1.1.4.2 yamt q = (n + nthreads - 1) / nthreads;
276 1.1.1.1.4.2 yamt
277 1.1.1.1.4.2 yamt if (q < ws->chunk_size_ull)
278 1.1.1.1.4.2 yamt q = ws->chunk_size_ull;
279 1.1.1.1.4.2 yamt if (q <= n)
280 1.1.1.1.4.2 yamt end = start + q * ws->incr_ull;
281 1.1.1.1.4.2 yamt else
282 1.1.1.1.4.2 yamt end = ws->end_ull;
283 1.1.1.1.4.2 yamt
284 1.1.1.1.4.2 yamt ws->next_ull = end;
285 1.1.1.1.4.2 yamt *pstart = start;
286 1.1.1.1.4.2 yamt *pend = end;
287 1.1.1.1.4.2 yamt return true;
288 1.1.1.1.4.2 yamt }
289 1.1.1.1.4.2 yamt
290 1.1.1.1.4.2 yamt #if defined HAVE_SYNC_BUILTINS && defined __LP64__
291 1.1.1.1.4.2 yamt /* Similar, but doesn't require the lock held, and uses compare-and-swap
292 1.1.1.1.4.2 yamt instead. Note that the only memory value that changes is ws->next_ull. */
293 1.1.1.1.4.2 yamt
294 1.1.1.1.4.2 yamt bool
295 1.1.1.1.4.2 yamt gomp_iter_ull_guided_next (gomp_ull *pstart, gomp_ull *pend)
296 1.1.1.1.4.2 yamt {
297 1.1.1.1.4.2 yamt struct gomp_thread *thr = gomp_thread ();
298 1.1.1.1.4.2 yamt struct gomp_work_share *ws = thr->ts.work_share;
299 1.1.1.1.4.2 yamt struct gomp_team *team = thr->ts.team;
300 1.1.1.1.4.2 yamt gomp_ull nthreads = team ? team->nthreads : 1;
301 1.1.1.1.4.2 yamt gomp_ull start, end, nend, incr;
302 1.1.1.1.4.2 yamt gomp_ull chunk_size;
303 1.1.1.1.4.2 yamt
304 1.1.1.1.4.2 yamt start = ws->next_ull;
305 1.1.1.1.4.2 yamt end = ws->end_ull;
306 1.1.1.1.4.2 yamt incr = ws->incr_ull;
307 1.1.1.1.4.2 yamt chunk_size = ws->chunk_size_ull;
308 1.1.1.1.4.2 yamt
309 1.1.1.1.4.2 yamt while (1)
310 1.1.1.1.4.2 yamt {
311 1.1.1.1.4.2 yamt gomp_ull n, q;
312 1.1.1.1.4.2 yamt gomp_ull tmp;
313 1.1.1.1.4.2 yamt
314 1.1.1.1.4.2 yamt if (start == end)
315 1.1.1.1.4.2 yamt return false;
316 1.1.1.1.4.2 yamt
317 1.1.1.1.4.2 yamt if (__builtin_expect (ws->mode, 0) == 0)
318 1.1.1.1.4.2 yamt n = (end - start) / incr;
319 1.1.1.1.4.2 yamt else
320 1.1.1.1.4.2 yamt n = (start - end) / -incr;
321 1.1.1.1.4.2 yamt q = (n + nthreads - 1) / nthreads;
322 1.1.1.1.4.2 yamt
323 1.1.1.1.4.2 yamt if (q < chunk_size)
324 1.1.1.1.4.2 yamt q = chunk_size;
325 1.1.1.1.4.2 yamt if (__builtin_expect (q <= n, 1))
326 1.1.1.1.4.2 yamt nend = start + q * incr;
327 1.1.1.1.4.2 yamt else
328 1.1.1.1.4.2 yamt nend = end;
329 1.1.1.1.4.2 yamt
330 1.1.1.1.4.2 yamt tmp = __sync_val_compare_and_swap (&ws->next_ull, start, nend);
331 1.1.1.1.4.2 yamt if (__builtin_expect (tmp == start, 1))
332 1.1.1.1.4.2 yamt break;
333 1.1.1.1.4.2 yamt
334 1.1.1.1.4.2 yamt start = tmp;
335 1.1.1.1.4.2 yamt }
336 1.1.1.1.4.2 yamt
337 1.1.1.1.4.2 yamt *pstart = start;
338 1.1.1.1.4.2 yamt *pend = nend;
339 1.1.1.1.4.2 yamt return true;
340 1.1.1.1.4.2 yamt }
341 1.1.1.1.4.2 yamt #endif /* HAVE_SYNC_BUILTINS */
342