chore: import upstream snapshot with attribution
This commit is contained in:
@@ -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);
|
||||
}
|
||||
Reference in New Issue
Block a user