第一题

设置线程块中线程数为1024效果优于设置为1023,且提升明显,不过原因未知,以后章节看看能不能回答。

第二题

参考文件sumArraysOnGPUtimer.cu,设置block=256,新建内核,使每个线程处理两个元素。

思路很简单,将数据的虚拟内存对半分为高低两块,每一内核线程同时处理两个索引区域序列相同的数据即可:

# include <cuda_runtime.h>
# include <stdio.h>
# include <sys/time.h>
# include "common.h" __global__ void sumArraysOnGPU(float *A, float *B, float *C, const int N)
{
int i = blockIdx.x * blockDim.x + threadIdx.x; if (i < N/2) {
C[i] = A[i] + B[i];
C[i+N/2] = A[i+N/2] + B[i+N/2];
}
} int main(int argc, char **argv)
{
printf("%s Starting...\n", argv[0]); // set up device
int dev = 0;
cudaDeviceProp deviceProp;
CHECK(cudaGetDeviceProperties(&deviceProp, dev));
printf("Using Device %d: %s\n", dev, deviceProp.name);
CHECK(cudaSetDevice(dev)); // set up data size of vectors
int nElem = 1 << 24;
printf("Vector size %d\n", nElem); // malloc host memory
size_t nBytes = nElem * sizeof(float); float *h_A, *h_B, *hostRef, *gpuRef;
h_A = (float *)malloc(nBytes);
h_B = (float *)malloc(nBytes);
hostRef = (float *)malloc(nBytes);
gpuRef = (float *)malloc(nBytes); double iStart, iElaps; // initialize data at host side
iStart = cpuSecond();
initialData(h_A, nElem);
initialData(h_B, nElem);
iElaps = cpuSecond() - iStart;
printf("initialData Time elapsed %f sec\n", iElaps);
memset(hostRef, 0, nBytes);
memset(gpuRef, 0, nBytes); // add vector at host side for result checks
iStart = cpuSecond();
sumArraysOnHost(h_A, h_B, hostRef, nElem);
iElaps = cpuSecond() - iStart;
printf("sumArraysOnHost Time elapsed %f sec\n", iElaps); // malloc device global memory
float *d_A, *d_B, *d_C;
CHECK(cudaMalloc((float**)&d_A, nBytes));
CHECK(cudaMalloc((float**)&d_B, nBytes));
CHECK(cudaMalloc((float**)&d_C, nBytes)); // transfer data from host to device
CHECK(cudaMemcpy(d_A, h_A, nBytes, cudaMemcpyHostToDevice));
CHECK(cudaMemcpy(d_B, h_B, nBytes, cudaMemcpyHostToDevice));
CHECK(cudaMemcpy(d_C, gpuRef, nBytes, cudaMemcpyHostToDevice)); // invoke kernel at host side
int iLen = 512;
dim3 block (iLen);
dim3 grid ((nElem + block.x - 1) / block.x / 2);
// <<< 16384, 512 >>> Time elapsed 0.000747 sec
// <<< 32768, 512 >>> Time elapsed 0.000709 sec iStart = cpuSecond();
sumArraysOnGPU<<<grid, block>>>(d_A, d_B, d_C, nElem);
CHECK(cudaDeviceSynchronize());
iElaps = cpuSecond() - iStart;
printf("sumArraysOnGPU <<< %d, %d >>> Time elapsed %f sec\n", grid.x,
block.x, iElaps); // check kernel error
// CHECK(cudaGetLastError()) ; // copy kernel result back to host side
CHECK(cudaMemcpy(gpuRef, d_C, nBytes, cudaMemcpyDeviceToHost)); // check device results
checkResult(hostRef, gpuRef, nElem); // free device global memory
CHECK(cudaFree(d_A));
CHECK(cudaFree(d_B));
CHECK(cudaFree(d_C)); // free host memory
free(h_A);
free(h_B);
free(hostRef);
free(gpuRef); return(0);
}

第四题

参考文件sumMatrixOnGPU-2D-gril-1D-block.cu,新建内核,使每个线程处理两个元素。

思路同上,由于是二维索引,所以采取的划分是按照纵坐标y将数据对半划分,可以直观理解为沿着y/2将数据对折,然后同一个线程处理数据为两个块中对应位置即可:

