[CCCL] 瘦身 + 补全: 移除 cudax/python/libcudacxx-tests 冗余文件, 新增 c2h 测试助手 + cmake 构建系统 + 8 个 CUDA thrust examples

变更摘要:
- 删除: cudax/ (783 files, 7.2M) — 实验性组件,竞赛不需要
- 删除: python/ (226 files, 2.0M) — Python 绑定,竞赛不需要
- 删除: libcudacxx/{test,benchmarks,codegen,cmake,share} (4432 files, 31M)
  保留: libcudacxx/include/ (1463 headers, cuda::std 编译依赖)
- 新增: c2h/ (27 files) — CUB Catch2 测试辅助头文件,编译 243 个测试必需
- 新增: cmake/ (29 files) — CCCL 原生 CMake 构建系统
- 新增: thrust/examples/cuda/ (7 files) + cpp_integration/ (1 file)
  async_reduce, custom_temporary_allocation, explicit_cuda_stream,
  global_device_vector, range_view, unwrap_pointer, wrap_pointer, device

结果: cccl_upstream 从 74M→35M (瘦身 53%), 核心内容 100% 保留:
  27/27 tuning headers, 78 benchmarks, 243 tests,
  60 thrust examples, 18 CUB examples, 全部编译头文件
This commit is contained in:
muh-bot
2026-08-03 12:39:26 +00:00
parent a2a5dd8f00
commit 24ef6a91b5
5439 changed files with 0 additions and 719516 deletions

View File

@@ -1,98 +0,0 @@
//===----------------------------------------------------------------------===//
//
// Part of CUDASTF in CUDA C++ Core Libraries,
// under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.
//
//===----------------------------------------------------------------------===//
#include <cuda/experimental/stf.cuh>
/*
* The goal of this test is to ensure that using read access modes actually
* results in concurrent tasks
*/
using namespace cuda::experimental::stf;
/**
* @brief Call `__nanosleep` (potentially repeatedly) to sleep `nanoseconds` nanoseconds. Supports sleep times longer
* than 4 billion nanoseconds (i.e. 4 seconds).
*
* @param nanoseconds how many nanoseconds to sleep
* @return void
*/
__global__ void nano_sleep(unsigned long long nanoseconds)
{
#if defined(__CUDA_ARCH__) && (__CUDA_ARCH__ >= 700)
static constexpr auto m = std::numeric_limits<unsigned int>::max();
for (;;)
{
if (nanoseconds > m)
{
__nanosleep(m);
nanoseconds -= m;
}
else
{
__nanosleep(static_cast<unsigned int>(nanoseconds));
break;
}
}
#else
const clock_t end = clock() + nanoseconds / (1000000000ULL / CLOCKS_PER_SEC);
while (clock() < end)
{
// busy wait
}
#endif
}
void run(context& ctx, int NTASKS, int ms)
{
int dummy[1];
auto handle = ctx.logical_data(dummy);
ctx.task().add_deps(handle.rw())->*[](cudaStream_t stream) {
nano_sleep<<<1, 1, 0, stream>>>(0);
};
for (int iter = 0; iter < 10; iter++)
{
for (int k = 0; k < NTASKS; k++)
{
ctx.task().add_deps(handle.read())->*[&](cudaStream_t stream) {
nano_sleep<<<1, 1, 0, stream>>>(ms * 1000ULL * 1000ULL);
};
}
ctx.task().add_deps(handle.rw())->*[&](cudaStream_t stream) {
nano_sleep<<<1, 1, 0, stream>>>(0);
};
}
ctx.finalize();
}
int main(int argc, char** argv)
{
int NTASKS = 256;
int ms = 40;
if (argc > 1)
{
NTASKS = atoi(argv[1]);
}
if (argc > 2)
{
ms = atoi(argv[2]);
}
context ctx;
run(ctx, NTASKS, ms);
ctx = graph_ctx();
run(ctx, NTASKS, ms);
}

View File

