Skip to content

Commit d427564

Browse files
antoniopetrefwyzard
authored andcommitted
[alpaka] Refactor prefixScan implementation
1 parent 9ee23ee commit d427564

3 files changed

Lines changed: 66 additions & 110 deletions

File tree

src/alpaka/AlpakaCore/HistoContainer.h

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -74,17 +74,15 @@ namespace cms {
7474
const unsigned int nblocks = (num_items + nthreads - 1) / nthreads;
7575
const Vec1 blocksPerGrid(nblocks);
7676

77+
auto d_pc = cms::alpakatools::allocDeviceBuf<int32_t>(1u);
78+
int32_t *pc = alpaka::getPtrNative(d_pc);
79+
alpaka::memset(queue, d_pc, 0, 1u);
80+
7781
const WorkDiv1 &workDiv = cms::alpakatools::make_workdiv(blocksPerGrid, threadsPerBlockOrElementsPerThread);
7882
alpaka::enqueue(queue,
7983
alpaka::createTaskKernel<ALPAKA_ACCELERATOR_NAMESPACE::Acc1>(
80-
workDiv, multiBlockPrefixScanFirstStep<uint32_t>(), poff, poff, num_items));
81-
82-
const WorkDiv1 &workDivWith1Block =
83-
cms::alpakatools::make_workdiv(Vec1::all(1), threadsPerBlockOrElementsPerThread);
84-
alpaka::enqueue(
85-
queue,
86-
alpaka::createTaskKernel<ALPAKA_ACCELERATOR_NAMESPACE::Acc1>(
87-
workDivWith1Block, multiBlockPrefixScanSecondStep<uint32_t>(), poff, poff, num_items, nblocks));
84+
workDiv, multiBlockPrefixScan<uint32_t>(), poff, poff, num_items, pc));
85+
alpaka::wait(queue);
8886
}
8987

9088
template <typename Histo, typename T>

src/alpaka/AlpakaCore/prefixScan.h

Lines changed: 52 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#include <cstdint>
55

66
#include "AlpakaCore/alpakaConfig.h"
7+
#include "AlpakaCore/threadfence.h"
78
#include "Framework/CMSUnrollLoop.h"
89