# include <cuda_runtime.h>
# include <stdio.h>
# include <sys/time.h>
# include "common.h" // grid 2D block 1D
__global__ void sumMatrixsOnGPUMix(float *MatA, float *MatB, float *MatC,
int nx, int ny)
{
int ix = threadIdx.x + blockIdx.x * blockDim.x;
int iy = blockIdx.y;
int idx = iy * nx + ix; if (ix < nx && iy < ny/2) {
MatC[idx] = MatA[idx] + MatB[idx];
MatC[idx + nx*ny/2] = MatA[idx + nx*ny/2] + MatB[idx + nx*ny/2];
}
} int main(int argc, char **argv){
printf("%s Startin... \n", argv[0]); //set up device
int dev = 0;
cudaDeviceProp deviceProp;
CHECK(cudaGetDeviceProperties(&deviceProp, dev));
printf("Using Device %d: %s\n", dev, deviceProp.name);
CHECK(cudaSetDevice(dev)); // matrix size
int nx = 1<<13;
int ny = 1<<5; // 2**18 int nxy = nx * ny;
int nBytes = nxy * sizeof(float);
printf("Matrix size:nx %d, ny %d\n", nx, ny); float *h_A, *h_B, *hostRef, *gpuRef;
h_A = (float *)malloc(nBytes);
h_B = (float *)malloc(nBytes);
hostRef = (float *)malloc(nBytes);
gpuRef = (float *)malloc(nBytes); // initialize data at host side
double iStart, iElaps;
iStart = cpuSecond();
initialData(h_A, nxy);
initialData(h_B, nxy);
iElaps = cpuSecond() - iStart; memset(hostRef, 0, nBytes);
memset(gpuRef, 0, nBytes); iStart = cpuSecond();
sumMatrixsOnHost(h_A, h_B, hostRef, nx, ny);
iElaps = cpuSecond() - iStart; // malloc device global memory
float *d_MatA, *d_MatB, *d_MatC;
cudaMalloc((float **)&d_MatA, nBytes);
cudaMalloc((float **)&d_MatB, nBytes);
cudaMalloc((float **)&d_MatC, nBytes); // transfer data from host to device
cudaMemcpy(d_MatA, h_A, nBytes, cudaMemcpyHostToDevice);
cudaMemcpy(d_MatB, h_B, nBytes, cudaMemcpyHostToDevice); // invoke kernel at host to device
dim3 block (256); // 2维块设置
dim3 grid ((nx+block.x-1)/block.x, ny/2); // 2维网格设置
/*
<<<(1024, 16384), (16, 1)>>> Time elapsed 0.021947sec
<<<(512, 16384), (32, 1)>>> Time elapsed 0.011039sec
<<<(64, 16384), (256, 1)>>> Time elapsed 0.009063sec
*/ iStart = cpuSecond();
sumMatrixsOnGPUMix<<<grid, block>>>(d_MatA, d_MatB, d_MatC, nx, ny);
cudaDeviceSynchronize(); // 测试用,同步线程,实际无需等待子线程
iElaps = cpuSecond() - iStart;
printf("sumArraysOnGPU <<<(%d, %d), (%d, %d)>>> Time elapsed %f" \
"sec\n", grid.x, grid.y, block.x, block.y, iElaps); cudaMemcpy(gpuRef, d_MatC, nBytes, cudaMemcpyDeviceToHost);
checkResult(hostRef, gpuRef, nxy); // free device global memory
cudaFree(d_MatA);
cudaFree(d_MatB);
cudaFree(d_MatC); // free host memory
free(h_A);
free(h_B);
free(hostRef);
free(gpuRef); // reset device
cudaDeviceReset(); return 0;
}

运行结果如下:

附common.h文件

# include <cuda_runtime.h>
# include <stdio.h>
# include <sys/time.h>
# define CHECK(call) \
{ \
const cudaError_t error = call; \
if (error != cudaSuccess) \
{ \
fprintf(stderr, "Error: %s:%d, ", __FILE__, __LINE__); \
fprintf(stderr, "code: %d, reason: %s\n", error, \
cudaGetErrorString(error)); \
exit(1); \
} \
} void initialData(float *ip, int size)
{
time_t t;
srand((unsigned int) time(&t)); for (int i=0; i<size; i++)
{
ip[i] = (float)(rand() & 0xFF)/10.0f;
}
} double cpuSecond() {
struct timeval tp;
gettimeofday(&tp, NULL);
return ((double)tp.tv_sec + (double)tp.tv_usec*1.e-6);
} void checkResult(float *hostRef, float *gpuRef, const int N) {
double epsilon = 1.0E-8;
bool match = 1;
for (int i=0; i<N; i++) {
if (abs(hostRef[i] - gpuRef[i]) > epsilon) {
match = 0;
printf("Arrays do not match!\n");
printf("host %5.2f gpu %5.2f at current %d\n",
hostRef[i], gpuRef[i], i);
break;
}
}
if (match) printf("Arrays match.\n\n");
} void sumArraysOnHost(float *A, float *B, float *C, const int N) {
for (int idx=0; idx<N; idx++)
C[idx] = A[idx] + B[idx];
} void sumMatrixsOnHost(float *A, float *B, float *C, const int nx, const int ny){
float *ia = A;
float *ib = B;
float *ic = C;
for (int iy=0; iy<ny; iy++){
for (int ix=0; ix<nx; ix++){
ic[ix] = ia[ix] + ib[ix];
}
ia += nx;
ib += nx;
ic += nx;
}
}