@@ -1,93 +0,0 @@
//===----------------------------------------------------------------------===//
//
// Part of CUDASTF in CUDA C++ Core Libraries,
// under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.
//
//===----------------------------------------------------------------------===//
/**
* @file
* @brief This test ensures that read() can be called on const logical_data
* (typed and untyped).
*/
#include <cuda/experimental/stf.cuh>
using namespace cuda::experimental::stf;
struct foo
{
// Intentionally choose an odd (actually prime) size
foo(context& ctx)
{
l = ctx.logical_data(shape_of<slice<int>>(50867));
}
void set(context& ctx, int val)
{
ctx.parallel_for(l.shape(), l.write())->*[=] _CCCL_DEVICE(size_t i, auto dl) {
dl(i) = val;
};
}
void copy_from(context& ctx, const foo& other)
{
ctx.parallel_for(l.shape(), l.write(), other.l.read())->*[=] _CCCL_DEVICE(size_t i, auto dl, auto dotherl) {
dl(i) = dotherl(i);
};
}
void ensure(context& ctx, int val)
{
std::ignore = val;
ctx.parallel_for(l.shape(), l.read())->*[=] _CCCL_DEVICE(size_t i, auto dl) {
assert(dl(i) == val);
};
}
auto& get_l() const
{
return l;
}
logical_data<slice<int>> l;
};
void read_only_access(context& ctx, const foo& f)
{
ctx.parallel_for(f.l.shape(), f.l.read())->*[] _CCCL_DEVICE(size_t i, auto dl) {
// no-op
};
ctx.parallel_for(f.get_l().shape(), f.get_l().read())->*[] _CCCL_DEVICE(size_t i, auto dl) {
// no-op
};
}
void read_only_access_untyped(const logical_data_untyped& ld)
{
auto dep = ld.read();
(void) dep;
}
int main()
{
context ctx;
foo A(ctx);
A.set(ctx, 42);
A.ensure(ctx, 42);
foo B(ctx);
B.copy_from(ctx, A);
B.ensure(ctx, 42);
read_only_access(ctx, A);
const logical_data_untyped& ld_untyped = A.l;
read_only_access_untyped(ld_untyped);
ctx.finalize();
}

View File

@@ -1,86 +0,0 @@
//===----------------------------------------------------------------------===//
//
// Part of CUDASTF in CUDA C++ Core Libraries,
// under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.
//
//===----------------------------------------------------------------------===//
/**
* @file
* @brief Ensure we can use the same logical data multiple time in a task
*/
#include <cuda/experimental/__stf/graph/graph_ctx.cuh>
#include <cuda/experimental/__stf/stream/stream_ctx.cuh>
using namespace cuda::experimental::stf;
template <typename T>
__global__ void diff_cnt(int n, T* x, T* y, int* delta)
{
int tid = blockIdx.x * blockDim.x + threadIdx.x;
int nthreads = gridDim.x * blockDim.x;
for (int ind = tid; ind < n; ind += nthreads)
{
if (y[ind] != x[ind])
{
atomicAdd(delta, 1);
}
}
}
template <typename Ctx, typename T>
void compare_two_vectors(Ctx& ctx, logical_data<T>& a, logical_data<T>& b, int& delta)
{
auto delta_cnt = ctx.logical_data(make_slice(&delta, 1));
const auto n = a.shape().extent(0);
// Count the number of differences
ctx.task(a.read(), b.read(), delta_cnt.rw())->*[=](cudaStream_t stream, auto da, auto db, auto ddelta) {
diff_cnt<<<16, 128, 0, stream>>>(static_cast<int>(n), da.data_handle(), db.data_handle(), ddelta.data_handle());
};
// Read that value on the host
ctx.host_launch(delta_cnt.read())->*[&](auto /*unused*/) {};
}
static const size_t N = 12;
template <class Ctx>
void run(double (&X)[N], double (&Y)[N])
{
Ctx ctx;
auto handle_X = ctx.logical_data(X);
auto handle_Y = ctx.logical_data(Y);
int ret1 = 0, ret2 = 0;
compare_two_vectors(ctx, handle_X, handle_Y, ret1);
compare_two_vectors(ctx, handle_X, handle_X, ret2);
ctx.finalize();
// After sync, we can inspect the returned values.
// First two vectors are different
assert(ret1 > 0);
// Other two vectors are equal
assert(ret2 == 0);
}
int main()
{
double X[N], Y[N];
for (size_t ind = 0; ind < N; ind++)
{
X[ind] = 1.0 * ind;
Y[ind] = 2.0 * ind - 3.0;
}
run<stream_ctx>(X, Y);
run<graph_ctx>(X, Y);
}

View File

@@ -1,55 +0,0 @@
//===----------------------------------------------------------------------===//
//
// Part of CUDASTF in CUDA C++ Core Libraries,
// under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.
//
//===----------------------------------------------------------------------===//
/**
* @file
* @brief Ensure we can use the same logical data multiple times in the same
* task even with different access modes (which should be combined)
*/
#include <cuda/experimental/__stf/graph/graph_ctx.cuh>
#include <cuda/experimental/__stf/stream/stream_ctx.cuh>
using namespace cuda::experimental::stf;
// a = b + 1;
template <typename T>
__global__ void add(T* a, const T* b)
{
*a = *b + 1;
}
template <class Ctx>
void run()
{
Ctx ctx;
int var = 42;
auto var_handle = ctx.logical_data(make_slice(&var, 1));
// da and db are for the same variable : we expect it to be equivalent to a RW access
ctx.task(var_handle.write(), var_handle.read())->*[](cudaStream_t stream, auto da, auto db) {
add<<<1, 1, 0, stream>>>(da.data_handle(), db.data_handle());
};
// Read that value on the host
ctx.host_launch(var_handle.read())->*[](auto da) {
[[maybe_unused]] int result = *da.data_handle();
assert(result == 43);
};
ctx.finalize();
}
int main()
{
run<stream_ctx>();
run<graph_ctx>();
}

