chore: import upstream snapshot with attribution

This commit is contained in:
wehub-resource-sync
2026-07-13 12:40:42 +08:00
commit e25996e7db
15472 changed files with 3536181 additions and 0 deletions
@@ -0,0 +1,2 @@
add_subdirectory(custom)
add_subdirectory(gpu)
@@ -0,0 +1,3 @@
if(WITH_CUSTOM_DEVICE)
paddle_test(custom_device_test SRCS custom_device_test.cc)
endif()
@@ -0,0 +1,267 @@
// Copyright (c) 2022 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <gtest/gtest.h>
#include <array>
#include <string>
#include "paddle/fluid/framework/tensor_util.h"
#include "paddle/fluid/platform/init.h"
#include "paddle/phi/backends/custom/fake_cpu_device.h"
#include "paddle/phi/backends/device_manager.h"
#include "paddle/phi/common/memory_utils.h"
#include "paddle/phi/core/memory/allocation/allocator_facade.h"
#include "paddle/phi/core/platform/device_context.h"
void RegisterDevice() {
CustomRuntimeParams runtime_params;
runtime_params.size = sizeof(CustomRuntimeParams);
auto device_interface = std::make_unique<C_DeviceInterface>();
runtime_params.interface = device_interface.get();
std::memset(runtime_params.interface, 0, sizeof(C_DeviceInterface));
runtime_params.interface->size = sizeof(C_DeviceInterface);
InitFakeCPUDevice(&runtime_params);
phi::LoadCustomRuntimeLib(
runtime_params, std::move(device_interface), "", nullptr);
std::vector<std::string> passes =
phi::CustomDevicePassManager::Instance()->GetCustomDevicePass();
EXPECT_EQ(passes[0], "fake_cpu_device_pass");
}
void InitDevice() {
RegisterDevice();
EXPECT_GT(static_cast<int>(phi::DeviceManager::GetAllDeviceTypes().size()),
0);
auto place = phi::CustomPlace(DEVICE_TYPE, 0);
auto device = phi::DeviceManager::GetDeviceWithPlace(place);
EXPECT_NE(device, nullptr);
std::vector<phi::Place> places;
auto device_types = phi::DeviceManager::GetAllDeviceTypes();
for (auto dev_type : device_types) {
auto devices = phi::DeviceManager::GetDeviceList(dev_type);
for (auto dev_id : devices) {
places.push_back(phi::PlaceHelper::CreatePlace(dev_type, dev_id));
}
}
EXPECT_GT(static_cast<int>(places.size()), 0);
phi::DeviceContextPool::Init(places);
}
void TestDeviceInterface(const phi::Place& place) {
std::cout << "TestDeviceInterface on " << place << std::endl;
if (phi::is_custom_place(place)) {
auto device = phi::DeviceManager::GetDeviceWithPlace(place);
auto dev_type = phi::PlaceHelper::GetDeviceType(place);
auto p1 =
device->MemoryAllocate(phi::DeviceManager::GetMinChunkSize(place));
EXPECT_NE(p1, nullptr);
phi::DeviceManager::SetDevice(place);
auto dev_id = phi::DeviceManager::GetDevice(dev_type);
EXPECT_EQ(dev_id, place.GetDeviceId());
}
}
void TestTensorMutableData(const phi::Place& place) {
std::cout << "TestTensorInitialization on " << place << std::endl;
phi::DenseTensor src_tensor;
float* p1 = nullptr;
float* p2 = nullptr;
// initialization
p1 = src_tensor.mutable_data<float>(common::make_ddim({1, 2, 3}), place);
auto p1_holder = src_tensor.Holder();
EXPECT_NE(p1, nullptr);
// set src_tensor a new dim with large size
// memory is supposed to be re-allocated
p2 = src_tensor.mutable_data<float>(common::make_ddim({3, 1024}), place);
auto p2_holder = src_tensor.Holder();
EXPECT_NE(p2, nullptr);
EXPECT_NE(p1_holder.get(), p2_holder.get());
// set src_tensor a new dim with same size
// memory block is supposed to be unchanged
p1 = src_tensor.mutable_data<float>(common::make_ddim({2, 2, 3}), place);
EXPECT_EQ(p1, p2);
// set src_tensor a new dim with smaller size
// memory block is supposed to be unchanged
p2 = src_tensor.mutable_data<float>(common::make_ddim({2, 2}), place);
EXPECT_EQ(p1, p2);
}
void TestTensorShareDataWith(const phi::Place& place) {
std::cout << "TestTensorShareDataWith on " << place << std::endl;
phi::DenseTensor src_tensor;
phi::DenseTensor dst_tensor;
src_tensor.mutable_data<int>(common::make_ddim({2, 3, 4}), place);
dst_tensor.ShareDataWith(src_tensor);
ASSERT_EQ(src_tensor.data<int>(), dst_tensor.data<int>());
}
void TestTensorUtils(const phi::Place& place) {
std::cout << "TestTensorUtils on " << place << std::endl;
if (phi::is_custom_place(place) == false) {
return;
}
phi::DenseTensor src_tensor;
phi::DenseTensor gpu_tensor;
phi::DenseTensor dst_tensor;
int* src_ptr =
src_tensor.mutable_data<int>(common::make_ddim({3, 3}), phi::CPUPlace());
std::array<int, 9> arr = {1, 2, 3, 4, 5, 6, 7, 8, 9};
memcpy(src_ptr, arr.data(), 9 * sizeof(int));
// CPU Tensor to GPU Tensor
phi::CustomContext gpu_ctx(place);
paddle::framework::TensorCopy(src_tensor, place, gpu_ctx, &gpu_tensor);
#if 0
// GPU Tensor to CPU Tensor
auto cpu_place = new phi::CPUPlace();
paddle::framework::TensorCopy(gpu_tensor, *cpu_place, gpu_ctx, &dst_tensor);
// Sync before Compare Tensors
gpu_ctx.Wait();
const int* dst_ptr = dst_tensor.data<int>();
EXPECT_NE(src_ptr, dst_ptr);
for (size_t i = 0; i < 9; ++i) {
EXPECT_EQ(src_ptr[i], dst_ptr[i]);
}
// Copy the same tensor
paddle::framework::TensorCopy(gpu_tensor, place, gpu_ctx, &gpu_tensor);
gpu_ctx.Wait();
const int* dst_ptr_tmp = dst_tensor.data<int>();
EXPECT_NE(src_ptr, dst_ptr_tmp);
for (size_t i = 0; i < 9; ++i) {
EXPECT_EQ(src_ptr[i], dst_ptr_tmp[i]);
}
phi::DenseTensor slice_tensor = src_tensor.Slice(1, 2);
// CPU Slice Tensor to GPU Tensor
paddle::framework::TensorCopy(slice_tensor, place, gpu_ctx, &gpu_tensor);
// GPU Tensor to CPU Tensor
paddle::framework::TensorCopy(gpu_tensor, *cpu_place, gpu_ctx, &dst_tensor);
// Sync before Compare Slice Tensors
gpu_ctx.Wait();
const int* slice_ptr = slice_tensor.data<int>();
dst_ptr = dst_tensor.data<int>();
EXPECT_NE(dst_ptr, slice_ptr);
for (size_t i = 0; i < 3; ++i) {
EXPECT_EQ(dst_ptr[i], slice_ptr[i]);
}
EXPECT_TRUE(dst_tensor.layout() == src_tensor.layout());
#endif
}
void TestCustomCCL(const phi::Place& place) {
std::cout << "TestCustomCCL on " << place << std::endl;
if (phi::is_custom_place(place) == false) {
return;
}
std::string dev_type = place.GetDeviceType();
phi::ccl::CCLComm comm;
phi::stream::Stream stream(place, nullptr);
phi::ccl::CCLRootId root_id;
phi::DeviceManager::CCLDestroyComm(dev_type, nullptr);
phi::DeviceManager::CCLGetUniqueId(dev_type, &root_id);
phi::DeviceManager::CCLCommInitRank(dev_type, 0, &root_id, 0, nullptr);
phi::DeviceManager::CCLBroadcast(dev_type,
nullptr,
0,
phi::DataType::FLOAT32,
0,
comm,
stream.raw_stream());
phi::DeviceManager::CCLAllReduce(dev_type,
nullptr,
nullptr,
0,
phi::DataType::FLOAT32,
phi::ccl::CCLReduceOp::SUM,
comm,
stream.raw_stream());
phi::DeviceManager::CCLReduce(dev_type,
nullptr,
nullptr,
0,
phi::DataType::FLOAT32,
phi::ccl::CCLReduceOp::SUM,
0,
comm,
stream.raw_stream());
phi::DeviceManager::CCLAllGather(dev_type,
nullptr,
nullptr,
0,
phi::DataType::FLOAT32,
comm,
stream.raw_stream());
phi::DeviceManager::CCLReduceScatter(dev_type,
nullptr,
nullptr,
0,
phi::DataType::FLOAT32,
phi::ccl::CCLReduceOp::SUM,
comm,
stream.raw_stream());
phi::DeviceManager::CCLGroupStart(dev_type);
phi::DeviceManager::CCLGroupEnd(dev_type);
phi::DeviceManager::CCLSend(dev_type,
nullptr,
0,
phi::DataType::FLOAT32,
0,
comm,
stream.raw_stream());
phi::DeviceManager::CCLRecv(dev_type,
nullptr,
0,
phi::DataType::FLOAT32,
0,
comm,
stream.raw_stream());
}
TEST(CustomDevice, Tensor) {
paddle::framework::InitMemoryMethod();
InitDevice();
auto dev_types = phi::DeviceManager::GetAllDeviceTypes();
for (const auto& dev_type : dev_types) {
std::cout << "Test on " << dev_type << std::endl;
EXPECT_GT(static_cast<int>(phi::DeviceManager::GetDeviceCount(dev_type)),
0);
auto place = phi::PlaceHelper::CreatePlace(dev_type);
TestDeviceInterface(place);
TestTensorMutableData(place);
TestTensorShareDataWith(place);
TestTensorUtils(place);
TestCustomCCL(place);
}
}
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
@@ -0,0 +1,17 @@
if(WITH_GPU)
add_subdirectory(cuda)
nv_test(cuda_helper_test SRCS cuda_helper_test.cu)
nv_test(
cudnn_desc_test
SRCS cudnn_desc_test.cc
DEPS phi common)
elseif(WITH_ROCM)
add_subdirectory(rocm)
hip_test(cuda_helper_test SRCS cuda_helper_test.cu)
hip_test(
cudnn_desc_test
SRCS cudnn_desc_test.cc
DEPS phi common)
endif()
@@ -0,0 +1,4 @@
nv_test(
cudnn_helper_test
SRCS cudnn_helper_test.cc
DEPS phi common)
@@ -0,0 +1,163 @@
/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License. */
#define GOOGLE_GLOG_DLL_DECL
#include <gtest/gtest.h>
#include "paddle/phi/core/platform/device/gpu/gpu_dnn.h"
TEST(CudnnHelper, ScopedTensorDescriptor) {
using phi::DataLayout;
using phi::backends::gpu::ScopedTensorDescriptor;
ScopedTensorDescriptor tensor_desc;
std::vector<int> shape = {2, 4, 6, 6};
auto desc = tensor_desc.descriptor<float>(DataLayout::NCHW, shape);
cudnnDataType_t type;
int nd;
std::vector<int> dims(4);
std::vector<int> strides(4);
phi::dynload::cudnnGetTensorNdDescriptor(
desc, 4, &type, &nd, dims.data(), strides.data());
EXPECT_EQ(nd, 4);
for (size_t i = 0; i < dims.size(); ++i) {
EXPECT_EQ(dims[i], shape[i]);
}
EXPECT_EQ(strides[3], 1);
EXPECT_EQ(strides[2], 6);
EXPECT_EQ(strides[1], 36);
EXPECT_EQ(strides[0], 144);
// test tensor5d: ScopedTensorDescriptor
ScopedTensorDescriptor tensor5d_desc;
std::vector<int> shape_5d = {2, 4, 6, 6, 6};
auto desc_5d = tensor5d_desc.descriptor<float>(DataLayout::NCDHW, shape_5d);
std::vector<int> dims_5d(5);
std::vector<int> strides_5d(5);
phi::dynload::cudnnGetTensorNdDescriptor(
desc_5d, 5, &type, &nd, dims_5d.data(), strides_5d.data());
EXPECT_EQ(nd, 5);
for (size_t i = 0; i < dims_5d.size(); ++i) {
EXPECT_EQ(dims_5d[i], shape_5d[i]);
}
EXPECT_EQ(strides_5d[4], 1);
EXPECT_EQ(strides_5d[3], 6);
EXPECT_EQ(strides_5d[2], 36);
EXPECT_EQ(strides_5d[1], 216);
EXPECT_EQ(strides_5d[0], 864);
}
TEST(CudnnHelper, ScopedFilterDescriptor) {
using phi::DataLayout;
using phi::backends::gpu::GetCudnnTensorFormat;
using phi::backends::gpu::ScopedFilterDescriptor;
ScopedFilterDescriptor filter_desc;
std::vector<int> shape = {2, 3, 3};
auto desc = filter_desc.descriptor<float>(DataLayout::NCHW, shape);
cudnnDataType_t type;
int nd;
cudnnTensorFormat_t format;
std::vector<int> kernel(3);
phi::dynload::cudnnGetFilterNdDescriptor(
desc, 3, &type, &format, &nd, kernel.data());
EXPECT_EQ(GetCudnnTensorFormat(DataLayout::NCHW), format);
EXPECT_EQ(nd, 3);
for (size_t i = 0; i < shape.size(); ++i) {
EXPECT_EQ(kernel[i], shape[i]);
}
ScopedFilterDescriptor filter_desc_4d;
std::vector<int> shape_4d = {2, 3, 3, 3};
auto desc_4d = filter_desc.descriptor<float>(DataLayout::NCDHW, shape_4d);
std::vector<int> kernel_4d(4);
phi::dynload::cudnnGetFilterNdDescriptor(
desc_4d, 4, &type, &format, &nd, kernel_4d.data());
EXPECT_EQ(GetCudnnTensorFormat(DataLayout::NCHW), format);
EXPECT_EQ(nd, 4);
for (size_t i = 0; i < shape_4d.size(); ++i) {
EXPECT_EQ(kernel_4d[i], shape_4d[i]);
}
}
TEST(CudnnHelper, ScopedConvolutionDescriptor) {
using phi::backends::gpu::ScopedConvolutionDescriptor;
ScopedConvolutionDescriptor conv_desc;
std::vector<int> src_pads = {2, 2, 2};
std::vector<int> src_strides = {1, 1, 1};
std::vector<int> src_dilations = {1, 1, 1};
auto desc = conv_desc.descriptor<float>(src_pads, src_strides, src_dilations);
cudnnDataType_t type;
cudnnConvolutionMode_t mode;
int nd;
std::vector<int> pads(3);
std::vector<int> strides(3);
std::vector<int> dilations(3);
phi::dynload::cudnnGetConvolutionNdDescriptor(desc,
3,
&nd,
pads.data(),
strides.data(),
dilations.data(),
&mode,
&type);
EXPECT_EQ(nd, 3);
for (size_t i = 0; i < src_pads.size(); ++i) {
EXPECT_EQ(pads[i], src_pads[i]);
EXPECT_EQ(strides[i], src_strides[i]);
EXPECT_EQ(dilations[i], src_dilations[i]);
}
EXPECT_EQ(mode, CUDNN_CROSS_CORRELATION);
}
TEST(CudnnHelper, ScopedPoolingDescriptor) {
using phi::backends::gpu::PoolingMode;
using phi::backends::gpu::ScopedPoolingDescriptor;
ScopedPoolingDescriptor pool_desc;
std::vector<int> src_kernel = {2, 2, 5};
std::vector<int> src_pads = {1, 1, 2};
std::vector<int> src_strides = {2, 2, 3};
auto desc = pool_desc.descriptor(
PoolingMode::kMaximum, src_kernel, src_pads, src_strides);
cudnnPoolingMode_t mode;
cudnnNanPropagation_t nan_t = CUDNN_PROPAGATE_NAN;
int nd;
std::vector<int> kernel(3);
std::vector<int> pads(3);
std::vector<int> strides(3);
phi::dynload::cudnnGetPoolingNdDescriptor(
desc, 3, &mode, &nan_t, &nd, kernel.data(), pads.data(), strides.data());
EXPECT_EQ(nd, 3);
for (size_t i = 0; i < src_pads.size(); ++i) {
EXPECT_EQ(kernel[i], src_kernel[i]);
EXPECT_EQ(pads[i], src_pads[i]);
EXPECT_EQ(strides[i], src_strides[i]);
}
EXPECT_EQ(mode, CUDNN_POOLING_MAX);
}
@@ -0,0 +1,310 @@
// Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <gtest/gtest.h>
#include <algorithm>
#include <iostream>
#ifdef _WIN32
#include <numeric>
#endif
#include <random>
#include "paddle/phi/backends/gpu/gpu_device_function.h"
#include "paddle/phi/backends/gpu/gpu_primitives.h"
#include "paddle/phi/common/float16.h"
#include "paddle/phi/core/platform/device/gpu/gpu_helper.h"
using phi::PADDLE_CUDA_NUM_THREADS;
using phi::dtype::float16;
template <typename T>
__global__ void AddKernel(const T* data_a, T* data_b, size_t num) {
CUDA_KERNEL_LOOP(i, num) { phi::CudaAtomicAdd(&data_b[i], data_a[i]); }
}
template <typename T>
struct AddFunctor {
T operator()(const T& a, const T& b) { return a + b; }
};
template <typename T>
void TestCase(size_t num) {
T *in1, *in2, *out;
T *d_in1, *d_in2;
size_t size = sizeof(T) * num;
#ifdef PADDLE_WITH_HIP
hipMalloc(reinterpret_cast<void**>(&d_in1), size);
hipMalloc(reinterpret_cast<void**>(&d_in2), size);
#else
cudaMalloc(reinterpret_cast<void**>(&d_in1), size);
cudaMalloc(reinterpret_cast<void**>(&d_in2), size);
#endif
in1 = reinterpret_cast<T*>(malloc(size));
in2 = reinterpret_cast<T*>(malloc(size));
out = reinterpret_cast<T*>(malloc(size));
std::minstd_rand engine;
std::uniform_real_distribution<double> dist(0.0, 1.0);
for (size_t i = 0; i < num; ++i) {
in1[i] = static_cast<T>(dist(engine));
in2[i] = static_cast<T>(dist(engine));
}
#ifdef PADDLE_WITH_HIP
hipMemcpy(d_in1, in1, size, hipMemcpyHostToDevice);
hipMemcpy(d_in2, in2, size, hipMemcpyHostToDevice);
hipLaunchKernelGGL(HIP_KERNEL_NAME(AddKernel<T>),
dim3(1),
dim3(PADDLE_CUDA_NUM_THREADS),
0,
0,
d_in1,
d_in2,
num);
hipDeviceSynchronize();
hipMemcpy(out, d_in2, size, hipMemcpyDeviceToHost);
hipDeviceSynchronize();
#else
cudaMemcpy(d_in1, in1, size, cudaMemcpyHostToDevice);
cudaMemcpy(d_in2, in2, size, cudaMemcpyHostToDevice);
AddKernel<T><<<1, PADDLE_CUDA_NUM_THREADS>>>(d_in1, d_in2, num);
cudaDeviceSynchronize();
cudaMemcpy(out, d_in2, size, cudaMemcpyDeviceToHost);
cudaDeviceSynchronize();
#endif
for (size_t i = 0; i < num; ++i) {
// NOTE(dzhwinter): the float16 add has small underflow/overflow
// so we use EXPECT_NEAR to check the result.
EXPECT_NEAR(static_cast<float>(out[i]),
static_cast<float>(AddFunctor<T>()(in1[i], in2[i])),
0.001);
}
free(in1);
free(in2);
free(out);
#ifdef PADDLE_WITH_HIP
hipFree(d_in1);
hipFree(d_in2);
#else
cudaFree(d_in1);
cudaFree(d_in2);
#endif
}
// cuda primitives
TEST(CudaAtomic, Add) {
TestCase<float>(static_cast<size_t>(10));
TestCase<float>(static_cast<size_t>(1024 * 1024));
TestCase<double>(static_cast<size_t>(10));
TestCase<double>(static_cast<size_t>(1024 * 1024));
}
TEST(CudaAtomic, float16) {
TestCase<float16>(static_cast<size_t>(1));
TestCase<float16>(static_cast<size_t>(2));
TestCase<float16>(static_cast<size_t>(3));
TestCase<float16>(static_cast<size_t>(10));
TestCase<float16>(static_cast<size_t>(1024 * 1024));
}
// unalignment of uint8
void TestUnalign(size_t num, const int shift_bit) {
ASSERT_EQ(num % 2, 0);
float16 *in1, *in2, *out;
float16 *d_in1, *d_in2;
size_t size = sizeof(uint8_t) * (num + shift_bit);
size_t array_size = sizeof(float16) * (num / 2);
#ifdef PADDLE_WITH_HIP
hipMalloc(reinterpret_cast<void**>(&d_in1), size);
hipMalloc(reinterpret_cast<void**>(&d_in2), size);
#else
cudaMalloc(reinterpret_cast<void**>(&d_in1), size);
cudaMalloc(reinterpret_cast<void**>(&d_in2), size);
#endif
in1 = reinterpret_cast<float16*>(malloc(size));
in2 = reinterpret_cast<float16*>(malloc(size));
out = reinterpret_cast<float16*>(malloc(size));
// right shift 1, mimic the unalignment of address
float16* r_in1 =
reinterpret_cast<float16*>(reinterpret_cast<uint8_t*>(in1) + shift_bit);
float16* r_in2 =
reinterpret_cast<float16*>(reinterpret_cast<uint8_t*>(in2) + shift_bit);
std::minstd_rand engine;
std::uniform_real_distribution<double> dist(0.0, 1.0);
for (size_t i = 0; i < num / 2; ++i) {
r_in1[i] = static_cast<float16>(dist(engine));
r_in2[i] = static_cast<float16>(dist(engine));
}
#ifdef PADDLE_WITH_HIP
hipMemcpy(d_in1, r_in1, array_size, hipMemcpyHostToDevice);
hipMemcpy(d_in2, r_in2, array_size, hipMemcpyHostToDevice);
hipLaunchKernelGGL(HIP_KERNEL_NAME(AddKernel<float16>),
dim3(1),
dim3(PADDLE_CUDA_NUM_THREADS),
0,
0,
d_in1,
d_in2,
num / 2);
hipDeviceSynchronize();
hipMemcpy(out, d_in2, array_size, hipMemcpyDeviceToHost);
hipDeviceSynchronize();
#else
cudaMemcpy(d_in1, r_in1, array_size, cudaMemcpyHostToDevice);
cudaMemcpy(d_in2, r_in2, array_size, cudaMemcpyHostToDevice);
AddKernel<float16><<<1, PADDLE_CUDA_NUM_THREADS>>>(d_in1, d_in2, num / 2);
cudaDeviceSynchronize();
cudaMemcpy(out, d_in2, array_size, cudaMemcpyDeviceToHost);
cudaDeviceSynchronize();
#endif
for (size_t i = 0; i < num / 2; ++i) {
// NOTE(dzhwinter): the float16 add has small truncate error.
// so we use EXPECT_NEAR to check the result.
EXPECT_NEAR(static_cast<float>(out[i]),
static_cast<float>(AddFunctor<float16>()(r_in1[i], r_in2[i])),
0.001);
}
free(in1);
free(in2);
free(out);
#ifdef PADDLE_WITH_HIP
hipFree(d_in1);
hipFree(d_in2);
#else
cudaFree(d_in1);
cudaFree(d_in2);
#endif
}
TEST(CudaAtomic, float16Unalign) {
// same with float16 testcase
TestUnalign(static_cast<size_t>(2), /*shift_bit*/ 2);
TestUnalign(static_cast<size_t>(1024), /*shift_bit*/ 2);
TestUnalign(static_cast<size_t>(1024 * 1024), /*shift_bit*/ 2);
// shift the address.
TestUnalign(static_cast<size_t>(2), /*shift_bit*/ 1);
TestUnalign(static_cast<size_t>(1024), /*shift_bit*/ 1);
TestUnalign(static_cast<size_t>(1024 * 1024), /*shift_bit*/ 1);
TestUnalign(static_cast<size_t>(2), /*shift_bit*/ 3);
TestUnalign(static_cast<size_t>(1024), /*shift_bit*/ 3);
TestUnalign(static_cast<size_t>(1024 * 1024), /*shift_bit*/ 3);
}
// https://devblogs.nvidia.com/faster-parallel-reductions-kepler/
template <typename T>
static __forceinline__ __device__ T WarpReduceSum(T val) {
unsigned mask = 0u;
CREATE_SHFL_MASK(mask, true);
for (int offset = warpSize / 2; offset > 0; offset /= 2) {
val += phi::backends::gpu::CudaShuffleDownSync(mask, val, offset);
}
return val;
}
template <typename T>
__forceinline__ __device__ T BlockReduce(T val) {
static __shared__ T shared[32]; // Shared mem for 32 partial sums
int lane = threadIdx.x % warpSize;
int wid = threadIdx.x / warpSize;
val = WarpReduceSum(val); // Each warp performs partial reduction
if (lane == 0) shared[wid] = val; // Write reduced value to shared memory
__syncthreads(); // Wait for all partial reductions
// read from shared memory only if that warp existed
val =
(threadIdx.x < blockDim.x / warpSize) ? shared[lane] : static_cast<T>(0);
if (wid == 0) val = WarpReduceSum(val); // Final reduce within first warp
return val;
}
template <typename T>
__global__ void DeviceReduceSum(T* in, T* out, size_t N) {
T sum(0);
CUDA_KERNEL_LOOP(i, N) { sum += in[i]; }
sum = BlockReduce<T>(sum);
__syncthreads();
if (threadIdx.x == 0) out[blockIdx.x] = sum;
}
template <typename T>
void TestReduce(size_t num, float atol = 0.01) {
T* in1;
T *d_in1, *d_in2;
size_t size = sizeof(T) * num;
#ifdef PADDLE_WITH_HIP
hipMalloc(reinterpret_cast<void**>(&d_in1), size);
hipMalloc(reinterpret_cast<void**>(&d_in2), sizeof(T));
#else
cudaMalloc(reinterpret_cast<void**>(&d_in1), size);
cudaMalloc(reinterpret_cast<void**>(&d_in2), sizeof(T));
#endif
in1 = reinterpret_cast<T*>(malloc(size));
std::minstd_rand engine;
std::uniform_real_distribution<double> dist(0.0, 1.0);
for (size_t i = 0; i < num; ++i) {
in1[i] = static_cast<T>(dist(engine));
}
auto out = std::accumulate(in1, in1 + num, static_cast<T>(0));
#ifdef PADDLE_WITH_HIP
hipMemcpy(d_in1, in1, size, hipMemcpyHostToDevice);
hipDeviceSynchronize();
hipLaunchKernelGGL(HIP_KERNEL_NAME(DeviceReduceSum<T>),
dim3(1),
dim3(PADDLE_CUDA_NUM_THREADS),
0,
0,
d_in1,
d_in2,
num);
hipMemcpy(in1, d_in2, sizeof(T), hipMemcpyDeviceToHost);
hipDeviceSynchronize();
#else
cudaMemcpy(d_in1, in1, size, cudaMemcpyHostToDevice);
cudaDeviceSynchronize();
DeviceReduceSum<T><<<1, PADDLE_CUDA_NUM_THREADS>>>(d_in1, d_in2, num);
cudaMemcpy(in1, d_in2, sizeof(T), cudaMemcpyDeviceToHost);
cudaDeviceSynchronize();
#endif
// NOTE(dzhwinter): the float16 add has small underflow/overflow
// so we use EXPECT_NEAR to check the result.
EXPECT_NEAR(static_cast<float>(in1[0]), static_cast<float>(out), atol);
free(in1);
#ifdef PADDLE_WITH_HIP
hipFree(d_in1);
hipFree(d_in2);
#else
cudaFree(d_in1);
cudaFree(d_in2);
#endif
}
TEST(CudaShuffleSync, float16) {
TestReduce<float>(10);
TestReduce<float>(1000);
// float16 will overflow or accumulate truncate errors in big size.
TestReduce<float16>(10);
TestReduce<float16>(100, /*atol error*/ 1.0);
}
@@ -0,0 +1,44 @@
// Copyright (c) 2019 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <gtest/gtest.h>
#include "paddle/phi/core/platform/device/gpu/gpu_dnn.h"
namespace paddle {
namespace platform {
TEST(TensorDescriptor, Empty) {
phi::backends::gpu::ActivationDescriptor a;
phi::backends::gpu::TensorDescriptor t;
phi::backends::gpu::TensorDescriptor t1;
phi::backends::gpu::TensorDescriptor *t11 =
new phi::backends::gpu::TensorDescriptor();
delete t11;
std::unique_ptr<phi::backends::gpu::TensorDescriptor> tt(
new phi::backends::gpu::TensorDescriptor());
}
TEST(TensorDescriptor, Normal) {
phi::DenseTensor tt;
tt.Resize({2, 3, 4});
tt.mutable_data<float>(phi::CPUPlace());
phi::backends::gpu::TensorDescriptor desc;
desc.set(tt);
EXPECT_TRUE(desc.desc() != nullptr);
}
} // namespace platform
} // namespace paddle
@@ -0,0 +1,4 @@
hip_test(
miopen_helper_test
SRCS miopen_helper_test.cc
DEPS phi common)
@@ -0,0 +1,92 @@
/* Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License. */
#define GOOGLE_GLOG_DLL_DECL
#include <gtest/gtest.h>
#include "paddle/phi/core/platform/device/gpu/gpu_dnn.h"
TEST(MIOpenHelper, ScopedTensorDescriptor) {
using phi::DataLayout;
using phi::backends::gpu::ScopedTensorDescriptor;
ScopedTensorDescriptor tensor_desc;
std::vector<int> shape = {2, 4, 6, 6};
auto desc = tensor_desc.descriptor<float>(DataLayout::NCHW, shape);
miopenDataType_t type;
int nd;
std::vector<int> dims(4);
std::vector<int> strides(4);
phi::dynload::miopenGetTensorDescriptor(
desc, &type, dims.data(), strides.data());
phi::dynload::miopenGetTensorDescriptorSize(desc, &nd);
EXPECT_EQ(nd, 4);
for (size_t i = 0; i < dims.size(); ++i) {
EXPECT_EQ(dims[i], shape[i]);
}
EXPECT_EQ(strides[3], 1);
EXPECT_EQ(strides[2], 6);
EXPECT_EQ(strides[1], 36);
EXPECT_EQ(strides[0], 144);
// test tensor5d: ScopedTensorDescriptor
ScopedTensorDescriptor tensor5d_desc;
std::vector<int> shape_5d = {2, 4, 6, 6, 6};
auto desc_5d = tensor5d_desc.descriptor<float>(DataLayout::NCDHW, shape_5d);
std::vector<int> dims_5d(5);
std::vector<int> strides_5d(5);
phi::dynload::miopenGetTensorDescriptor(
desc_5d, &type, dims_5d.data(), strides_5d.data());
phi::dynload::miopenGetTensorDescriptorSize(desc_5d, &nd);
EXPECT_EQ(nd, 5);
for (size_t i = 0; i < dims_5d.size(); ++i) {
EXPECT_EQ(dims_5d[i], shape_5d[i]);
}
EXPECT_EQ(strides_5d[4], 1);
EXPECT_EQ(strides_5d[3], 6);
EXPECT_EQ(strides_5d[2], 36);
EXPECT_EQ(strides_5d[1], 216);
EXPECT_EQ(strides_5d[0], 864);
}
TEST(MIOpenHelper, ScopedConvolutionDescriptor) {
using phi::backends::gpu::ScopedConvolutionDescriptor;
ScopedConvolutionDescriptor conv_desc;
std::vector<int> src_pads = {2, 2, 2};
std::vector<int> src_strides = {1, 1, 1};
std::vector<int> src_dilations = {1, 1, 1};
auto desc = conv_desc.descriptor<float>(src_pads, src_strides, src_dilations);
miopenConvolutionMode_t mode;
int nd;
std::vector<int> pads(3);
std::vector<int> strides(3);
std::vector<int> dilations(3);
phi::dynload::miopenGetConvolutionNdDescriptor(
desc, 3, &nd, pads.data(), strides.data(), dilations.data(), &mode);
EXPECT_EQ(nd, 3);
for (size_t i = 0; i < src_pads.size(); ++i) {
EXPECT_EQ(pads[i], src_pads[i]);
EXPECT_EQ(strides[i], src_strides[i]);
EXPECT_EQ(dilations[i], src_dilations[i]);
}
EXPECT_EQ(mode, miopenConvolution);
}