我正在学习cuda并设法在其中获得一个2D数组,并返回一个带有一个小问题的2D双精度数组。现在,例如,我想将所有值设置为所有值的两倍,但我似乎无法超越第一行。我似乎无法正确循环。我怀疑是块/线程数还是代码本身。这是我完整的代码:
#include <stdio.h>
#include <vector>
using namespace std;
#define THETA 10
// Error checking.
//
#define gpuErrorCheck(ans) { gpuAssert((ans), __FILE__, __LINE__); }
inline void gpuAssert(cudaError_t code, char *file, int line, bool abort=true)
{
if (code != cudaSuccess)
{
fprintf(stderr, "GPUassert: %s %s %d\n", cudaGetErrorString(code), file, line);
if (abort) exit(code);
}
}
// Pass 2-dim array to GPU and change it there.
//
__global__
void addArrays(double *twoDimArray, size_t pitch)
{
printf("\n\nOn GPU array : thread : %d\n", threadIdx.x);
int tidx = blockIdx.x * blockDim.x + threadIdx.x; //// tidx = Columns in CUDA
int tidy = blockIdx.y * blockDim.y + threadIdx.y; //// tidy = Rows In cuda
if ((tidx < THETA) && (tidy < THETA))
{
double tempval = 0;
for(int i=0; i < THETA ; i++)
{
tempval = 250;
}
twoDimArray[tidy * THETA + tidx]=tempval;
}
}
int main()
{
//
// 2-Dimensional Array
//
printf("\n*******************\n2-DIMENSIONAL ARRAY\n*******************\n\n");
// Create 2-dim array on the CPU.
//
double arrayOnCpu[THETA][THETA];
double arrayOnCpu2[THETA][THETA];
// Initialise the vector of vector on the CPU.
//
for (int i = 0; i < THETA; i++) // Aantal buitenste vectoren.
{
for (int j = 0; j < THETA; j++) // Aantal binnenste elementen.
{
arrayOnCpu2[i][j] = j;
}
}
// Print the vector of vectors.
//
for (int i = 0; i < THETA; i++)
{
for (int j = 0; j < THETA; j++)
{
printf("%2.2f\t", arrayOnCpu2[i][j]);
}
printf("\n");
}
// Create corresponding double array on the GPU.
//
double *pToArrayOnGpu;
size_t pitch;
gpuErrorCheck( cudaMallocPitch((void **)&pToArrayOnGpu, &pitch, THETA * sizeof(double), THETA) );
// Copy CPU data to vector on GPU.
//
gpuErrorCheck( cudaMemcpy2D(pToArrayOnGpu, pitch, arrayOnCpu2, pitch, THETA * sizeof(double), THETA, cudaMemcpyHostToDevice) );
// Launch GPU code with THETA threads, one per vector element.
//
addArrays<<<1, THETA>>>(pToArrayOnGpu, pitch);
gpuErrorCheck( cudaDeviceSynchronize() );
// Copy array from GPU back to CPU.
//
gpuErrorCheck( cudaMemcpy2D(arrayOnCpu2, pitch, pToArrayOnGpu,pitch, THETA * sizeof(double), THETA, cudaMemcpyDeviceToHost) );
// Print the vector of vectors.
//
for (int i = 0; i < THETA; i++) // Aantal buitenste vectoren.
{
for (int j = 0; j < THETA; j++) // Aantal binnenste elementen.
{
printf("%2.2f\t", arrayOnCpu2[i][j]);
}
printf("\n");
}
printf("\n\n");
// Free up the array on the GPU.
//
gpuErrorCheck( cudaFree(pToArrayOnGpu) );
return 0;
}
答案 0 :(得分:1)
代码的主要问题是您在主机端和设备端混淆了pitch
2D阵列的概念。下面是一个简短的介绍。
在主机中,一个[行] [列]存储在a+row*width_of_column*sizeof(element)+column*sizeof(element)
中。但是在cuda的全局内存访问中,从256字节对齐地址(addr = 0、256、512等)的连续访问效率最高。因此,为了提高内存访问效率,可以使用cudaMallocPitch函数。
在cudaMallocPitch()分配的内存中,数组每一行的第一个元素的地址对齐。由于未定义每行数据,因此row*width_of_column*sizeof(element)
不一定是256的倍数。因此,为了确保数组每行第一元素的起始地址对齐,cudaMallocPitch()
会为每个行分配更多字节分配内存以确保width_of_column*sizeof(element)+extra allocated bytes
256的倍数(对齐)。另外,从pitch
返回的cudaMallocPitch()
应该添加额外的已分配内存。
函数原型如下:
__host__ cudaError_t cudaMemcpy2D ( void* dst, size_t dpitch, const void* src, size_t spitch, size_t width, size_t height, cudaMemcpyKind kind )
因此,当您使用cudaMemcpy2D()时,应提供正确的pitch
,这意味着主机和设备阵列中的音高不相同。您的情况应该是这样:
gpuErrorCheck(cudaMemcpy2D(pToArrayOnGpu, pitch, arrayOnCpu2, THETA * sizeof(double), THETA * sizeof(double), THETA, cudaMemcpyHostToDevice));
第一个音高(dpitch)从cudaMallocPitch()
返回,而最后一个音高({pitch)是THETA * sizeof(double)
。
您可以在此link中获取更多信息。
我对您的代码做了简短的更改,可以在我的计算机上正常运行。
env: Ubuntu 16.04 Tesla P100
#include "cuda_runtime.h"
#include "device_launch_parameters.h"
#include <stdio.h>
#include <stdio.h>
#include <vector>
using namespace std;
#define THETA 10
// Error checking.
#define gpuErrorCheck(ans) { gpuAssert((ans), __FILE__, __LINE__); }
inline void gpuAssert(cudaError_t code, char *file, int line, bool abort = true)
{
if (code != cudaSuccess)
{
fprintf(stderr, "GPUassert: %s %s %d\n", cudaGetErrorString(code), file, line);
if (abort) exit(code);
}
}
// Pass 2-dim array to GPU and change it there.
//
__global__
void addArrays(double *twoDimArray, size_t pitch){
int tidx = threadIdx.x;
if ((tidx < THETA) /*&& (tidy < THETA)*/){
double tempval = 250;
for (int i = 0; i < THETA; i++){
double* row = (double*)((char*)twoDimArray + i * pitch);
row[tidx] *= tempval;
}
}
}
int main(){
double arrayOnCpu2[THETA][THETA];
// Initialise the vector of vector on the CPU.
for (int i = 0; i < THETA; i++){ // Aantal buitenste vectoren.
for (int j = 0; j < THETA; j++) // Aantal binnenste elementen.
arrayOnCpu2[i][j] = j;
}
// Print the vector of vectors.
printf("Before:\n");
for (int i = 0; i < THETA; i++){
for (int j = 0; j < THETA; j++)
printf("%2.2f\t", arrayOnCpu2[i][j]);
printf("\n");
}
// Create corresponding double array on the GPU.
double *pToArrayOnGpu;
size_t pitch;
gpuErrorCheck(cudaMallocPitch((void **)&pToArrayOnGpu, &pitch, THETA * sizeof(double), THETA));
// Copy CPU data to vector on GPU.
gpuErrorCheck(cudaMemcpy2D(pToArrayOnGpu, pitch, arrayOnCpu2, THETA * sizeof(double), THETA * sizeof(double), THETA, cudaMemcpyHostToDevice));
// Launch GPU code with THETA threads, one per vector element.
addArrays << <1, THETA >> >(pToArrayOnGpu, pitch);
gpuErrorCheck(cudaDeviceSynchronize());
// Copy array from GPU back to CPU.
gpuErrorCheck(cudaMemcpy2D(arrayOnCpu2, THETA * sizeof(double), pToArrayOnGpu, pitch, THETA * sizeof(double), THETA, cudaMemcpyDeviceToHost));
// Print the vector of vectors.
printf("After:\n");
for (int i = 0; i < THETA; i++){ // Aantal buitenste vectoren.
for (int j = 0; j < THETA; j++) // Aantal binnenste elementen.
printf("%2.2f\t", arrayOnCpu2[i][j]);
printf("\n");
}
printf("\n\n");
// Free up the array on the GPU.
gpuErrorCheck(cudaFree(pToArrayOnGpu));
return 0;
}
输出为:
Before:
0.00 1.00 2.00 3.00 4.00 5.00 6.00 7.00 8.00 9.00
0.00 1.00 2.00 3.00 4.00 5.00 6.00 7.00 8.00 9.00
0.00 1.00 2.00 3.00 4.00 5.00 6.00 7.00 8.00 9.00
0.00 1.00 2.00 3.00 4.00 5.00 6.00 7.00 8.00 9.00
0.00 1.00 2.00 3.00 4.00 5.00 6.00 7.00 8.00 9.00
0.00 1.00 2.00 3.00 4.00 5.00 6.00 7.00 8.00 9.00
0.00 1.00 2.00 3.00 4.00 5.00 6.00 7.00 8.00 9.00
0.00 1.00 2.00 3.00 4.00 5.00 6.00 7.00 8.00 9.00
0.00 1.00 2.00 3.00 4.00 5.00 6.00 7.00 8.00 9.00
0.00 1.00 2.00 3.00 4.00 5.00 6.00 7.00 8.00 9.00
After:
0.00 250.00 500.00 750.00 1000.00 1250.00 1500.00 1750.00 2000.00 2250.00
0.00 250.00 500.00 750.00 1000.00 1250.00 1500.00 1750.00 2000.00 2250.00
0.00 250.00 500.00 750.00 1000.00 1250.00 1500.00 1750.00 2000.00 2250.00
0.00 250.00 500.00 750.00 1000.00 1250.00 1500.00 1750.00 2000.00 2250.00
0.00 250.00 500.00 750.00 1000.00 1250.00 1500.00 1750.00 2000.00 2250.00
0.00 250.00 500.00 750.00 1000.00 1250.00 1500.00 1750.00 2000.00 2250.00
0.00 250.00 500.00 750.00 1000.00 1250.00 1500.00 1750.00 2000.00 2250.00
0.00 250.00 500.00 750.00 1000.00 1250.00 1500.00 1750.00 2000.00 2250.00
0.00 250.00 500.00 750.00 1000.00 1250.00 1500.00 1750.00 2000.00 2250.00
0.00 250.00 500.00 750.00 1000.00 1250.00 1500.00 1750.00 2000.00 2250.00
答案 1 :(得分:0)
首先,如果可能的话,通常希望避免在GPU上使用double
。尽管如今基本上所有支持CUDA的消费类GPU都以某种方式支持双精度算法,但通常比单精度要慢得多。
除此之外,关于不同音高应该意味着什么似乎有些困惑。通常,数组的音高是从一行的开始到下一行的开始必须跳过的字节数。驱动程序可以选择/需要在2D阵列的行之间添加填充,以使分配满足硬件的对齐要求和/或允许更优化的内存访问。这意味着,对于GPU上的2D阵列,间距可能会大于element size * width
。
但是,对于您的CPU阵列,间距仅为THETA * sizeof(double)
。您的GPU阵列的螺距从cudaMallocPitch()
返回给您。在对cudaMemcpy2D()
的调用中,您将GPU阵列的间距作为GPU和CPU的间距传递。为您的CPU阵列使用正确的间距。在设备代码中,您可以通过twoDimArray[tidy * THETA + tidx]
访问GPU阵列。在这里,您实际上是使用THETA * sizeof(double)
的间距来访问GPU数据。您应该使用阵列的实际间距。例如:
double* my_row = reinterpret_cast<double*>(reinterpret_cast<char*>(twoDimArray) + tidy * pitch);
my_row[tidx] = tempval;