View File

@@ -1,80 +0,0 @@
//===----------------------------------------------------------------------===//
//
// Part of CUDASTF in CUDA C++ Core Libraries,
// under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.
//
//===----------------------------------------------------------------------===//
#include <cuda/experimental/__stf/utility/run_once.cuh>
#include <cuda/experimental/stf.cuh>
using namespace cuda::experimental::stf;
int main()
{
context ctx;
const int N = 16;
size_t niter = 12;
int A[N];
for (int i = 0; i < N; i++)
{
A[i] = 2 * i + 1;
}
auto lres = ctx.logical_data(A);
for (size_t k = 0; k < niter; k++)
{
auto ltmp = ctx.logical_data(lres.shape());
ctx.parallel_for(ltmp.shape(), ltmp.write())->*[] __device__(size_t i, auto tmp) {
tmp(i) = i;
};
ctx.parallel_for(lres.shape(), ltmp.read(), lres.rw())->*[] __device__(size_t i, auto tmp, auto res) {
res(i) += tmp(i);
};
}
for (size_t k = 0; k < niter; k++)
{
auto ltmp = run_once()->*[&]() {
// Ensure this is only done once !
static bool done = false;
EXPECT(!done);
done = true;
auto ltmp = ctx.logical_data(lres.shape());
ctx.parallel_for(ltmp.shape(), ltmp.write())->*[] __device__(size_t i, auto tmp) {
tmp(i) = i;
};
return ltmp;
};
auto ltmp2 = run_once(size_t(k % 4))->*[&](size_t val) {
// fprintf(stderr, "COMPUTE FOR %ld\n", val);
auto ltmp = ctx.logical_data(lres.shape());
ctx.parallel_for(ltmp.shape(), ltmp.write())->*[val] __device__(size_t i, auto tmp) {
tmp(i) = val;
};
return ltmp;
};
ctx.parallel_for(lres.shape(), ltmp.read(), lres.rw())->*[] __device__(size_t i, auto tmp, auto res) {
res(i) += tmp(i);
};
}
ctx.finalize();
for (int i = 0; i < N; i++)
{
EXPECT(A[i] == (2 * i + 1) + 2 * i * niter);
}
}

View File

@@ -1,65 +0,0 @@
//===----------------------------------------------------------------------===//
//
// Part of CUDASTF in CUDA C++ Core Libraries,
// under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.
//
//===----------------------------------------------------------------------===//
#include <cuda/experimental/__stf/utility/run_once.cuh>
#include <cuda/experimental/stf.cuh>
using namespace cuda::experimental::stf;
int main()
{
context ctx;
const int N = 16;
size_t niter = 12;
int A[N];
for (int i = 0; i < N; i++)
{
A[i] = 2 * i + 1;
}
auto lres = ctx.logical_data(A);
for (size_t k = 0; k < niter; k++)
{
auto ltmp = ctx.logical_data(lres.shape());
ctx.parallel_for(ltmp.shape(), ltmp.write())->*[k] __device__(size_t i, auto tmp) {
tmp(i) = (k % 2) * i;
};
ctx.parallel_for(lres.shape(), ltmp.read(), lres.rw())->*[] __device__(size_t i, auto tmp, auto res) {
res(i) += tmp(i);
};
}
for (size_t k = 0; k < niter; k++)
{
auto ltmp = run_once(k)->*[&](size_t k) {
auto out = ctx.logical_data(lres.shape());
ctx.parallel_for(out.shape(), out.write())->*[k] __device__(size_t i, auto tmp) {
tmp(i) = (k % 2) * i;
};
return out;
};
ctx.parallel_for(lres.shape(), ltmp.read(), lres.rw())->*[] __device__(size_t i, auto tmp, auto res) {
res(i) += tmp(i);
};
}
ctx.finalize();
for (int i = 0; i < N; i++)
{
EXPECT(A[i] == (2 * i + 1) + 2 * i * niter / 2);
}
}

View File

