Skip to content

Commit 8c33baf

Browse files
sifakisclaude
andcommitted
ex_voxelBlockManager_host_cuda: replace OpenMP threading with util::forEach
Use nanovdb::util::forEach (TBB-backed) for parallel block iteration in the CPU benchmarks, removing the OpenMP threading dependency. Keep #pragma omp simd as a pure vectorization hint. This eliminates the OpenMP linker/include issues on clang++ and also improves performance: TBB work-stealing yields ~3-4x faster sanity checks and more consistent full-decode timings vs. OMP schedule(static). Thread-private scratch globals are replaced by lambda-local arrays; the reduction dummy becomes std::atomic<uint32_t>. CMakeLists no longer needs find_package(OpenMP) or OpenMP::OpenMP_CXX for this target. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: Efty Sifakis <esifakis@nvidia.com>
1 parent 4538b7a commit 8c33baf

2 files changed

Lines changed: 21 additions & 41 deletions

File tree

nanovdb/nanovdb/examples/CMakeLists.txt

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -116,15 +116,9 @@ nanovdb_example(NAME "ex_coarsen_nanovdb_cuda" OPENVDB)
116116

117117
nanovdb_example(NAME "ex_voxelBlockManager_host_cuda")
118118
if(TARGET ex_voxelBlockManager_host_cuda)
119-
find_package(OpenMP)
120119
target_compile_options(ex_voxelBlockManager_host_cuda PRIVATE
121120
$<$<COMPILE_LANGUAGE:CUDA>:-Xcompiler=-mavx2>
122121
$<$<COMPILE_LANGUAGE:CXX>:-mavx2>)
123-
if(OpenMP_CXX_FOUND)
124-
target_link_libraries(ex_voxelBlockManager_host_cuda PRIVATE OpenMP::OpenMP_CXX)
125-
target_compile_options(ex_voxelBlockManager_host_cuda PRIVATE
126-
$<$<COMPILE_LANGUAGE:CUDA>:-Xcompiler=-fopenmp>)
127-
endif()
128122
endif()
129123

130124
if(CUDAToolkit_FOUND)

nanovdb/nanovdb/examples/ex_voxelBlockManager_host_cuda/vbm_host_cuda_kernels.cu

Lines changed: 21 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -16,20 +16,17 @@
1616
#include <nanovdb/HostBuffer.h>
1717
#include <nanovdb/tools/cuda/PointsToGrid.cuh>
1818
#include <nanovdb/tools/VoxelBlockManager.h>
19+
#include <nanovdb/util/ForEach.h>
1920
#include <nanovdb/util/Timer.h>
2021
#include <nanovdb/util/cuda/Timer.h>
2122
#include <nanovdb/util/cuda/Util.h>
2223

2324
#include <thrust/universal_vector.h>
2425

2526
#include <immintrin.h>
26-
#ifdef _OPENMP
27-
#include <omp.h>
28-
#else
29-
inline int omp_get_max_threads() { return 1; }
30-
#endif
3127

3228
#include <algorithm>
29+
#include <atomic>
3330
#include <vector>
3431
#include <iostream>
3532

