[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:
@@ -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);
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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>();
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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(¤t_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();
|
||||
}
|
||||
}
|
||||
@@ -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(¤t_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(¤t_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, ¤t_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(¤t_ctx));
|
||||
unsigned long long current_id;
|
||||
cuda_safe_call(cuCtxGetId(current_ctx, ¤t_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(¤t_ctx));
|
||||
cuda_safe_call(cuCtxGetId(current_ctx, ¤t_id));
|
||||
EXPECT(current_id == green1_id);
|
||||
}
|
||||
// active1 destroyed, should restore to green context 0
|
||||
|
||||
cuda_safe_call(cuCtxGetCurrent(¤t_ctx));
|
||||
cuda_safe_call(cuCtxGetId(current_ctx, ¤t_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) ^^^
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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>();
|
||||
}
|
||||
Reference in New Issue
Block a user