@@ -1,103 +0,0 @@
//===----------------------------------------------------------------------===//
//
// Part of CUDASTF in CUDA C++ Core Libraries,
// under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.
//
//===----------------------------------------------------------------------===//
/**
* @file
* @brief Show how we can create tasks in the CUDA graph backend by using the
* actual CUDA graph API in tasks (instead of relying on graph capture
* implicitly)
*/
#include <cuda/experimental/__stf/graph/graph_ctx.cuh>
#include <iostream>
using namespace cuda::experimental::stf;
int main(int argc, char** argv)
{
const size_t n = 12;
double X[n];
double Y[n];
for (size_t ind = 0; ind < n; ind++)
{
X[ind] = 1.0 * ind + 42;
Y[ind] = 0.0;
}
// We here do not assume there is a valid copy on the host and only provide
// constant parameters
graph_ctx ctx;
auto handle_X = ctx.logical_data(X);
handle_X.set_symbol("x");
auto handle_Y = ctx.logical_data(Y);
handle_Y.set_symbol("y");
auto handle_TMP = ctx.logical_data<double>(n);
handle_TMP.set_symbol("tmp");
int NITER = 4;
for (int iter = 0; iter < NITER; iter++)
{
// We swap X and Y using TMP as temporary buffer
// TMP = X
// X = Y
// Y = TMP
ctx.task(exec_place::current_device(), handle_X.rw(), handle_Y.rw(), handle_TMP.write())
->*[&](cudaGraph_t child_graph, auto d_x, auto d_y, auto d_tmp) {
// TMP = X
cudaGraphNode_t cpy_tmp_to_x;
cuda_try(cudaGraphAddMemcpyNode1D(
&cpy_tmp_to_x,
child_graph,
nullptr,
0,
d_tmp.data_handle(),
d_x.data_handle(),
n * sizeof(double),
cudaMemcpyDeviceToDevice));
// X = Y
cudaGraphNode_t cpy_x_to_y;
cuda_try(cudaGraphAddMemcpyNode1D(
&cpy_x_to_y,
child_graph,
&cpy_tmp_to_x,
1,
d_x.data_handle(),
d_y.data_handle(),
n * sizeof(double),
cudaMemcpyDeviceToDevice));
// Y = TMP
cudaGraphNode_t cpy_tmp_to_y;
cuda_try(cudaGraphAddMemcpyNode1D(
&cpy_tmp_to_y,
child_graph,
&cpy_x_to_y,
1,
d_y.data_handle(),
d_tmp.data_handle(),
n * sizeof(double),
cudaMemcpyDeviceToDevice));
};
}
ctx.submit();
if (argc > 1)
{
std::cout << "Generating DOT output in " << argv[1] << '\n';
ctx.print_to_dot(argv[1]);
}
ctx.finalize();
}

View File

@@ -1,65 +0,0 @@
//===----------------------------------------------------------------------===//
//
// Part of CUDASTF in CUDA C++ Core Libraries,
// under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.
//
//===----------------------------------------------------------------------===//
//! \file
//!
//! \brief Test the behavior of the get_stream() method of the tasks in the different backends
#include <cuda/experimental/stf.cuh>
using namespace cuda::experimental::stf;
__global__ void dummy() {}
void test_stream()
{
// stream context
context ctx;
auto token = ctx.token();
EXPECT(token.is_void_interface());
auto t = ctx.task(token.write());
t.start();
cudaStream_t s = t.get_stream();
EXPECT(s != nullptr);
dummy<<<1, 1, 0, s>>>();
t.end();
ctx.finalize();
}
void test_graph()
{
context ctx = graph_ctx();
auto token = ctx.token();
auto t = ctx.task(token.write());
t.start();
cudaStream_t s = t.get_stream();
// We are not capturing so there is no stream associated
EXPECT(s == nullptr);
t.end();
auto t2 = ctx.task(token.rw());
t2.enable_capture();
t2.start();
cudaStream_t s2 = t2.get_stream();
// We are capturing so the stream used for capture is associated to the task
EXPECT(s2 != nullptr);
t2.end();
ctx.finalize();
}
int main()
{
test_stream();
test_graph();
return 0;
}

View File