@@ -38,13 +35,6 @@ namespace {
3835
static constexpr int Log2BlockWidth = 7;
3936
static constexpr int BlockWidth = 1 << Log2BlockWidth; // 128
4037

41-
// Thread-private scratch arrays for CPU decodeInverseMaps benchmarks.
42-
// Declared at namespace scope with static storage so threadprivate is valid;
43-
// aligned to a cache line to avoid false sharing between threads.
44-
alignas(64) static uint32_t g_scratchLeafIndex[BlockWidth];
45-
alignas(64) static uint16_t g_scratchVoxelOffset[BlockWidth];
46-
#pragma omp threadprivate(g_scratchLeafIndex, g_scratchVoxelOffset)
47-
4838
using VBM = nanovdb::tools::cuda::VoxelBlockManager<Log2BlockWidth>;
4939
using CPUVBM = nanovdb::tools::VoxelBlockManager<Log2BlockWidth>;
5040

@@ -381,23 +371,19 @@ void runVBMCudaTest(const std::vector<nanovdb::Coord>& coords)
381371
static constexpr int nPerfRuns = 20;
382372

383373
// --- Sanity check: countOn + std::fill only, no tree access ---
384-
// If OpenMP parallelism is working, this should scale near-linearly.
385374
{
386-
std::cout << "\nCPU decodeInverseMaps sanity (countOn only, "
387-
<< omp_get_max_threads() << " OMP threads):\n";
375+
std::cout << "\nCPU decodeInverseMaps sanity (countOn only):\n";
388376

389-
// Never true: nExtraLeaves <= JumpMapLength*64, so == JumpMapLength*64+1 is impossible.
390-
// Used only to prevent dead code elimination without a reduction.
377+
// Never true: used only to prevent dead code elimination.
391378
volatile uint32_t dummy = 0;
392379

393380
for (int run = 0; run < nPerfRuns; ++run) {
394381
nanovdb::util::Timer timer;
395382
timer.start("");
396383

397-
#pragma omp parallel
398-
{
399-
#pragma omp for schedule(static)
400-
for (uint32_t bID = 0; bID < nBlocks; ++bID) {
384+
nanovdb::util::forEach(0, nBlocks, 1,
385+
[&](const nanovdb::util::Range1D& range) {
386+
for (auto bID = range.begin(); bID < range.end(); ++bID) {
401387
int nExtraLeaves = 0;
402388
for (int i = 0; i < CPUVBM::JumpMapLength; i++)
403389
nExtraLeaves += nanovdb::util::countOn(
@@ -443,28 +429,28 @@ void runVBMCudaTest(const std::vector<nanovdb::Coord>& coords)
443429
if (prefixCountRealigned[31][15] == 513u)
444430
dummy = prefixCountRealigned[31][15];
445431
}
446-
}
432+
});
447433

448434
const float ms =
449435
(float)timer.elapsed<std::chrono::microseconds>() / 1000.0f;
450436
std::cout << " run " << run << ": " << ms << " ms\n";
451437
}
452438
}
453439

454-
uint32_t dummy = 0;
440+
std::atomic<uint32_t> dummy{0};
455441

456442
std::cout << "\nCPU decodeInverseMaps performance ("
457-
<< nBlocks << " blocks, " << nVoxels << " voxels"
458-
<< ", " << omp_get_max_threads() << " OMP threads):\n";
443+
<< nBlocks << " blocks, " << nVoxels << " voxels):\n";
459444

460445
for (int run = 0; run < nPerfRuns; ++run) {
461446
nanovdb::util::Timer timer;
462447
timer.start("");
463448

464-
#pragma omp parallel reduction(+:dummy)
465-
{
466-
#pragma omp for schedule(static)
467-
for (uint32_t bID = 0; bID < nBlocks; ++bID) {
449+
nanovdb::util::forEach(0, nBlocks, 1,
450+
[&](const nanovdb::util::Range1D& range) {
451+
alignas(64) uint32_t scratchLeafIndex[BlockWidth];
452+
alignas(64) uint16_t scratchVoxelOffset[BlockWidth];
453+
for (auto bID = range.begin(); bID < range.end(); ++bID) {
468454
const uint64_t blockFirstOffset =
469455
cpuHandle.firstOffset() + (uint64_t)bID * BlockWidth;
470456

@@ -473,18 +459,18 @@ void runVBMCudaTest(const std::vector<nanovdb::Coord>& coords)
473459
firstLeafIDPtr[bID],
474460
&jumpMapPtr[(uint64_t)bID * CPUVBM::JumpMapLength],
475461
blockFirstOffset,
476-
g_scratchLeafIndex,
477-
g_scratchVoxelOffset);
462+
scratchLeafIndex,
463+
scratchVoxelOffset);
478464

479-
dummy += (g_scratchLeafIndex[0] != CPUVBM::UnusedLeafIndex) ? 1u : 0u;
480-
dummy += (g_scratchVoxelOffset[0] != CPUVBM::UnusedVoxelOffset) ? 1u : 0u;
465+
dummy += (scratchLeafIndex[0] != CPUVBM::UnusedLeafIndex) ? 1u : 0u;
466+
dummy += (scratchVoxelOffset[0] != CPUVBM::UnusedVoxelOffset) ? 1u : 0u;
481467
}
482-
}
468+
});
483469

484470
const float ms =
485471
(float)timer.elapsed<std::chrono::microseconds>() / 1000.0f;
486472
std::cout << " run " << run << ": " << ms << " ms\n";
487473
}
488-
std::cout << " (dummy=" << dummy << ")\n";
474+
std::cout << " (dummy=" << dummy.load() << ")\n";
489475
}
490476
}

0 commit comments

Comments
 (0)