910
#ifdef ALPAKA_ACC_GPU_CUDA_ENABLED
@@ -49,23 +50,23 @@ namespace cms {
4950
#endif
5051
) {
5152
#if defined ALPAKA_ACC_GPU_CUDA_ENABLED and __CUDA_ARCH__
52-
uint32_t const blockDimension(alpaka::getWorkDiv<alpaka::Block, alpaka::Threads>(acc)[0u]);
53-
uint32_t const gridBlockIdx(alpaka::getIdx<alpaka::Grid, alpaka::Blocks>(acc)[0u]);
54-
uint32_t const blockThreadIdx(alpaka::getIdx<alpaka::Block, alpaka::Threads>(acc)[0u]);
55-
assert(ws);
53+
const int32_t blockDim(alpaka::getWorkDiv<alpaka::Block, alpaka::Threads>(acc)[0u]);
54+
const int32_t gridBlockIdx(alpaka::getIdx<alpaka::Grid, alpaka::Blocks>(acc)[0u]);
55+
const int32_t blockThreadIdx(alpaka::getIdx<alpaka::Block, alpaka::Threads>(acc)[0u]);
56+
ALPAKA_ASSERT_OFFLOAD(ws);
5657
ALPAKA_ASSERT_OFFLOAD(size <= 1024);
57-
ALPAKA_ASSERT_OFFLOAD(0 == blockDimension % 32);
58+
ALPAKA_ASSERT_OFFLOAD(0 == blockDim % 32);
5859
auto first = blockThreadIdx;
5960
auto mask = __ballot_sync(0xffffffff, first < size);
6061
auto laneId = blockThreadIdx & 0x1f;
6162

62-
for (auto i = first; i < size; i += blockDimension) {
63+
for (auto i = first; i < size; i += blockDim) {
6364
warpPrefixScan(laneId, ci, co, i, mask);
6465
auto warpId = i / 32;
6566
ALPAKA_ASSERT_OFFLOAD(warpId < 32);
6667
if (31 == laneId)
6768
ws[warpId] = co[i];
68-
mask = __ballot_sync(mask, i + blockDimension < size);
69+
mask = __ballot_sync(mask, i + blockDim < size);
6970
}
7071
alpaka::syncBlockThreads(acc);
7172
if (size <= 32)
@@ -74,7 +75,7 @@ namespace cms {
7475
warpPrefixScan(laneId, ws, blockThreadIdx, 0xffffffff);
7576
}
7677
alpaka::syncBlockThreads(acc);
77-
for (auto i = first + 32; i < size; i += blockDimension) {
78+
for (auto i = first + 32; i < size; i += blockDim) {
7879
uint32_t warpId = i / 32;
7980
co[i] += ws[warpId - 1];
8081
}
@@ -97,23 +98,23 @@ namespace cms {
9798
#endif
9899
) {
99100
#if defined ALPAKA_ACC_GPU_CUDA_ENABLED and __CUDA_ARCH__
100-
uint32_t const blockDimension(alpaka::getWorkDiv<alpaka::Block, alpaka::Threads>(acc)[0u]);
101-
uint32_t const gridBlockIdx(alpaka::getIdx<alpaka::Grid, alpaka::Blocks>(acc)[0u]);
102-
uint32_t const blockThreadIdx(alpaka::getIdx<alpaka::Block, alpaka::Threads>(acc)[0u]);
103-
assert(ws);
101+
const int32_t blockDim(alpaka::getWorkDiv<alpaka::Block, alpaka::Threads>(acc)[0u]);
102+
const int32_t gridBlockIdx(alpaka::getIdx<alpaka::Grid, alpaka::Blocks>(acc)[0u]);
103+
const int32_t blockThreadIdx(alpaka::getIdx<alpaka::Block, alpaka::Threads>(acc)[0u]);
104+
ALPAKA_ASSERT_OFFLOAD(ws);
104105
ALPAKA_ASSERT_OFFLOAD(size <= 1024);
105-
ALPAKA_ASSERT_OFFLOAD(0 == blockDimension % 32);
106+
ALPAKA_ASSERT_OFFLOAD(0 == blockDim % 32);
106107
auto first = blockThreadIdx;
107108
auto mask = __ballot_sync(0xffffffff, first < size);
108109
auto laneId = blockThreadIdx & 0x1f;
109110

110-
for (auto i = first; i < size; i += blockDimension) {
111+
for (auto i = first; i < size; i += blockDim) {
111112
warpPrefixScan(laneId, c, i, mask);
112113
auto warpId = i / 32;
113114
ALPAKA_ASSERT_OFFLOAD(warpId < 32);
114115
if (31 == laneId)
115116
ws[warpId] = c[i];
116-
mask = __ballot_sync(mask, i + blockDimension < size);
117+
mask = __ballot_sync(mask, i + blockDim < size);
117118
}
118119
alpaka::syncBlockThreads(acc);
119120
if (size <= 32)
@@ -122,7 +123,7 @@ namespace cms {
122123
warpPrefixScan(laneId, ws, blockThreadIdx, 0xffffffff);
123124
}
124125
alpaka::syncBlockThreads(acc);
125-
for (auto i = first + 32; i < size; i += blockDimension) {
126+
for (auto i = first + 32; i < size; i += blockDim) {
126127
auto warpId = i / 32;
127128
c[i] += ws[warpId - 1];
128129
}
@@ -135,98 +136,64 @@ namespace cms {
135136

136137
// limited to 1024*1024 elements....
137138
template <typename T>
138-
struct multiBlockPrefixScanFirstStep {
139+
struct multiBlockPrefixScan {
139140
template <typename T_Acc>
140-
ALPAKA_FN_ACC void operator()(const T_Acc& acc, T const* ci, T* co, int32_t size) const {
141-
uint32_t const blockDimension(alpaka::getWorkDiv<alpaka::Block, alpaka::Threads>(acc)[0u]);
142-
uint32_t const threadDimension(alpaka::getWorkDiv<alpaka::Thread, alpaka::Elems>(acc)[0u]);
143-
uint32_t const blockIdx(alpaka::getIdx<alpaka::Grid, alpaka::Blocks>(acc)[0u]);
141+
ALPAKA_FN_ACC void operator()(const T_Acc& acc, T const* ci, T* co, int32_t size, int32_t* pc) const {
142+
const int32_t blockDim(alpaka::getWorkDiv<alpaka::Block, alpaka::Threads>(acc)[0u]);
143+
const int32_t threadDim(alpaka::getWorkDiv<alpaka::Thread, alpaka::Elems>(acc)[0u]);
144+
const int32_t blockIdx(alpaka::getIdx<alpaka::Grid, alpaka::Blocks>(acc)[0u]);
145+
const int32_t threadIdx(alpaka::getIdx<alpaka::Block, alpaka::Threads>(acc)[0u]);
144146

145-
auto& ws = alpaka::declareSharedVar<T[32], __COUNTER__>(acc);
146147
// first each block does a scan of size 1024; (better be enough blocks....)
147-
#ifndef NDEBUG
148-
uint32_t const gridDimension(alpaka::getWorkDiv<alpaka::Grid, alpaka::Blocks>(acc)[0u]);
149-
ALPAKA_ASSERT_OFFLOAD(gridDimension / threadDimension <= 1024);
150-
#endif
151-
int off = blockDimension * blockIdx * threadDimension;
148+
int32_t const gridDim(alpaka::getWorkDiv<alpaka::Grid, alpaka::Blocks>(acc)[0u]);
149+
ALPAKA_ASSERT_OFFLOAD(gridDim / threadDim <= 1024);
150+
int off = blockDim * blockIdx * threadDim;
151+
auto& ws = alpaka::declareSharedVar<T[32], __COUNTER__>(acc);
152152
if (size - off > 0)
153-
blockPrefixScan(acc, ci + off, co + off, std::min(int(blockDimension * threadDimension), size - off), ws);
154-
}
155-
};
153+
blockPrefixScan(acc, ci + off, co + off, std::min(int(blockDim * threadDim), size - off), ws);
156154

157-
// limited to 1024*1024 elements....
158-
template <typename T>
159-
struct multiBlockPrefixScanSecondStep {
160-
template <typename T_Acc>
161-
ALPAKA_FN_ACC void operator()(const T_Acc& acc, T const* ci, T* co, int32_t size, int32_t numBlocks) const {
162-
uint32_t const blockDimension(alpaka::getWorkDiv<alpaka::Block, alpaka::Threads>(acc)[0u]);
163-
uint32_t const threadDimension(alpaka::getWorkDiv<alpaka::Thread, alpaka::Elems>(acc)[0u]);
155+
auto& isLastBlockDone = alpaka::declareSharedVar<bool, __COUNTER__>(acc);
156+
if (0 == threadIdx) {
157+
cms::alpakatools::threadfence(acc);
158+
auto value = alpaka::atomicAdd(acc, pc, 1, alpaka::hierarchy::Blocks{}); // block counter
159+
isLastBlockDone = (value == (gridDim - 1));
160+
}
164161

165-
uint32_t const threadIdx(alpaka::getIdx<alpaka::Block, alpaka::Threads>(acc)[0u]);
162+
alpaka::syncBlockThreads(acc);
166163

167-
auto* const psum(alpaka::getDynSharedMem<T>(acc));
164+
if (!isLastBlockDone)
165+
return;
168166

169-
// first each block does a scan of size 1024; (better be enough blocks....)
170-
ALPAKA_ASSERT_OFFLOAD(static_cast<int32_t>(blockDimension * threadDimension) >= numBlocks);
171-
for (int elemId = 0; elemId < static_cast<int>(threadDimension); ++elemId) {
172-
int index = +threadIdx * threadDimension + elemId;
167+
ALPAKA_ASSERT_OFFLOAD(gridDim == *pc);
173168

174-
if (index < numBlocks) {
175-
int lastElementOfPreviousBlockId = index * blockDimension * threadDimension - 1;
169+
auto& psum = alpaka::declareSharedVar<T[1024], __COUNTER__>(acc);
170+
171+
ALPAKA_ASSERT_OFFLOAD(static_cast<int32_t>(blockDim * threadDim) >= gridDim);
172+
173+
for (int elemId = 0; elemId < static_cast<int>(threadDim); ++elemId) {
174+
int index = +threadIdx * threadDim + elemId;
175+
176+
if (index < gridDim) {
177+
int lastElementOfPreviousBlockId = index * blockDim * threadDim - 1;
176178
psum[index] = (lastElementOfPreviousBlockId < size and lastElementOfPreviousBlockId >= 0)
177179
? co[lastElementOfPreviousBlockId]
178180
: T(0);
179181
}
180182
}
181183

182184
alpaka::syncBlockThreads(acc);
185+
blockPrefixScan(acc, psum, psum, gridDim, ws);
183186

184-
auto& ws = alpaka::declareSharedVar<T[32], __COUNTER__>(acc);
185-
blockPrefixScan(acc, psum, psum, numBlocks, ws);
186-
187-
for (int elemId = 0; elemId < static_cast<int>(threadDimension); ++elemId) {
188-
int first = threadIdx * threadDimension + elemId;
189-
for (int i = first + blockDimension * threadDimension; i < size; i += blockDimension * threadDimension) {
190-
auto k = i / (blockDimension * threadDimension);
187+
for (int elemId = 0; elemId < static_cast<int>(threadDim); ++elemId) {
188+
int first = threadIdx * threadDim + elemId;
189+
for (int i = first + blockDim * threadDim; i < size; i += blockDim * threadDim) {
190+
auto k = i / (blockDim * threadDim);
191191
co[i] += psum[k];
192192
}
193193
}
194194
}
195195
};
196-
197196
} // namespace alpakatools
198197
} // namespace cms
199198

200-
namespace alpaka {
201-
namespace traits {
202-
203-
//#############################################################################
204-
//! The trait for getting the size of the block shared dynamic memory for a kernel.
205-
template <typename T, typename TAcc>
206-
struct BlockSharedMemDynSizeBytes<cms::alpakatools::multiBlockPrefixScanSecondStep<T>, TAcc> {
207-
//-----------------------------------------------------------------------------
208-
//! \return The size of the shared memory allocated for a block.
209-
template <typename TVec>
210-
ALPAKA_FN_HOST_ACC static auto getBlockSharedMemDynSizeBytes(
211-
cms::alpakatools::multiBlockPrefixScanSecondStep<T> const& myKernel,
212-
TVec const& blockThreadExtent,
213-
TVec const& threadElemExtent,
214-
T const* ci,
215-
T* co,
216-
int32_t size,
217-
int32_t numBlocks) -> T {
218-
alpaka::ignore_unused(myKernel);
219-
alpaka::ignore_unused(blockThreadExtent);
220-
alpaka::ignore_unused(threadElemExtent);
221-
alpaka::ignore_unused(ci);
222-
alpaka::ignore_unused(co);
223-
alpaka::ignore_unused(size);
224-
225-
return static_cast<size_t>(numBlocks) * sizeof(T);
226-
}
227-
};
228-
229-
} // namespace traits
230-
} // namespace alpaka
231-
232199
#endif // HeterogeneousCore_AlpakaUtilities_interface_prefixScan_h

src/alpaka/test/alpaka/prefixScan_t.cc

Lines changed: 8 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -176,23 +176,14 @@ int main() {
176176
cms::alpakatools::make_workdiv(blocksPerGrid4, threadsPerBlockOrElementsPerThread4);
177177

178178
std::cout << "launch multiBlockPrefixScan " << num_items << ' ' << nBlocks << std::endl;
179-
alpaka::enqueue(queue,
180-
alpaka::createTaskKernel<Acc1>(workDivMultiBlock,
181-
cms::alpakatools::multiBlockPrefixScanFirstStep<uint32_t>(),
182-
input_d,
183-
output1_d,
184-
num_items));
185-
186-
const Vec1 blocksPerGridSecondStep(Vec1::all(1));
187-
const WorkDiv1& workDivMultiBlockSecondStep =
188-
cms::alpakatools::make_workdiv(blocksPerGridSecondStep, threadsPerBlockOrElementsPerThread4);
189-
alpaka::enqueue(queue,
190-
alpaka::createTaskKernel<Acc1>(workDivMultiBlockSecondStep,
191-
cms::alpakatools::multiBlockPrefixScanSecondStep<uint32_t>(),
192-
input_d,
193-
output1_d,
194-
num_items,
195-
nBlocks));
179+
auto d_pc(alpaka::allocBuf<int32_t, Idx>(device, size));
180+
int32_t* pc = alpaka::getPtrNative(d_pc);
181+
182+
alpaka::memset(queue, d_pc, 0, size);
183+
alpaka::enqueue(
184+
queue,
185+
alpaka::createTaskKernel<Acc1>(
186+
workDivMultiBlock, cms::alpakatools::multiBlockPrefixScan<uint32_t>(), input_d, output1_d, num_items, pc));
196187

197188
alpaka::enqueue(queue, alpaka::createTaskKernel<Acc1>(workDivMultiBlock, verify(), output1_d, num_items));
198189

0 commit comments

Comments
 (0)