@@ -1,251 +0,0 @@
//===----------------------------------------------------------------------===//
//
// Part of CUDASTF in CUDA C++ Core Libraries,
// under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// SPDX-FileCopyrightText: Copyright (c) 2022-2025 NVIDIA CORPORATION & AFFILIATES.
//
//===----------------------------------------------------------------------===//
//! \file
//! \brief Test the stream picking functionality using execution place abstraction
#include <cuda/experimental/stf.cuh>
using namespace cuda::experimental::stf;
int main()
{
// Get the number of available devices
int device_count;
cuda_safe_call(cudaGetDeviceCount(&device_count));
// Create async_resources_handle for stream pool management.
// This can be used independently of any CUDASTF context.
async_resources_handle resources;
// Get current device for comparison
int current_device;
cuda_safe_call(cudaGetDevice(&current_device));
// ==========================================================================
// Test exec_place::pick_stream() - returns cudaStream_t directly
// ==========================================================================
{
exec_place place = exec_place::current_device();
// pick_stream() returns a cudaStream_t directly (simpler API)
cudaStream_t stream = place.pick_stream(resources);
EXPECT(stream != nullptr);
EXPECT(get_device_from_stream(stream) == current_device);
// The for_computation parameter is a performance hint (defaults to true).
// When true, uses the computation stream pool; when false, uses the
// transfer stream pool. Using separate pools can improve overlapping.
cudaStream_t compute_stream = place.pick_stream(resources, true);
cudaStream_t transfer_stream = place.pick_stream(resources, false);
EXPECT(compute_stream != nullptr);
EXPECT(transfer_stream != nullptr);
}
// ==========================================================================
// Test exec_place::getStream() - returns augmented_stream with metadata
// ==========================================================================
{
exec_place place = exec_place::current_device();
// getStream() returns a augmented_stream with additional metadata
augmented_stream dstream = place.getStream(resources, true);
EXPECT(dstream.stream != nullptr);
EXPECT(dstream.dev_id == current_device);
EXPECT(get_device_from_stream(dstream.stream) == current_device);
}
// ==========================================================================
// Test stream_pool_size() and pick_all_streams()
// ==========================================================================
{
exec_place place = exec_place::current_device();
// Query the pool size
size_t pool_size = place.stream_pool_size(resources);
EXPECT(pool_size > 0);
EXPECT(pool_size == async_resources_handle::pool_size);
// Get all streams from the pool as a vector
auto all_streams = place.pick_all_streams(resources);
EXPECT(all_streams.size() == pool_size);
// Verify all streams are valid and on the correct device
for (cudaStream_t s : all_streams)
{
EXPECT(s != nullptr);
EXPECT(get_device_from_stream(s) == current_device);
}
}
// ==========================================================================
// Test with multiple devices
// ==========================================================================
if (device_count > 1)
{
for (int test_device = 0; test_device < ::std::min(device_count, 2); ++test_device)
{
exec_place dev_place = exec_place::device(test_device);
// pick_stream on a specific device
cudaStream_t stream = dev_place.pick_stream(resources);
EXPECT(stream != nullptr);
EXPECT(get_device_from_stream(stream) == test_device);
// getStream returns more metadata
augmented_stream dstream = dev_place.getStream(resources, true);
EXPECT(dstream.stream != nullptr);
EXPECT(dstream.dev_id == test_device);
}
}
// ==========================================================================
// Test activate()/deactivate() - generic alternative to cudaSetDevice
// These methods can be used without a CUDASTF context
// ==========================================================================
{
// Save initial device
int initial_device;
cuda_safe_call(cudaGetDevice(&initial_device));
// Use activate() to switch to current device (no-op but verifies it works)
exec_place current_place = exec_place::current_device();
{
auto active = current_place.activate();
int after_activate;
cuda_safe_call(cudaGetDevice(&after_activate));
EXPECT(after_activate == initial_device);
}
// exec_place_scope destructor restores automatically
}
// Test activate() with multiple devices using RAII
if (device_count > 1)
{
// Save initial device
int initial_device;
cuda_safe_call(cudaGetDevice(&initial_device));
// Switch to device 1 using RAII scope
{
exec_place place1 = exec_place::device(1);
auto active = place1.activate();
// Verify we're now on device 1
int new_device;
cuda_safe_call(cudaGetDevice(&new_device));
EXPECT(new_device == 1);
}
// exec_place_scope destructor restores previous device
// Verify we're back on the initial device
int restored_device;
cuda_safe_call(cudaGetDevice(&restored_device));
EXPECT(restored_device == initial_device);
// Nested activation test
{
exec_place place0 = exec_place::device(0);
auto active0 = place0.activate();
int new_device;
cuda_safe_call(cudaGetDevice(&new_device));
EXPECT(new_device == 0);
{
exec_place place1 = exec_place::device(1);
auto active1 = place1.activate();
cuda_safe_call(cudaGetDevice(&new_device));
EXPECT(new_device == 1);
}
// active1 destroyed, should restore to device 0
cuda_safe_call(cudaGetDevice(&new_device));
EXPECT(new_device == 0);
}
// active0 destroyed, should restore to initial device
cuda_safe_call(cudaGetDevice(&restored_device));
EXPECT(restored_device == initial_device);
}
// Test that host exec_place activate works (no-op in practice)
{
exec_place host_place = exec_place::host();
auto active = host_place.activate();
}
// ==========================================================================
// Test context stream picking (for comparison)
// ==========================================================================
{
context ctx;
// Contexts also have pick_stream() which uses the default execution place
cudaStream_t stream = ctx.pick_stream();
EXPECT(stream != nullptr);
EXPECT(get_device_from_stream(stream) == current_device);
ctx.finalize();
}
// ==========================================================================
// Test using exec_place::pick_stream with a context's async_resources
// When working alongside a context, use ctx.async_resources() to share
// the same stream pools between your code and the context's operations.
// ==========================================================================
{
stream_ctx ctx;
// Get a stream from a specific execution place using the context's resources
exec_place place = exec_place::current_device();
cudaStream_t stream1 = place.pick_stream(ctx.async_resources());
EXPECT(stream1 != nullptr);
EXPECT(get_device_from_stream(stream1) == current_device);
// This stream comes from the same pool used by ctx internally
cudaStream_t stream2 = ctx.pick_stream();
EXPECT(stream2 != nullptr);
// Both methods use the same underlying stream pool
// (streams may or may not be the same depending on round-robin selection)
ctx.finalize();
}
// ==========================================================================
// Test with graph context
// ==========================================================================
{
graph_ctx gctx;
cudaStream_t stream = gctx.pick_stream();
EXPECT(stream != nullptr);
EXPECT(get_device_from_stream(stream) == current_device);
gctx.finalize();
}
// ==========================================================================
// Test context with execution affinity
// ==========================================================================
if (device_count > 1)
{
context ctx;
exec_place dev1_place = exec_place::device(1);
ctx.push_affinity(::std::make_shared<exec_place>(dev1_place));
// Stream should now come from device 1's pool
cudaStream_t affinity_stream = ctx.pick_stream();
EXPECT(affinity_stream != nullptr);
EXPECT(get_device_from_stream(affinity_stream) == 1);
ctx.finalize();
}
}

