Files
project_6/cccl_upstream/c2h/include/c2h/checked_allocator.cuh
muh-bot dedf08166a [CCCL] Add missing CCCL components: c2h, nvbench_helper, cmake, cudax, AGENTS.md
Added 863 files from NVIDIA/cccl sparse checkout:
- c2h/ (27 files): Catch2 test helpers — generators, validators, runner
- nvbench_helper/ (10 files): Benchmark harness utilities
- cmake/ (29 files): CMake presets and build helpers
- cudax/ (794 files): Experimental CUDA extensions
- AGENTS.md: NVIDIA's official AI agent instructions for CCCL
- CMakePresets.json: Standardized build configurations
- cccl-version.json: Version tracking

Also added CCCL_ASSET_MAP.md mapping all 4295 CCCL files to
competition value and PRD items.

cccl_upstream now covers 100% of competition-critical assets:
- 27 tuning headers (SM80/90/100 benchmark data)
- 32 dispatch headers (algorithm implementations)
- 60 Thrust examples (correctness verification)
- 217 CUB Catch2 tests (regression matrix)
- 153 CUB benchmarks (parameter space search)
- 18 CUB examples (API verification)
- 27 test helpers + benchmark harness
- 794 cudax experimental extensions
2026-08-06 02:14:18 +00:00

206 lines
5.8 KiB
Plaintext

// SPDX-FileCopyrightText: Copyright (c) 2011-2024, NVIDIA CORPORATION. All rights reserved.
// SPDX-License-Identifier: BSD-3
#pragma once
#include <thrust/device_allocator.h>
#include <thrust/mr/new.h>
#include <thrust/system/cuda/memory.h>
#include <thrust/system/cuda/memory_resource.h>
#include <thrust/system/cuda/pointer.h>
#include <cstdlib>
#include <iostream>
#include <new>
#include <optional>
#include <string>
#include <cuda_runtime_api.h>
namespace c2h
{
namespace detail
{
inline std::optional<std::string> get_env(const char* name)
{
#ifdef _WIN32
char* buf = nullptr;
std::size_t len = 0;
if (_dupenv_s(&buf, &len, name) || !buf)
{
return std::nullopt;
}
std::string val(buf);
free(buf);
return val;
#else
if (const char* v = std::getenv(name))
{
return std::string(v);
}
return std::nullopt;
#endif
}
struct memory_info
{
std::size_t free{};
std::size_t total{};
bool override{false};
};
// If the environment variable C2H_DEVICE_MEMORY_LIMIT is set, the total device memory
// will be limited to this number of bytes.
inline std::size_t get_device_memory_limit()
{
static std::optional<std::string> override_str = get_env("C2H_DEVICE_MEMORY_LIMIT");
static std::size_t result = override_str ? static_cast<std::size_t>(std::atoll(override_str->c_str())) : 0;
return result;
}
inline bool get_debug_checked_allocs()
{
static std::optional<std::string> debug_checked_allocs = get_env("C2H_DEBUG_CHECKED_ALLOC_FAILURES");
static bool result = debug_checked_allocs && (std::atoi(debug_checked_allocs->c_str()) != 0);
return result;
}
inline cudaError_t get_device_memory(memory_info& info)
{
static std::size_t device_memory_limit = get_device_memory_limit();
cudaError_t status = cudaMemGetInfo(&info.free, &info.total);
if (status != cudaSuccess)
{
return status;
}
if (device_memory_limit > 0)
{
info.free = (std::max) (std::size_t{0}, static_cast<std::size_t>(info.free - (info.total - device_memory_limit)));
info.total = device_memory_limit;
info.override = true;
}
return cudaSuccess;
}
inline cudaError_t check_free_device_memory(std::size_t bytes)
{
memory_info info;
cudaError_t status = get_device_memory(info);
if (status != cudaSuccess)
{
return status;
}
// Avoid allocating all available memory:
constexpr std::size_t padding = 16 * 1024 * 1024; // 16 MiB
if (info.free < (bytes + padding))
{
if (get_debug_checked_allocs())
{
const double total_GiB = static_cast<double>(info.total) / (1024 * 1024 * 1024);
const double free_GiB = static_cast<double>(info.free) / (1024 * 1024 * 1024);
const double requested_GiB = static_cast<double>(bytes) / (1024 * 1024 * 1024);
const double padded_GiB = static_cast<double>(bytes + padding) / (1024 * 1024 * 1024);
std::cerr << "Device memory allocation failed due to insufficient free device memory.\n";
if (info.override)
{
std::cerr
<< "Available device memory has been limited (env var C2H_DEVICE_MEMORY_LIMIT=" << get_device_memory_limit()
<< ").\n";
}
std::cerr
<< "Total device mem: " << total_GiB << " GiB\n" //
<< "Free device mem: " << free_GiB << " GiB\n" //
<< "Requested device mem: " << requested_GiB << " GiB\n" //
<< "Padded device mem: " << padded_GiB << " GiB\n";
}
return cudaErrorMemoryAllocation;
}
return cudaSuccess;
}
// Check available memory prior to calling cudaMalloc.
// This avoids hangups and slowdowns from allocating swap / non-device memory
// on some platforms, namely tegra.
inline cudaError_t checked_cuda_malloc(void** ptr, std::size_t bytes)
{
auto status = check_free_device_memory(bytes);
if (status != cudaSuccess)
{
return status;
}
return cudaMalloc(ptr, bytes);
}
} // namespace detail
using checked_cuda_memory_resource = THRUST_NS_QUALIFIER::system::cuda::detail::
cuda_memory_resource<detail::checked_cuda_malloc, cudaFree, THRUST_NS_QUALIFIER::cuda::pointer<void>>;
template <typename T>
class checked_cuda_allocator
: public THRUST_NS_QUALIFIER::mr::
stateless_resource_allocator<T, THRUST_NS_QUALIFIER::device_ptr_memory_resource<checked_cuda_memory_resource>>
{
using base = THRUST_NS_QUALIFIER::mr::
stateless_resource_allocator<T, THRUST_NS_QUALIFIER::device_ptr_memory_resource<checked_cuda_memory_resource>>;
public:
template <typename U>
struct rebind
{
using other = checked_cuda_allocator<U>;
};
checked_cuda_allocator() = default;
_CCCL_HOST_DEVICE checked_cuda_allocator(const checked_cuda_allocator& other)
: base(other)
{}
template <typename U>
_CCCL_HOST_DEVICE checked_cuda_allocator(const checked_cuda_allocator<U>& other)
: base(other)
{}
checked_cuda_allocator& operator=(const checked_cuda_allocator&) = default;
~checked_cuda_allocator() = default;
};
struct checked_host_memory_resource final : public THRUST_NS_QUALIFIER::mr::new_delete_resource_base
{
void* do_allocate(std::size_t bytes, std::size_t alignment = THRUST_MR_DEFAULT_ALIGNMENT) final
{
// Some systems with integrated host/device memory have issues with allocating more memory
// than is available. Check the amount of free memory before attempting to allocate on
// integrated systems.
int device = 0;
CubDebugExit(cudaGetDevice(&device));
cudaDeviceProp prop;
CubDebugExit(cudaGetDeviceProperties(&prop, device));
if (prop.integrated)
{
auto status = detail::check_free_device_memory(bytes + alignment + sizeof(std::size_t));
if (status != cudaSuccess)
{
throw std::bad_alloc{};
}
}
return this->new_delete_resource_base::do_allocate(bytes, alignment);
}
};
template <typename T>
using checked_host_allocator = THRUST_NS_QUALIFIER::mr::stateless_resource_allocator<T, checked_host_memory_resource>;
} // namespace c2h