『CUDA C编程权威指南』第二章编程题选做的更多相关文章

  1. HTTP权威指南:第二章

    URL概览 前面提到,URL资源是HTTP协议所使用的寻找资源位置的定位符.分为三个部分,主要的结构是: 方案://服务器/路径 这种结构使得网络上的每一个资源都只有唯一的命名方法,从而使得浏览器可以 ...

  2. 【HTTP权威指南】第二章-URL与资源

    [统一资源定位符URL]通过位置来标示资源,其表达的格式如下:https://item.jd.com/523961.html 第一部分(https)是方案,告知客户端要[怎样访问],这里使用的是htt ...

  3. 读《Android编程权威指南》

    因为去年双十二购买了一折的<Android 编程权威指南(第一版)>,在第二版出来后图灵社区给我推送了第二版的优惠码,激动之余就立马下单购买电子书,不得不说Big Nerd Ranch G ...

  4. 《Android编程权威指南》

    <Android编程权威指南> 基本信息 原书名:Android programming: the big nerd ranch guide 原出版社: Big Nerd Ranch Gu ...

  5. Android编程权威指南第三版 第32章

    版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn.net/qq_35564145/article/de ...

  6. Swift编程权威指南第2版 读后收获

    自从参加工作一直在用OC做iOS开发.在2015年的时候苹果刚推出swift1.0不久,当时毕竟是新推出的语言,大家也都很有激情的学习.不过在学完后发现很难在实际项目中使用,再加上当时公司项目都是基于 ...

  7. 《Android编程权威指南》PhotoGallery应用梳理

    PhotoGalley是<Android编程权威指南>书中另外一个重要的应用.       

  8. 《Android编程权威指南》CriminalIntent项目梳理

    相信很多新手或者初级开发人员都已经买了第2版的<Android编程权威指南>, 这本书基于Android Studio开发,对入门人员来说是很好的选择,但是很可惜的是, 在完成一个项目后, ...

  9. 使用最新AndroidStudio编写Android编程权威指南(第3版)中的代码会遇到的一些问题

    Android编程权威指南(第3版)这本书是基于Android7.0的,到如今已经过于古老,最新的Android版本已经到10,而这本书的第四版目前还没有正式发售,在最近阅读这本书时,我发现这本书的部 ...

随机推荐

  1. 【Spring Security】七、RememberMe配置

    一.概述 RememberMe 是指用户在网站上能够在 Session 之间记住登录用户的身份的凭证,通俗的来说就是用户登陆成功认证一次之后在制定的一定时间内可以不用再输入用户名和密码进行自动登录.这 ...

  2. 网络_TCP连接的建立与释放

    三报文握手 1.概述 TCP是面向连接的协议.TCP建立连接的过程叫做握手,握手需要在客户和服务器之间交换三个TCP报文段,即我们说的"三次握手"(严格讲是一次握手过程中交换了三个 ...

  3. 论文阅读:CNN-RNN: A Unified Framework for Multi-label Image Classification

    CNN-RNN: A Unified Framework for Multi-label Image Classification Updated on 2018-08-07 22:30:41 Pap ...

  4. 论文笔记之:Learning Cross-Modal Deep Representations for Robust Pedestrian Detection

    Learning Cross-Modal Deep Representations for Robust Pedestrian Detection 2017-04-11  19:40:22  Moti ...

  5. 【NOI 2016】优秀的拆分

    Problem Description 如果一个字符串可以被拆分为 \(AABB\) 的形式,其中 \(A\) 和 \(B\) 是任意非空字符串,则我们称该字符串的这种拆分是优秀的. 例如,对于字符串 ...

  6. HDU 4325 Flowers(树状数组+离散化)

    http://acm.hdu.edu.cn/showproblem.php?pid=4325 题意:给出n个区间和m个询问,每个询问为一个x,问有多少个区间包含了x. 思路: 因为数据量比较多,所以需 ...

  7. Go 结构体(Struct)

    引用曾经看到的一篇文章里面对 Golang 中结构体的描述,如果说 Golang 的基础类型是原子,那么 结构体就是分子.我们都知道分子是由原子组成的,换言之就是结构体里面可以包含基础类型.切片. 字 ...

  8. Python pycharm 常用快捷键

    快捷键 1.编辑(Editing) Ctrl + Space 基本的代码完成(类.方法.属性) Ctrl + Alt + Space 快速导入任意类 Ctrl + Shift + Enter 语句完成 ...

  9. Thymeleaf的基本语法总结

    最近用Spring boot开发一些测试平台和工具,用到页面展示的部分, 选择的是thymeleaf模版引擎. 页面开发的7788快结束了,下面来总结下此过程中对thymeleaf的使用总结. 什么是 ...

  10. Oracle(转换函数)

    在数据库中主要使用数据类型:字符,数字,日期(时间戳),所以这三种数据类型之间需要实现转换操作. 常用转换函数: 3.1.TO_CHAR()函数 将数据类型变为字符串. 日期格式化标记: 在TO_CH ...