【cuda学习日记】3.3 CUDA执行模型--展开循环

3.3
考虑以下代码

for(int i = 0; i < 100; i++){a[i] = b[i] + c[i];
}for(int i = 0; i < 100; i+=2){a[i] = b[i] + c[i];a[i+1] = b[i+1] + c[i+1];
}

原来的循环中则检查了100次, 展开后i < 100只检查了50次。在CUDA中,循环展开的意义非常重大:通过减少指令消耗和增加更多的独立调度指令来提高性能。因此,更多的并发操作被添加到流水线上,以产生更高的指令和内存带宽。

3.3.1 展开的归约
(接上一篇3.2 归约问题)在reduceInterleaved核函数中每个线程块只处理一部分数据,这些数据可以被认为是一个数据块。如果用一个线程块手动展开两个数据块的处理。
添加核函数

__global__ void reduceUnrolling2( int *g_idata, int *g_odata, unsigned int n){unsigned int tid = threadIdx.x;int * idata = g_idata + blockIdx.x * blockDim.x * 2;  //指针的间隔变成2个BLOCKDIMint idx = threadIdx.x + blockIdx.x * blockDim.x * 2;if (idx > n) return;  // boundary check//unrolling 2 data blocks  ,先把2个block的数加起来?if (idx + blockDim.x < n){g_idata[idx] += g_idata[idx + blockDim.x];}__syncthreads();for (int stride = blockDim.x / 2; stride > 0; stride >>= 1){  //if (tid < stride){idata[tid] += idata[tid + stride];}__syncthreads();}if  (tid == 0){ g_odata[blockIdx.x] = idata[0];}
}

因为现在每个线程块处理两个数据块,我们需要调整内核的执行配置,将网格大小减
小至一半, 在main函数里添加

	cudaMemcpy(d_idata, h_idata, nBytes, cudaMemcpyHostToDevice);cudaDeviceSynchronize();timer.start();reduceUnrolling2<<<grid.x /2, block>>>(d_idata, d_odata, size);cudaDeviceSynchronize(); timer.stop();float elapsedTime5 = timer.elapsedms();cudaMemcpy(h_odata, d_odata, grid.x * sizeof(int),cudaMemcpyDeviceToHost);gpu_sum = 0;for (int i = 0; i < grid.x / 2; i ++){gpu_sum += h_odata[i];}printf("reduceUnrolling2 gpu reduce time: %f,  sum: %d, gird ,block (%d %d)\n", elapsedTime5, gpu_sum, grid.x / 2, block.x);

输出:
sum value is : 2139095040
cpu reduce time: 11.161504, sum: 2139095040
warm up reduce time: 0.570368, sum: 2139095040
reduceNeighbored gpu reduce time: 0.231840, sum: 2139095040, gird ,block (32768 512)
reduceNeighboredLess gpu reduce time: 0.179328, sum: 2139095040, gird ,block (32768 512)
reduceInterleaved gpu reduce time: 0.116288, sum: 2139095040, gird ,block (32768 512)
reduceUnrolling2 gpu reduce time: 0.075360, sum: 2139095040, gird ,block (16384 512)
简单的展开也能让核函数的执行速度比一开始快约3倍。可以进一步展开获得更好的性能吗?

