diff --git a/CMakeLists.txt b/CMakeLists.txt index 1b29c5b..aaab181 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -130,6 +130,30 @@ link_directories("${PADDLE_DIR}/libs") set(BIN_PREFIX "paddle_") +# Detect CUDA toolkit to enable PADDLE_WITH_CUDA for Paddle tests that need +# CUDA-specific Paddle compat headers (e.g. CUDAGeneratorImpl). +find_package(CUDAToolkit QUIET) +if(CUDAToolkit_FOUND) + message(STATUS "CUDAToolkit found: ${CUDAToolkit_INCLUDE_DIRS}") + # CUDA 13+ ships Thrust inside cccl/; add it so resolves. + set(_paddle_cuda_incs "${CUDAToolkit_INCLUDE_DIRS}" + "${CUDAToolkit_INCLUDE_DIRS}/cccl") + set(_paddle_cuda_defs "PADDLE_WITH_CUDA") + # CUDA runtime is needed by Paddle GPU compat headers. + list(APPEND PADDLE_LIBRARIES CUDA::cudart) +else() + set(_paddle_cuda_incs "") + set(_paddle_cuda_defs "") +endif() + create_paddle_tests( - "${BIN_PREFIX}" "${TEST_SRC_FILES}" "${PADDLE_TARGET_FOLDER}" - "${PADDLE_LIBRARIES}" "${PADDLE_INCLUDE_DIR}" 1) + "${BIN_PREFIX}" + "${TEST_SRC_FILES}" + "${PADDLE_TARGET_FOLDER}" + "${PADDLE_LIBRARIES}" + "${PADDLE_INCLUDE_DIR}" + 1 + EXTRA_DEFS + ${_paddle_cuda_defs} + EXTRA_INCS + ${_paddle_cuda_incs}) diff --git a/cmake/build.cmake b/cmake/build.cmake index 2bcaf4e..925c078 100644 --- a/cmake/build.cmake +++ b/cmake/build.cmake @@ -6,6 +6,10 @@ function( DEPS_LIBRARIES INCLUDE_DIR USE_PADDLE_API) + # Optional named-keyword args (parsed after positional arg USE_PADDLE_API): + # EXTRA_DEFS -- extra compile definitions (e.g. PADDLE_WITH_CUDA) EXTRA_INCS + # -- extra include directories + cmake_parse_arguments(PARSE_ARGV 6 _CPT "" "" "EXTRA_DEFS;EXTRA_INCS") foreach(_test_file ${TEST_SRC_FILES}) get_filename_component(_file_name ${_test_file} NAME_WE) @@ -23,6 +27,12 @@ function( message(STATUS "include dir: ${INCLUDE_DIR}") target_compile_definitions(${_test_name} PRIVATE USE_PADDLE_API=${USE_PADDLE_API}) + if(_CPT_EXTRA_DEFS) + target_compile_definitions(${_test_name} PRIVATE ${_CPT_EXTRA_DEFS}) + endif() + if(_CPT_EXTRA_INCS) + target_include_directories(${_test_name} PRIVATE ${_CPT_EXTRA_INCS}) + endif() message(STATUS "USE_PADDLE_API: ${USE_PADDLE_API}") add_test(NAME ${_test_name} COMMAND ${_test_name}) set_tests_properties(${_test_name} PROPERTIES TIMEOUT 5) diff --git a/test/GeneratorTest.cpp b/test/GeneratorTest.cpp new file mode 100644 index 0000000..f3dc5ca --- /dev/null +++ b/test/GeneratorTest.cpp @@ -0,0 +1,279 @@ +// Tests for: +// at::Generator +// at::cuda::detail::getDefaultCUDAGenerator +// at::get_generator_or_default +// at::CUDAGeneratorImpl + +#include +#include +#include + +#include +#include + +#include "../src/file_manager.h" + +extern paddle_api_test::ThreadSafeParam g_custom_param; + +namespace at { +namespace test { + +using paddle_api_test::FileManerger; +using paddle_api_test::ThreadSafeParam; + +class GeneratorTest : public ::testing::Test { + protected: + void SetUp() override { + test_gen_ = at::cuda::detail::createCUDAGenerator(0); + test_gen_.set_current_seed(42); + } + at::Generator test_gen_; +}; + +// ============================================================ +// at::Generator +// ============================================================ + +// 默认构造函数产生未定义(impl 为 nullptr)的 Generator +TEST_F(GeneratorTest, DefaultConstructorUndefined) { + auto file_name = g_custom_param.get(); + FileManerger file(file_name); + file.createFile(); + file << "DefaultConstructorUndefined "; + at::Generator gen; + file << std::to_string(gen.defined()) << " "; + file << "\n"; + file.saveFile(); +} + +// set_current_seed / current_seed 往返测试 +TEST_F(GeneratorTest, SetAndGetSeed) { + auto file_name = g_custom_param.get(); + FileManerger file(file_name); + file.openAppend(); + file << "SetAndGetSeed "; + test_gen_.set_current_seed(12345); + file << std::to_string(test_gen_.current_seed()) << " "; + file << "\n"; + file.saveFile(); +} + +// set_offset / get_offset 往返测试 +TEST_F(GeneratorTest, SetAndGetOffset) { + auto file_name = g_custom_param.get(); + FileManerger file(file_name); + file.openAppend(); + file << "SetAndGetOffset "; + test_gen_.set_offset(100); + file << std::to_string(test_gen_.get_offset()) << " "; + file << "\n"; + file.saveFile(); +} + +// device() 返回 CUDA 设备,设备字符串为 "cuda:0" +TEST_F(GeneratorTest, DeviceIsCUDA) { + auto file_name = g_custom_param.get(); + FileManerger file(file_name); + file.openAppend(); + file << "DeviceIsCUDA "; + file << test_gen_.device().str() << " "; + file << "\n"; + file.saveFile(); +} + +// clone() 复制种子状态,修改克隆体不影响原体 +TEST_F(GeneratorTest, CloneIndependence) { + auto file_name = g_custom_param.get(); + FileManerger file(file_name); + file.openAppend(); + file << "CloneIndependence "; + // SetUp 中已设置 seed = 42 + at::Generator cloned = test_gen_.clone(); + cloned.set_current_seed(999); + // 原体 seed 仍为 42,克隆体 seed 为 999 + file << std::to_string(test_gen_.current_seed()) << " "; + file << std::to_string(cloned.current_seed()) << " "; + file << "\n"; + file.saveFile(); +} + +// operator== 同一底层实现时相等;不同实例不相等 +TEST_F(GeneratorTest, EqualityOperators) { + auto file_name = g_custom_param.get(); + FileManerger file(file_name); + file.openAppend(); + file << "EqualityOperators "; + at::Generator other = at::cuda::detail::createCUDAGenerator(0); + file << std::to_string(test_gen_ == test_gen_) << " "; // 1 + file << std::to_string(test_gen_ != other) << " "; // 1 + file << "\n"; + file.saveFile(); +} + +// ============================================================ +// at::cuda::detail::getDefaultCUDAGenerator +// ============================================================ + +// 返回已定义的 Generator +TEST_F(GeneratorTest, GetDefaultCUDAGeneratorDefined) { + auto file_name = g_custom_param.get(); + FileManerger file(file_name); + file.openAppend(); + file << "GetDefaultCUDAGeneratorDefined "; + const at::Generator& gen = at::cuda::detail::getDefaultCUDAGenerator(0); + file << std::to_string(gen.defined()) << " "; + file << "\n"; + file.saveFile(); +} + +// 返回的 Generator 设备为 cuda:0 +TEST_F(GeneratorTest, GetDefaultCUDAGeneratorDevice) { + auto file_name = g_custom_param.get(); + FileManerger file(file_name); + file.openAppend(); + file << "GetDefaultCUDAGeneratorDevice "; + const at::Generator& gen = at::cuda::detail::getDefaultCUDAGenerator(0); + file << gen.device().str() << " "; + file << "\n"; + file.saveFile(); +} + +// 多次调用返回同一实例(地址相同) +TEST_F(GeneratorTest, GetDefaultCUDAGeneratorSameInstance) { + auto file_name = g_custom_param.get(); + FileManerger file(file_name); + file.openAppend(); + file << "GetDefaultCUDAGeneratorSameInstance "; + const at::Generator& gen1 = at::cuda::detail::getDefaultCUDAGenerator(0); + const at::Generator& gen2 = at::cuda::detail::getDefaultCUDAGenerator(0); + file << std::to_string(&gen1 == &gen2) << " "; + file << "\n"; + file.saveFile(); +} + +// ============================================================ +// at::get_generator_or_default +// ============================================================ + +// 提供有效 Generator 时,返回该 Generator 的 impl +TEST_F(GeneratorTest, GetOrDefaultUsesProvidedGenerator) { + auto file_name = g_custom_param.get(); + FileManerger file(file_name); + file.openAppend(); + file << "GetOrDefaultUsesProvidedGenerator "; + test_gen_.set_current_seed(55555); + std::optional opt_gen = test_gen_; + const at::Generator& default_gen = + at::cuda::detail::getDefaultCUDAGenerator(0); + at::CUDAGeneratorImpl* impl = + at::get_generator_or_default(opt_gen, default_gen); + file << std::to_string(impl->current_seed() == 55555) << " "; + file << "\n"; + file.saveFile(); +} + +// nullopt 时,返回 default_gen 的 impl +TEST_F(GeneratorTest, GetOrDefaultUsesDefaultWhenNullopt) { + auto file_name = g_custom_param.get(); + FileManerger file(file_name); + file.openAppend(); + file << "GetOrDefaultUsesDefaultWhenNullopt "; + std::optional opt_gen = std::nullopt; + at::Generator custom_default = at::cuda::detail::createCUDAGenerator(0); + custom_default.set_current_seed(77777); + at::CUDAGeneratorImpl* impl = + at::get_generator_or_default(opt_gen, + custom_default); + file << std::to_string(impl->current_seed() == 77777) << " "; + file << "\n"; + file.saveFile(); +} + +// ============================================================ +// at::CUDAGeneratorImpl +// ============================================================ + +// 静态方法 device_type() 返回 kCUDA +TEST_F(GeneratorTest, CUDAImplDeviceType) { + auto file_name = g_custom_param.get(); + FileManerger file(file_name); + file.openAppend(); + file << "CUDAImplDeviceType "; + file << std::to_string(at::CUDAGeneratorImpl::device_type() == + c10::DeviceType::CUDA) + << " "; + file << "\n"; + file.saveFile(); +} + +// set_philox_offset_per_thread / philox_offset_per_thread 往返测试 +TEST_F(GeneratorTest, CUDAImplPhiloxOffset) { + auto file_name = g_custom_param.get(); + FileManerger file(file_name); + file.openAppend(); + file << "CUDAImplPhiloxOffset "; + at::CUDAGeneratorImpl* impl = test_gen_.get(); + impl->set_philox_offset_per_thread(256); + file << std::to_string(impl->philox_offset_per_thread()) << " "; + file << "\n"; + file.saveFile(); +} + +// philox_cuda_state(increment) 将 offset 推进 increment +TEST_F(GeneratorTest, CUDAImplPhiloxCudaState) { + auto file_name = g_custom_param.get(); + FileManerger file(file_name); + file.openAppend(); + file << "CUDAImplPhiloxCudaState "; + at::CUDAGeneratorImpl* impl = test_gen_.get(); + impl->set_philox_offset_per_thread(0); + impl->set_current_seed(12345); + impl->philox_cuda_state(4); + // offset 应已推进 4 + file << std::to_string(impl->philox_offset_per_thread()) << " "; + file << "\n"; + file.saveFile(); +} + +// philox_engine_inputs(increment) 返回 (seed, offset_before),并推进 offset +TEST_F(GeneratorTest, CUDAImplPhiloxEngineInputs) { + auto file_name = g_custom_param.get(); + FileManerger file(file_name); + file.openAppend(); + file << "CUDAImplPhiloxEngineInputs "; + at::CUDAGeneratorImpl* impl = test_gen_.get(); + impl->set_current_seed(99999); + impl->set_philox_offset_per_thread(0); + auto inputs = impl->philox_engine_inputs(8); + // inputs.first == seed, inputs.second == offset before call + file << std::to_string(inputs.first) << " "; + file << std::to_string(inputs.second) << " "; + // offset 推进 8 + file << std::to_string(impl->philox_offset_per_thread()) << " "; + file << "\n"; + file.saveFile(); +} + +// clone() 保留 seed 与 offset;修改克隆体不影响原体 +TEST_F(GeneratorTest, CUDAImplClone) { + auto file_name = g_custom_param.get(); + FileManerger file(file_name); + file.openAppend(); + file << "CUDAImplClone "; + at::CUDAGeneratorImpl* impl = test_gen_.get(); + impl->set_current_seed(12345); + impl->set_philox_offset_per_thread(100); + at::Generator cloned_gen = test_gen_.clone(); + at::CUDAGeneratorImpl* cloned_impl = cloned_gen.get(); + // 克隆体拥有相同的 seed 和 offset + file << std::to_string(cloned_impl->current_seed()) << " "; + file << std::to_string(cloned_impl->philox_offset_per_thread()) << " "; + // 修改克隆体后,原体 seed 不变 + cloned_impl->set_current_seed(999); + file << std::to_string(impl->current_seed()) << " "; + file << "\n"; + file.saveFile(); +} + +} // namespace test +} // namespace at