View File

@@ -1,259 +0,0 @@
//===----------------------------------------------------------------------===//
//
// Part of CUDASTF in CUDA C++ Core Libraries,
// under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// SPDX-FileCopyrightText: Copyright (c) 2022-2025 NVIDIA CORPORATION & AFFILIATES.
//
//===----------------------------------------------------------------------===//
//! \file
//! \brief Test the pick_stream functionality with green contexts
#include <cuda/experimental/__places/exec/green_context.cuh>
#include <cuda/experimental/stf.cuh>
using namespace cuda::experimental::stf;
// Green contexts are only supported since CUDA 12.4
#if _CCCL_CTK_AT_LEAST(12, 4)
//! \brief Verify that a stream belongs to the expected green context
void verify_stream_green_context(cudaStream_t stream, CUgreenCtx expected_g_ctx)
{
// Get the green context associated to that CUDA stream
CUgreenCtx stream_cugc;
cuda_safe_call(cuStreamGetGreenCtx(CUstream(stream), &stream_cugc));
EXPECT(stream_cugc != nullptr);
CUcontext stream_green_primary;
CUcontext expected_green_primary;
unsigned long long stream_ctxId;
unsigned long long expected_ctxId;
// Convert green contexts to primary contexts and get their ID
cuda_safe_call(cuCtxFromGreenCtx(&stream_green_primary, stream_cugc));
cuda_safe_call(cuCtxGetId(stream_green_primary, &stream_ctxId));
cuda_safe_call(cuCtxFromGreenCtx(&expected_green_primary, expected_g_ctx));
cuda_safe_call(cuCtxGetId(expected_green_primary, &expected_ctxId));
// Make sure the stream belongs to the same green context as expected
EXPECT(stream_ctxId == expected_ctxId);
}
#endif // _CCCL_CTK_AT_LEAST(12, 4)
int main()
{
#if _CCCL_CTK_BELOW(12, 4)
// Green contexts are not supported, skip the test
return 0;
#else // ^^^ _CCCL_CTK_BELOW(12, 4) ^^^ / vvv _CCCL_CTK_AT_LEAST(12, 4) vvv
// Get current device
int current_device;
cuda_safe_call(cudaGetDevice(&current_device));
// Create green context helper with 8 SMs per context
const int num_sms = 8;
green_context_helper gc(num_sms, current_device);
// Create async_resources_handle for stream pool management.
// This can be used independently of any CUDASTF context.
async_resources_handle resources;
// ==========================================================================
// Compare regular device vs green context execution places
// ==========================================================================
exec_place regular_device_place = exec_place::current_device();
// pick_stream() returns cudaStream_t directly
cudaStream_t device_stream = regular_device_place.pick_stream(resources);
EXPECT(device_stream != nullptr);
EXPECT(get_device_from_stream(device_stream) == current_device);
// ==========================================================================
// Test green context execution places - each has isolated stream pools
// ==========================================================================
auto cnt = gc.get_count();
if (cnt > 0)
{
// Test first green context view - demonstrates place-specific stream pools
auto view0 = gc.get_view(0);
exec_place gc_place0 = exec_place::green_ctx(view0);
// Green context execution place uses its dedicated stream pool (not shared device pool)
cudaStream_t gc_stream = gc_place0.pick_stream(resources);
EXPECT(gc_stream != nullptr);
EXPECT(get_device_from_stream(gc_stream) == current_device);
// Verify the stream belongs to the correct green context
verify_stream_green_context(gc_stream, view0.g_ctx);
// Test with multiple views - demonstrates isolation between green contexts
if (cnt > 1)
{
auto view1 = gc.get_view(1);
exec_place gc_place1 = exec_place::green_ctx(view1);
cudaStream_t gc_stream1 = gc_place1.pick_stream(resources);
EXPECT(gc_stream1 != nullptr);
EXPECT(get_device_from_stream(gc_stream1) == current_device);
// Each green context has its own isolated stream pool
verify_stream_green_context(gc_stream1, view1.g_ctx);
// Streams from different green context places are isolated
EXPECT(gc_stream != gc_stream1);
}
// getStream() provides additional metadata if needed
augmented_stream dstream = gc_place0.getStream(resources, true);
EXPECT(dstream.stream != nullptr);
EXPECT(dstream.dev_id == current_device);
}
// ==========================================================================
// Test activate()/deactivate() with green contexts
// These methods can be used without a CUDASTF context
// ==========================================================================
if (cnt > 0)
{
auto view = gc.get_view(0);
exec_place gc_place = exec_place::green_ctx(view);
// Save the current CUDA context
CUcontext initial_ctx;
cuda_safe_call(cuCtxGetCurrent(&initial_ctx));
unsigned long long initial_ctx_id;
cuda_safe_call(cuCtxGetId(initial_ctx, &initial_ctx_id));
{
// Activate the green context using RAII
auto active = gc_place.activate();
// Verify the current context is now the green context
CUcontext current_ctx;
cuda_safe_call(cuCtxGetCurrent(&current_ctx));
// The current context should be the green context's driver context
CUcontext green_driver_ctx;
cuda_safe_call(cuCtxFromGreenCtx(&green_driver_ctx, view.g_ctx));
unsigned long long current_ctx_id, green_ctx_id;
cuda_safe_call(cuCtxGetId(current_ctx, &current_ctx_id));
cuda_safe_call(cuCtxGetId(green_driver_ctx, &green_ctx_id));
EXPECT(current_ctx_id == green_ctx_id);
}
// exec_place_scope destructor restores previous context
// Verify we're back to the initial context
CUcontext restored_ctx;
cuda_safe_call(cuCtxGetCurrent(&restored_ctx));
unsigned long long restored_ctx_id;
cuda_safe_call(cuCtxGetId(restored_ctx, &restored_ctx_id));
EXPECT(initial_ctx_id == restored_ctx_id);
}
// Test switching between multiple green contexts using nested RAII
if (cnt > 1)
{
auto view0 = gc.get_view(0);
auto view1 = gc.get_view(1);
exec_place gc_place0 = exec_place::green_ctx(view0);
exec_place gc_place1 = exec_place::green_ctx(view1);
CUcontext green0_ctx, green1_ctx;
cuda_safe_call(cuCtxFromGreenCtx(&green0_ctx, view0.g_ctx));
cuda_safe_call(cuCtxFromGreenCtx(&green1_ctx, view1.g_ctx));
unsigned long long green0_id, green1_id;
cuda_safe_call(cuCtxGetId(green0_ctx, &green0_id));
cuda_safe_call(cuCtxGetId(green1_ctx, &green1_id));
{
// Activate first green context
auto active0 = gc_place0.activate();
// Verify we're in green context 0
CUcontext current_ctx;
cuda_safe_call(cuCtxGetCurrent(&current_ctx));
unsigned long long current_id;
cuda_safe_call(cuCtxGetId(current_ctx, &current_id));
EXPECT(current_id == green0_id);
{
// Switch to second green context (nested)
auto active1 = gc_place1.activate();
// Verify we're now in green context 1
cuda_safe_call(cuCtxGetCurrent(&current_ctx));
cuda_safe_call(cuCtxGetId(current_ctx, &current_id));
EXPECT(current_id == green1_id);
}
// active1 destroyed, should restore to green context 0
cuda_safe_call(cuCtxGetCurrent(&current_ctx));
cuda_safe_call(cuCtxGetId(current_ctx, &current_id));
EXPECT(current_id == green0_id);
}
// active0 destroyed, restores to original context
}
// ==========================================================================
// Test context with green context affinity
// ==========================================================================
{
stream_ctx ctx;
if (cnt > 0)
{
// Set affinity to green context execution place
auto view = gc.get_view(0);
exec_place gc_place = exec_place::green_ctx(view);
ctx.push_affinity(::std::make_shared<exec_place>(gc_place));
// Context pick_stream() respects the green context affinity
cudaStream_t stream = ctx.pick_stream();
EXPECT(stream != nullptr);
EXPECT(get_device_from_stream(stream) == current_device);
// Verify stream belongs to the green context we set as affinity
verify_stream_green_context(stream, view.g_ctx);
}
ctx.finalize();
}
// ==========================================================================
// Test graph context with green context affinity
// ==========================================================================
{
graph_ctx gctx;
if (cnt > 0)
{
// Set green context affinity for graph context
auto view = gc.get_view(0);
exec_place gc_place = exec_place::green_ctx(view);
gctx.push_affinity(::std::make_shared<exec_place>(gc_place));
// Graph context also respects the execution place abstraction
cudaStream_t graph_stream = gctx.pick_stream();
EXPECT(graph_stream != nullptr);
EXPECT(get_device_from_stream(graph_stream) == current_device);
// Verify graph submission stream also respects green context affinity
verify_stream_green_context(graph_stream, view.g_ctx);
}
gctx.finalize();
}
return 0;
#endif // ^^^ _CCCL_CTK_AT_LEAST(12, 4) ^^^
}