//核函数
__global__ void reduceUnrolling4( int *g_idata, int *g_odata, unsigned int n){unsigned int tid = threadIdx.x;int * idata = g_idata + blockIdx.x * blockDim.x * 4;  //指针的间隔变成4个BLOCKDIMint idx = threadIdx.x + blockIdx.x * blockDim.x * 4;if (idx > n) return;  // boundary check//unrolling 4 data blocks if (idx +  3 * blockDim.x < n) {g_idata[idx] += (g_idata[idx + 1 * blockDim.x]);g_idata[idx] += (g_idata[idx + 2 * blockDim.x]);g_idata[idx] += (g_idata[idx + 3 * blockDim.x]);}__syncthreads();for (int stride = blockDim.x / 4; stride > 0; stride >>= 1){  //if (tid < stride){idata[tid] += idata[tid + stride];}__syncthreads();}
//调用
cudaMemcpy(d_idata, h_idata, nBytes, cudaMemcpyHostToDevice);cudaDeviceSynchronize();timer.start();reduceUnrolling4<<<grid.x /4, block>>>(d_idata, d_odata, size);cudaDeviceSynchronize(); timer.stop();float elapsedTime6 = timer.elapsedms();cudaMemcpy(h_odata, d_odata, grid.x/4 * sizeof(int),cudaMemcpyDeviceToHost);gpu_sum = 0;for (int i = 0; i < grid.x / 4; i ++){gpu_sum += h_odata[i];}printf("reduceUnrolling4 gpu reduce time: %f,  sum: %d, gird ,block (%d %d)\n", elapsedTime6, gpu_sum, grid.x / 4, block.x);

输出的结果:
sum value is : 2139095040
cpu reduce time: 10.978304, sum: 2139095040
warm up reduce time: 5.448128, sum: 2139095040
reduceNeighbored gpu reduce time: 0.234400, sum: 2139095040, gird ,block (32768 512)
reduceNeighboredLess gpu reduce time: 0.279584, sum: 2139095040, gird ,block (32768 512)
reduceInterleaved gpu reduce time: 0.134304, sum: 2139095040, gird ,block (32768 512)
reduceUnrolling2 gpu reduce time: 0.104128, sum: 2139095040, gird ,block (16384 512)
reduceUnrolling4 gpu reduce time: 0.084064, sum: 1069547520, gird ,block (8192 512)
发现sum的值,是原来的一半,还不理解。

破案了

__global__ void reduceUnrolling4( int *g_idata, int *g_odata, unsigned int n){unsigned int tid = threadIdx.x;int * idata = g_idata + blockIdx.x * blockDim.x * 4;  //指针的间隔变成4个BLOCKDIMint idx = threadIdx.x + blockIdx.x * blockDim.x * 4;if (idx > n) return;  // boundary check//unrolling 4 data blocks if (idx +  3 * blockDim.x < n) {g_idata[idx] += (g_idata[idx + 1 * blockDim.x]);g_idata[idx] += (g_idata[idx + 2 * blockDim.x]);g_idata[idx] += (g_idata[idx + 3 * blockDim.x]);}__syncthreads();for (int stride = blockDim.x / 2; stride > 0; stride >>= 1){  //关键是 blockDim.x / 2不能改, 这个2是从interleaved继承来的,而不是unrolling2if (tid < stride){idata[tid] += idata[tid + stride];}__syncthreads();}if  (tid == 0){ g_odata[blockIdx.x] = idata[0];}
}

结果输出:
reduceUnrolling2 gpu reduce time: 0.244896, sum: 2139095040, gird ,block (16384 512)
reduceUnrolling4 gpu reduce time: 0.074560, sum: 2139095040, gird ,block (8192 512)
reduceUnrolling8 gpu reduce time: 0.050816, sum: 2139095040, gird ,block (4096 512)

3.3.2 展开线程的归约
__syncthreads是用于块内同步的。在归约核函数中,它用来确保在线程进入下一轮之前,每一轮中所有线程已经将局部结果写入全局内存中了。
然而,要细想一下只剩下32个或更少线程(即一个线程束)的情况。因为线程束的执行是SIMT(单指令多线程)的,每条指令之后有隐式的线程束内同步过程。因此,归约循环的最后6个迭代可以用语句来展开,展开避免了执行循环控制和线程同步逻辑。注意变量vmem是和volatile修饰符一起被声明的,它告诉编译器每次赋值时必须将vmem[tid]的值存回全局内存中。

__global__ void reduceUnrolling8Warp( int *g_idata, int *g_odata, unsigned int n){unsigned int tid = threadIdx.x;int * idata = g_idata + blockIdx.x * blockDim.x * 8;  //指针的间隔变成8个BLOCKDIMint idx = threadIdx.x + blockIdx.x * blockDim.x * 8;if (idx > n) return;  // boundary check//unrolling 8 data blocks if (idx + 7 * blockDim.x < n){g_idata[idx] += (g_idata[idx + blockDim.x] + g_idata[idx + 2 * blockDim.x] + g_idata[idx + 3 * blockDim.x]+ g_idata[idx + 4 * blockDim.x] + g_idata[idx + 5 * blockDim.x] + g_idata[idx + 6 * blockDim.x] + g_idata[idx + 7 * blockDim.x]);}__syncthreads();for (int stride = blockDim.x / 2; stride > 32; stride >>= 1){  //  这地方改了 32!if (tid < stride){idata[tid] += idata[tid + stride];}__syncthreads();}if (tid < 32){volatile int *vmem  = idata;vmem[tid] += vmem[tid + 32];   //怎么保证这一行执行完了才是下一行?vmem[tid] += vmem[tid + 16];vmem[tid] += vmem[tid +  8];vmem[tid] += vmem[tid +  4];vmem[tid] += vmem[tid +  2];vmem[tid] += vmem[tid +  1];}if  (tid == 0){ g_odata[blockIdx.x] = idata[0];}
}

书上介绍说会比Unrolling8快,但是我执行下来多数比Unrolling8慢一点:
reduceUnrolling8 gpu reduce time: 0.047424, sum: 2139095040, gird ,block (4096 512)
reduceUnrolling8Warp gpu reduce time: 0.054528, sum: 2139095040, gird ,block (4096 512)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/5541.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

微服务学习-Gateway 统一微服务入口

1. 微服务为什么需要 API 网关&#xff1f; 1.1. 在微服务架构中&#xff0c;通常一个系统会被拆分为多个微服务&#xff0c;面对多个微服务客户端应该如何去调用呢&#xff1f; 如果根据每个微服务的地址发起调用&#xff0c;存在如下问题&#xff1a; 客户端多次请求不同的…

linux-FTP服务配置与应用

也许你对FTP不陌生&#xff0c;但是你是否了解FTP到底是个什么玩意&#xff1f; FTP 是File Transfer Protocol&#xff08;文件传输协议&#xff09;的英文简称&#xff0c;而中文简称为 “文传协议” 用于Internet上的控制文件的双向传输。同时&#xff0c;它也是一个应用程序…

代码随想录算法训练营第 14 天(树2)| 226.翻转二叉树、101. 对称二叉树、104.二叉树的最大深度、111.二叉树的最小深度

一、#226.翻转二叉树 题目&#xff1a;https://leetcode.cn/problems/invert-binary-tree/ 视频&#xff1a;https://www.bilibili.com/video/BV1sP4y1f7q7 讲解&#xff1a;https://programmercarl.com/0226.%E7%BF%BB%E8%BD%AC%E4%BA%8C%E5%8F%89%E6%A0%91.html 注意这里交换…

基于微信小程序的科创微应用平台设计与实现(LW+源码+讲解)

专注于大学生项目实战开发,讲解,毕业答疑辅导&#xff0c;欢迎高校老师/同行前辈交流合作✌。 技术范围&#xff1a;SpringBoot、Vue、SSM、HLMT、小程序、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容&#xff1a;…

【MySQL】数据库基础知识

欢迎拜访&#xff1a;雾里看山-CSDN博客 本篇主题&#xff1a;【MySQL】数据库基础知识 发布时间&#xff1a;2025.1.21 隶属专栏&#xff1a;MySQL 目录 什么是数据库为什么要有数据库数据库的概念 主流数据库mysql的安装mysql登录使用一下mysql显示数据库内容创建一个数据库创…

STM32学习9---EXIT外部中断(理论)

本文参考江科大和其他博主&#xff0c;侵删&#xff01; 中断系统是管理和执行中断的逻辑结构 &#xff0c;外部中断是产生中断的外设之一。 一、STM32中断 1、中断基本介绍 68个可屏蔽中断通道&#xff08;中断源&#xff09;&#xff0c;包含EXTI外部、TIM定时器、ADC模数…

步入响应式编程篇(二)之Reactor API

步入响应式编程篇&#xff08;二&#xff09;之Reactor API 前言回顾响应式编程Reactor API的使用Stream引入依赖Reactor API的使用流源头的创建 reactor api的背压模式发布者与订阅者使用的线程查看弹珠图查看形成新流的日志 前言 对于响应式编程的基于概念&#xff0c;以及J…

66,【6】buuctf web [HarekazeCTF2019]Avatar Uploader 1

进入靶场 习惯性输入admin 还想用桌面上的123.png 发现不行 看看给的源码 <?php // 关闭错误报告&#xff0c;可能会隐藏一些错误信息&#xff0c;在开发阶段可考虑开启&#xff08;例如 error_reporting(E_ALL)&#xff09; error_reporting(0); // 引入配置文件&#x…

FortiGate配置远程拨号VPN

我们前面介绍了FortiGate如何配置IPsec VPN的两种类型&#xff1a;站到站&#xff08;卷土重来&#xff01;这次终于把FortiGate的IPsec VPN配置成功了&#xff01;&#xff09;和Hub-and-Spoke&#xff08;漂亮&#xff01;FortiGate配置Hub-Spoke类型的IPsec VPN竟然是Full-M…

linux下springboot项目nohup日志或tomcat日志切割处理方案

目录 1. 配置流程 2. 配置说明 其他配置选项&#xff1a; 3. 测试执行 4. 手动执行 https://juejin.cn/post/7081890486453010469 通常情况下&#xff0c;我们的springboot项目部署到linux服务器中&#xff0c;通过nohup java -jar xxx.jar &指令来进行后台运行我们…

CSS中相对定位和绝对定位详解

文章目录 CSS中相对定位和绝对定位详解一、引言二、相对定位1、相对定位的概念1.1、代码示例 三、绝对定位1、绝对定位的概念1.1、代码示例 四、相对定位与绝对定位的区别五、使用示例六、总结 CSS中相对定位和绝对定位详解 一、引言 在CSS布局中&#xff0c;定位是一种强大的…

XCode-Color-Fixer 常见问题解决方案

XCode-Color-Fixer 常见问题解决方案 XCode-Color-Fixer StoryBoard / XIB 颜色偏差很严重&#xff0c;怎么破&#xff1f;XCode-Color-Fixer帮你忙&#xff01; 项目地址: https://gitcode.com/gh_mirrors/xc/XCode-Color-Fixer 项目基础介绍 XCode-Color-Fixer 是一个…

Visual Studio2019调试DLL

1、编写好DLL代码之后&#xff0c;对DLL项目的属性进行设置&#xff0c;选择待注入的DLL&#xff0c;如下图所示 2、生成DLL文件 3、将DLL设置为启动项目之后&#xff0c;按F5启动调试。弹出选择注入的exe的界面之后&#xff0c;使用代码注入器注入步骤2中生成的dll&#xff0…

C++入门基础篇:域、C++的输入输出、缺省参数、函数重载、引用、inline、nullptr

本篇文章是对C学习前期的一些基础部分的学习分享&#xff0c;希望也能够对你有所帮助。 那咱们废话不多说&#xff0c;直接开始吧&#xff01; 目录 1.第一个C程序 2. 域 3. namespace 3.1 namespace的作用 3.2 namespace的定义 3.3 namespace使用说明 4.C的输入和输出…

在Ubuntu上安装RabbitMQ教程

1、安装erlang 因为rabbitmq是基于erlang开发的&#xff0c;所以要安装rabbitmq&#xff0c;首先需要安装erlang运行环境 apt-get install erlang执行命令查是否安装成功&#xff1a;erl&#xff0c;疯狂 Ctrlc 就能退出命令行 2、安装rabbitmq 1、查看erlang与rabbitmq版本…

latin1_swedish_ci(latin1 不支持存储中文、日文、韩文等多字节字符)

文章目录 1、SHOW TABLE STATUS WHERE Name batch_version;2、latin1_swedish_ci使用场景注意事项修改字符集和排序规则修改表的字符集和排序规则修改列的字符集和排序规则修改数据库的默认字符集和排序规则 3、ALTER TABLE batch_version CONVERT TO CHARACTER SET utf8mb4 C…

使用vue-next-admin框架后台修改动态路由

vue-next-admin框架是一个基于 Vue 3 和 Vite 构建的后台管理系统框架。它采用了最新的前端技术栈&#xff0c;旨在提供一个高效、灵活、现代化的管理后台解决方案。该框架主要用于构建功能丰富且易于定制的管理后台应用&#xff0c;适合各种中大型项目。 其主要特点包括&am…

qiankun+vite+vue3

基座与子应用代码示例 本示例中,基座为Vue3,子应用也是Vue3,由于qiankun不支持Vite构建的项目,这里还要引入 vite-plugin-qiankun 插件 基座(主应用) 加载qiankun依赖 npm i qiankun -S qiankun配置(src/qiankun) src/qiankun/config.ts export default {subApp…

深度学习中Batch Normalization(BN)原理、作用浅析

最近做剪枝学习&#xff0c;其中一种是基于BN层的γ作为缩放因子进行剪枝的&#xff0c;那么我想搞懂BN的工作原理更好的理解网络、剪枝等&#xff0c;所以有了该文。 首先先说BN的作用在详细拆解&#xff0c;理解。以知乎一条高赞评论说明BN层到底在干什么。 Batch Norm 为什…

Python----Python高级(正则表达式:语法规则,re库)

一、正则表达式 1.1、概念 正则表达式&#xff0c;又称规则表达式,&#xff08;Regular Expression&#xff0c;在代码中常简写为regex、 regexp或RE&#xff09;&#xff0c;是一种文本模式&#xff0c;包括普通字符&#xff08;例如&#xff0c;a 到 z 之间的字母&#xff0…