|
15 | 15 | function _decoupled_fence end |
16 | 16 |
|
17 | 17 |
|
| 18 | +# Exclusive scan of one value per thread in local memory. All threads in the block must call it. |
| 19 | +@inline function block_exclusive_scan!(@context, op, totals, seed, block_size, ithread) |
| 20 | + # Up-sweep. Use index-sized counters for block sizes of 256 or more. |
| 21 | + offset = one(ithread) |
| 22 | + d = block_size >> 0x1 |
| 23 | + while d > 0x0 |
| 24 | + @synchronize() |
| 25 | + if ithread < d |
| 26 | + ai = offset * (0x2 * ithread + 0x1) - 0x1 |
| 27 | + bi = offset * (0x2 * ithread + 0x2) - 0x1 |
| 28 | + totals[bi + 0x1] = op(totals[bi + 0x1], totals[ai + 0x1]) |
| 29 | + end |
| 30 | + offset = offset << 0x1 |
| 31 | + d = d >> 0x1 |
| 32 | + end |
| 33 | + |
| 34 | + @synchronize() |
| 35 | + block_total = op(seed, totals[block_size]) |
| 36 | + @synchronize() |
| 37 | + if ithread == 0x0 |
| 38 | + totals[block_size] = seed |
| 39 | + end |
| 40 | + |
| 41 | + # Down-sweep to an exclusive scan. |
| 42 | + d = one(ithread) |
| 43 | + while d < block_size |
| 44 | + offset = offset >> 0x1 |
| 45 | + @synchronize() |
| 46 | + if ithread < d |
| 47 | + ai = offset * (0x2 * ithread + 0x1) - 0x1 |
| 48 | + bi = offset * (0x2 * ithread + 0x2) - 0x1 |
| 49 | + t = totals[ai + 0x1] |
| 50 | + totals[ai + 0x1] = totals[bi + 0x1] |
| 51 | + totals[bi + 0x1] = op(totals[bi + 0x1], t) |
| 52 | + end |
| 53 | + d = d << 0x1 |
| 54 | + end |
| 55 | + @synchronize() |
| 56 | + |
| 57 | + return totals[ithread + 0x1], block_total |
| 58 | +end |
| 59 | + |
| 60 | + |
18 | 61 | # Register-raking block scan with striped loads and stores. |
19 | 62 | @kernel cpu=false inbounds=true unsafe_indices=true function _accumulate_block!( |
20 | 63 | op, v, init, neutral, |
@@ -53,50 +96,13 @@ function _decoupled_fence end |
53 | 96 | k += 1 |
54 | 97 | end |
55 | 98 | thread_totals[ithread + 0x1] = acc |
56 | | - @synchronize() |
57 | 99 |
|
58 | 100 | # Scan the per-thread totals. Later blocks receive their carry from the |
59 | 101 | # second kernel. |
60 | 102 | seed = iblock == 0x0 ? init : neutral |
61 | | - |
62 | | - # Use index-sized counters for block sizes of 256 or more. |
63 | | - offset = one(ithread) |
64 | | - d = block_size >> 0x1 |
65 | | - while d > 0x0 |
66 | | - @synchronize() |
67 | | - if ithread < d |
68 | | - ai = offset * (0x2 * ithread + 0x1) - 0x1 |
69 | | - bi = offset * (0x2 * ithread + 0x2) - 0x1 |
70 | | - thread_totals[bi + 0x1] = |
71 | | - op(thread_totals[bi + 0x1], thread_totals[ai + 0x1]) |
72 | | - end |
73 | | - offset = offset << 0x1 |
74 | | - d = d >> 0x1 |
75 | | - end |
76 | | - |
77 | | - @synchronize() |
78 | | - block_total = op(seed, thread_totals[block_size]) |
79 | | - @synchronize() |
80 | | - if ithread == 0x0 |
81 | | - thread_totals[block_size] = seed |
82 | | - end |
83 | | - |
84 | | - # Down-sweep to an exclusive scan. |
85 | | - d = one(ithread) |
86 | | - while d < block_size |
87 | | - offset = offset >> 0x1 |
88 | | - @synchronize() |
89 | | - if ithread < d |
90 | | - ai = offset * (0x2 * ithread + 0x1) - 0x1 |
91 | | - bi = offset * (0x2 * ithread + 0x2) - 0x1 |
92 | | - t = thread_totals[ai + 0x1] |
93 | | - thread_totals[ai + 0x1] = thread_totals[bi + 0x1] |
94 | | - thread_totals[bi + 0x1] = op(thread_totals[bi + 0x1], t) |
95 | | - end |
96 | | - d = d << 0x1 |
97 | | - end |
98 | | - @synchronize() |
99 | | - thread_prefix = thread_totals[ithread + 0x1] |
| 103 | + thread_prefix, block_total = block_exclusive_scan!( |
| 104 | + @context, op, thread_totals, seed, block_size, ithread, |
| 105 | + ) |
100 | 106 |
|
101 | 107 | # DecoupledLookback keeps later blocks inclusive until the carry pass. |
102 | 108 | block_inclusive = inclusive || (iblock != 0x0 && !isnothing(flags)) |
|
0 commit comments