View File

@@ -1,99 +0,0 @@
//===----------------------------------------------------------------------===//
//
// Part of CUDASTF in CUDA C++ Core Libraries,
// under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.
//
//===----------------------------------------------------------------------===//
#include <cuda/experimental/__stf/stream/stream_ctx.cuh>
using namespace cuda::experimental::stf;
/*
* In this example, the user provides streams in which the STF model inserts the proper dependencies
*/
static __global__ void cuda_sleep_kernel(long long int clock_cnt)
{
long long int start_clock = clock64();
long long int clock_offset = 0;
while (clock_offset < clock_cnt)
{
clock_offset = clock64() - start_clock;
}
}
void cuda_sleep(double ms, cudaStream_t stream)
{
int device;
cudaGetDevice(&device);
// cudaDevAttrClockRate: Peak clock frequency in kilohertz;
int clock_rate;
cudaDeviceGetAttribute(&clock_rate, cudaDevAttrClockRate, device);
long long int clock_cnt = (long long int) (ms * clock_rate);
cuda_sleep_kernel<<<1, 1, 0, stream>>>(clock_cnt);
}
int main()
{
stream_ctx ctx;
double vA, vB, vC, vD;
auto A = ctx.logical_data(make_slice(&vA, 1));
auto B = ctx.logical_data(make_slice(&vB, 1));
auto C = ctx.logical_data(make_slice(&vC, 1));
auto D = ctx.logical_data(make_slice(&vD, 1));
// We are going to submit kernels with the following data accesses, where
// K2 and K3 can be executed concurrently, after K1 and been executed, and
// before K4 is executed.
// K1(Aw); K2(Ar,Bw); K3(Ar, Cw); K4(Br,Cr,Dw);
// User-provided streams
cudaStream_t K1_stream;
cudaStream_t K2_stream;
cudaStream_t K3_stream;
cudaStream_t K4_stream;
cudaStreamCreate(&K1_stream);
cudaStreamCreate(&K2_stream);
cudaStreamCreate(&K3_stream);
cudaStreamCreate(&K4_stream);
// Kernel 1 : A(write)
auto k1 = ctx.task(A.rw());
k1.set_stream(K1_stream);
k1.set_symbol("K1");
k1.start();
cuda_sleep(500, K1_stream);
k1.end();
// Kernel 2 : A(read) B(write)
auto k2 = ctx.task(A.read(), B.write());
k2.set_stream(K2_stream);
k2.set_symbol("K2");
k2.start();
cuda_sleep(500, K2_stream);
k2.end();
// Kernel 3 : A(read) C(write)
auto k3 = ctx.task(A.read(), C.write());
k3.set_stream(K3_stream);
k3.set_symbol("K3");
k3.start();
cuda_sleep(500, K3_stream);
k3.end();
// Kernel 4 : B(read) C(read) D(write)
auto k4 = ctx.task(B.read(), C.read(), D.write());
k4.set_stream(K4_stream);
k4.set_symbol("K4");
k4.start();
cuda_sleep(500, K4_stream);
k4.end();
ctx.finalize();
}

