11#include < ATen/ATen.h>
22#include < ATen/cuda/CUDAContextLight.h>
33
4- // at::cuda::blas::gemm<T> is compiled with hidden symbol visibility inside
5- // libtorch and cannot be resolved from external code. It IS exported by the
6- // Paddle compat library, so we only include CUDABlas.h and instantiate the
7- // gemm tests when linking against Paddle (USE_PADDLE_API=1).
4+ // 【差异点1】at::cuda::blas::gemm<T> 符号可见性差异
5+ // PyTorch(libtorch)将 at::cuda::blas::gemm<T> 编译为 hidden visibility
6+ // (动态符号表中 nm -D 结果为小写 't'),外部代码无法链接该符号。
7+ // Paddle compat 库中该符号为公开导出(大写 'T'),可正常从外部调用。
8+ // 因此仅在 Paddle 构建(USE_PADDLE_API=1)时包含头文件并实例化 gemm 测试;
9+ // Torch 构建输出 "not_exported" 占位,保持两端输出行对齐。
810#if USE_PADDLE_API
911#include < ATen/cuda/CUDABlas.h>
1012#endif
@@ -50,10 +52,13 @@ static void write_gemm_result_to_file(FileManerger* file,
5052// at::cuda::getCurrentCUDABlasHandle tests
5153// ============================================================
5254
53- // Verify that getCurrentCUDABlasHandle returns a non-null handle.
54- // Uses `auto` to stay type-agnostic between PyTorch (cublasHandle_t) and
55- // Paddle compat (at::cuda::CUDAContextBlasHandle which aliases the same type
56- // in CUDA builds but differs in HIP/ROCm builds).
55+ // 验证 getCurrentCUDABlasHandle 返回非空 handle。
56+ // 使用 `auto` 接收返回值以屏蔽类型差异:
57+ // 【差异点2】返回类型差异
58+ // PyTorch:直接返回 cublasHandle_t
59+ // Paddle compat:返回 at::cuda::CUDAContextBlasHandle,
60+ // 在 CUDA 构建中该类型 typedef 为 cublasHandle_t,
61+ // 在 HIP/ROCm 构建中则为 phi::blasHandle_t(不同类型)。
5762TEST_F (CUDABlasTest, HandleNonNull) {
5863 auto file_name = g_custom_param.get ();
5964 FileManerger file (file_name);
@@ -66,21 +71,22 @@ TEST_F(CUDABlasTest, HandleNonNull) {
6671 return ;
6772 }
6873#if USE_PADDLE_API
69- // Paddle's getCurrentCUDABlasHandle() internally calls
70- // phi::DeviceContextPool::Instance().Get(GPUPlace()), which requires
71- // prior framework initialization via paddle::framework::InitDevices().
72- // In a standalone C++ test binary this pool is not initialized, so
73- // Paddle throws a PreconditionNotMet exception. Record the difference.
74+ // 【差异点3】Paddle 的 getCurrentCUDABlasHandle() 实现依赖框架全局状态
75+ // Paddle 内部调用 phi::DeviceContextPool::Instance().Get(GPUPlace()),
76+ // 该调用要求事先通过 paddle::framework::InitDevices() 初始化
77+ // DeviceContextPool。 在独立 C++ 测试二进制中框架未初始化,Paddle 抛出
78+ // PreconditionNotMet 异常。 PyTorch 无此约束,只需 CUDA
79+ // 分配器初始化即可正常返回 handle。 输出 "exception_needs_pool_init"
80+ // 记录该行为差异。
7481 try {
7582 auto handle = at::cuda::getCurrentCUDABlasHandle ();
7683 file << std::to_string (handle != nullptr ? 1 : 0 ) << " " ;
7784 } catch (const std::exception&) {
7885 file << " exception_needs_pool_init " ;
7986 }
8087#else
81- // PyTorch requires the CUDA caching allocator to be initialized before
82- // the first cuBLAS handle creation. A dummy tensor allocation on the
83- // GPU is the canonical way to trigger that initialisation.
88+ // PyTorch 在首次创建 cuBLAS handle 前要求 CUDA 缓存分配器已初始化。
89+ // 分配一个 dummy GPU tensor 是触发该初始化的标准方式。
8490 {
8591 auto _init = at::zeros ({1 }, at::kFloat ).cuda ();
8692 (void )_init;
@@ -110,7 +116,8 @@ TEST_F(CUDABlasTest, HandleConsistency) {
110116 return ;
111117 }
112118#if USE_PADDLE_API
113- // Same pool-init constraint as HandleNonNull.
119+ // 与 HandleNonNull 相同的 pool-init 限制(见差异点3),
120+ // Paddle 在 DeviceContextPool 未初始化时抛出异常。
114121 try {
115122 auto handle1 = at::cuda::getCurrentCUDABlasHandle ();
116123 auto handle2 = at::cuda::getCurrentCUDABlasHandle ();
@@ -137,9 +144,14 @@ TEST_F(CUDABlasTest, HandleConsistency) {
137144
138145#if USE_PADDLE_API
139146
140- // Helper: create a 1-D float32 tensor from an initializer list on CPU and
141- // move it to the current CUDA device. at::tensor(initializer_list<float>)
142- // without explicit TensorOptions is not in Paddle's ATen/Utils.h overload set.
147+ // 【差异点4】at::tensor(initializer_list<T>) 无 TensorOptions 重载缺失
148+ // PyTorch 的 ATen/Utils.h 提供 at::tensor(std::initializer_list<float>)
149+ // 直接推断类型的重载; Paddle compat 的 ATen/Utils.h 仅提供
150+ // at::tensor(ArrayRef<T>, TensorOptions) 形式, 不支持不带 TensorOptions 的
151+ // initializer_list 重载。 因此此处使用 cpu_fill_f32 辅助函数代替
152+ // at::tensor({...}) 构造张量。
153+
154+ // 辅助函数:在 CPU 上从 initializer_list 填充 float32 一维张量后移至 GPU。
143155static at::Tensor cpu_fill_f32 (std::initializer_list<float > vals) {
144156 auto t = at::zeros ({(int64_t )vals.size ()}, at::kFloat );
145157 float * p = t.data_ptr <float >();
@@ -535,8 +547,10 @@ TEST_F(CUDABlasTest, GemmLargeMatrix) {
535547
536548#else // !USE_PADDLE_API
537549
538- // at::cuda::blas::gemm<T> is not exported from libtorch (hidden symbol
539- // visibility). Emit "not_exported" so both output files stay line-aligned.
550+ // 【差异点1 对应桩代码】
551+ // at::cuda::blas::gemm<T> 在 libtorch 中为 hidden visibility,外部无法链接。
552+ // 输出 "not_exported" 占位,保持 torch/paddle 两端输出文件行数对齐,
553+ // 以便比对脚本(result_cmp.sh)能逐行对比其余可比较的测试结果。
540554#define CUDABLAS_GEMM_STUB (name ) \
541555 TEST_F (CUDABlasTest, name) { \
542556 auto file_name = g_custom_param.get (); \
0 commit comments