Cuda 我应该声明内部或外部维度上带有GPU块号的双数组吗?
我应该声明内部或外部维度上带有GPU块号的双数组吗 例如,我应该怎么做Cuda 我应该声明内部或外部维度上带有GPU块号的双数组吗?,cuda,Cuda,我应该声明内部或外部维度上带有GPU块号的双数组吗 例如,我应该怎么做 int payload[LEN][BLOCKS]; 或 其中LEN是一个非常大的数字 我计划让每个块遍历双数组,保持块维度常量并在LEN维度上迭代。假设您要以面向块的方式访问数据,您希望执行后者。这大概是因为当您加载“len”维度的第一个元素时,您已经为后续7ish元素在缓存中丢失付出了代价。在第一个选项中,GPU块之间可能共享缓存线,但共享相对有限,级别也不低 实际上,下面的代码报告第二个选项需要0.481秒才能执行,第
int payload[LEN][BLOCKS];
或
其中LEN是一个非常大的数字
我计划让每个块遍历双数组,保持块维度常量并在LEN维度上迭代。假设您要以面向块的方式访问数据,您希望执行后者。这大概是因为当您加载“len”维度的第一个元素时,您已经为后续7ish元素在缓存中丢失付出了代价。在第一个选项中,GPU块之间可能共享缓存线,但共享相对有限,级别也不低 实际上,下面的代码报告第二个选项需要0.481秒才能执行,第一个选项需要0.979秒将数据块排列在外部尺寸上的性能大约是原来的两倍。
#include <cuda_runtime_api.h>
#include <cuda.h>
#include <string>
#include <chrono>
#include <iostream>
#define BLOCKS 80
#define LEN (1 << 20)
void CheckCudaErrorAux (const char *file, unsigned line, const char *statement, cudaError_t err) {
if (err == cudaSuccess)
return;
std::cerr << statement<<" returned " << cudaGetErrorString(err) << "("<<err<< ") at "<<file<<":"<<line << std::endl;
exit (1);
}
#define CUDA_CHECK_RETURN(value) CheckCudaErrorAux(__FILE__,__LINE__, #value, value)
struct Data1 {
int payload[LEN][BLOCKS];
};
struct Data2 {
int payload[BLOCKS][LEN];
};
__global__ void f1(Data1 * data1) {
int sum = 0;
for (int i = 0; i < LEN; ++i) {
sum += data1->payload[i][blockIdx.x];
}
printf("block %i has f1 sum %i\n", blockIdx.x, sum);
}
__global__ void f2(Data2 * data2) {
int sum = 0;
for (int i = 0; i < LEN; ++i) {
sum += data2->payload[blockIdx.x][i];
}
printf("block %i has f2 sum %i\n", blockIdx.x, sum);
}
int main() {
Data1 * data1 = (Data1 *) malloc(sizeof(Data1));
Data2 * data2 = (Data2 *) malloc(sizeof(Data2));;
for (int i = 0; i < LEN; ++i) {
for (int b = 0; b < BLOCKS; ++b) {
data1->payload[i][b] = i * b;
data2->payload[b][i] = i * b;
}
}
Data1 * data1_on_gpu;
CUDA_CHECK_RETURN(cudaMalloc(&data1_on_gpu, sizeof(Data1)));
Data2 * data2_on_gpu;
cudaMalloc(&data2_on_gpu, sizeof(Data2));
CUDA_CHECK_RETURN(cudaDeviceSynchronize());
cudaMemcpy(data1_on_gpu, data1, sizeof(Data1), cudaMemcpyHostToDevice);
cudaMemcpy(data2_on_gpu, data2, sizeof(Data1), cudaMemcpyHostToDevice);
CUDA_CHECK_RETURN(cudaDeviceSynchronize());
std::chrono::time_point<std::chrono::system_clock> t1 = std::chrono::system_clock::now();
f1<<<80,1>>>(data1_on_gpu);
CUDA_CHECK_RETURN(cudaDeviceSynchronize());
std::chrono::time_point<std::chrono::system_clock> t2 = std::chrono::system_clock::now();
f2<<<80,1>>>(data2_on_gpu);
CUDA_CHECK_RETURN(cudaDeviceSynchronize());
std::chrono::time_point<std::chrono::system_clock> t3 = std::chrono::system_clock::now();
std::chrono::duration<double> duration_1_to_2 = t2 - t1;
std::chrono::duration<double> duration_2_to_3 = t3 - t2;
duration_1_to_2.count();
printf("timer for 1st took %.3lf\n", duration_1_to_2.count());
printf("timer for 2nd took %.3lf\n", duration_2_to_3.count());
}
#包括
#包括
#包括
#包括
#包括
#定义块80
#定义LEN(确实是1。但最好只使用1D数组,而不是2D。2D数组是分段的,每个指针都是连续的内存块,但整个数组将不连续。如果使用1D数组,则可以使用常量或纹理内存(取决于应用程序)来加速它其中一种是有效的访问模式,如果您对GPU上的性能感兴趣,您永远不希望启动每个块包含一个线程的块。此外,您不清楚内核启动配置参数是什么。除非您认为您的f2
内核需要精确的1字节动态数据联合分配的共享内存(它没有),
配置没有多大意义。无论如何,
或
对性能都不好。这是一个打字错误——修复了,谢谢。很明显,我真正的应用程序每个块有多个线程。这个问答只是为了帮助我弄清楚如何布局我的一个数据结构。在真正的应用程序中,所有的一个块中的线程正在读取gpu上的数据[1/2]\u中的同一个条目,所以我猜额外的线程对于回答这个问题来说是无关紧要的。在某种程度上,我感到欣慰的是,性能差异只是2倍的一个因素。对我来说,这意味着硬件在弥补“坏”方面已经走了很长的路编程。如果差异明显更大,那么这里的“坏”选择可能会主导我的程序的性能。事实上,这两种方式可能都没有多大关系。
#include <cuda_runtime_api.h>
#include <cuda.h>
#include <string>
#include <chrono>
#include <iostream>
#define BLOCKS 80
#define LEN (1 << 20)
void CheckCudaErrorAux (const char *file, unsigned line, const char *statement, cudaError_t err) {
if (err == cudaSuccess)
return;
std::cerr << statement<<" returned " << cudaGetErrorString(err) << "("<<err<< ") at "<<file<<":"<<line << std::endl;
exit (1);
}
#define CUDA_CHECK_RETURN(value) CheckCudaErrorAux(__FILE__,__LINE__, #value, value)
struct Data1 {
int payload[LEN][BLOCKS];
};
struct Data2 {
int payload[BLOCKS][LEN];
};
__global__ void f1(Data1 * data1) {
int sum = 0;
for (int i = 0; i < LEN; ++i) {
sum += data1->payload[i][blockIdx.x];
}
printf("block %i has f1 sum %i\n", blockIdx.x, sum);
}
__global__ void f2(Data2 * data2) {
int sum = 0;
for (int i = 0; i < LEN; ++i) {
sum += data2->payload[blockIdx.x][i];
}
printf("block %i has f2 sum %i\n", blockIdx.x, sum);
}
int main() {
Data1 * data1 = (Data1 *) malloc(sizeof(Data1));
Data2 * data2 = (Data2 *) malloc(sizeof(Data2));;
for (int i = 0; i < LEN; ++i) {
for (int b = 0; b < BLOCKS; ++b) {
data1->payload[i][b] = i * b;
data2->payload[b][i] = i * b;
}
}
Data1 * data1_on_gpu;
CUDA_CHECK_RETURN(cudaMalloc(&data1_on_gpu, sizeof(Data1)));
Data2 * data2_on_gpu;
cudaMalloc(&data2_on_gpu, sizeof(Data2));
CUDA_CHECK_RETURN(cudaDeviceSynchronize());
cudaMemcpy(data1_on_gpu, data1, sizeof(Data1), cudaMemcpyHostToDevice);
cudaMemcpy(data2_on_gpu, data2, sizeof(Data1), cudaMemcpyHostToDevice);
CUDA_CHECK_RETURN(cudaDeviceSynchronize());
std::chrono::time_point<std::chrono::system_clock> t1 = std::chrono::system_clock::now();
f1<<<80,1>>>(data1_on_gpu);
CUDA_CHECK_RETURN(cudaDeviceSynchronize());
std::chrono::time_point<std::chrono::system_clock> t2 = std::chrono::system_clock::now();
f2<<<80,1>>>(data2_on_gpu);
CUDA_CHECK_RETURN(cudaDeviceSynchronize());
std::chrono::time_point<std::chrono::system_clock> t3 = std::chrono::system_clock::now();
std::chrono::duration<double> duration_1_to_2 = t2 - t1;
std::chrono::duration<double> duration_2_to_3 = t3 - t2;
duration_1_to_2.count();
printf("timer for 1st took %.3lf\n", duration_1_to_2.count());
printf("timer for 2nd took %.3lf\n", duration_2_to_3.count());
}