View File

@@ -1,60 +0,0 @@
//===----------------------------------------------------------------------===//
//
// Part of CUDASTF in CUDA C++ Core Libraries,
// under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.
//
//===----------------------------------------------------------------------===//
//! \file
//!
//! \brief Test ctx.wait() on a token: a blocking, value-less synchronization
#include <cuda/experimental/stf.cuh>
#include <type_traits>
using namespace cuda::experimental::stf;
__global__ void set_value(int* p, int v)
{
*p = v;
}
template <typename context_t>
void run()
{
context_t ctx;
// Externally owned buffer: STF only schedules around it, it never owns it.
int* d_val = nullptr;
cuda_safe_call(cudaMalloc(&d_val, sizeof(int)));
auto tok = ctx.token();
ctx.task(tok.write())->*[=](cudaStream_t s) {
set_value<<<1, 1, 0, s>>>(d_val, 42);
};
// wait(token) has no value to materialize: it must return void and only
// block the host until the token's producing work has completed.
static_assert(::std::is_void_v<decltype(ctx.wait(tok))>, "wait(token) must return void");
ctx.wait(tok);
int h_val = 0;
cuda_safe_call(cudaMemcpy(&h_val, d_val, sizeof(int), cudaMemcpyDeviceToHost));
_CCCL_ASSERT(h_val == 42, "wait(token) did not synchronize the producing task");
ctx.finalize();
cuda_safe_call(cudaFree(d_val));
}
int main()
{
run<stream_ctx>();
run<graph_ctx>();
run<context>();
run<stackable_ctx>();
}