CUDA Samples: Calculate Histogram(atomicAdd)
以下CUDA sample是分别用C++和CUDA实现的计算一维直方图,并对其中使用到的CUDA函数进行了解说,code参考了《GPU高性能编程CUDA实战》一书的第九章,各个文件内容如下:
funset.cpp:
#include "funset.hpp"
#include <random>
#include <iostream>
#include <vector>
#include <memory>
#include <string>
#include <algorithm>
#include "common.hpp"
#include <opencv2/opencv.hpp>int test_calculate_histogram()
{const int length{ 10 * 1024 * 1024 }; // 100MBstd::unique_ptr<unsigned char[]> data(new unsigned char[length]);generator_random_number<unsigned char>(data.get(), length, 0, 255);const int hist_size{ 256 };std::unique_ptr<size_t[]> hist1(new size_t[hist_size]), hist2(new size_t[hist_size]);std::for_each(hist1.get(), hist1.get() + hist_size, [](size_t& n) {n = 0; });std::for_each(hist2.get(), hist2.get() + hist_size, [](size_t& n) {n = 0; });float elapsed_time1{ 0.f }, elapsed_time2{ 0.f }; // millisecondssize_t value1{ 0 }, value2{ 0 };int ret = calculate_histogram_cpu(data.get(), length, hist1.get(), value1, &elapsed_time1);if (ret != 0) PRINT_ERROR_INFO(calculate_histogram_cpu);ret = calculate_histogram_gpu(data.get(), length, hist2.get(), value2, &elapsed_time2);if (ret != 0) PRINT_ERROR_INFO(calculate_histogram_gpu);if (value1 != value2) {fprintf(stderr, "their values are different: val1: %d, val2: %d\n", value1, value2);return -1;}for (int i = 0; i < hist_size; ++i) {if (hist1[i] != hist2[i]) {fprintf(stderr, "their values are different at: %d, val1: %d, val2: %d\n",i, hist1[i], hist2[i]);return -1;}}fprintf(stderr, "test calculate histogram: cpu run time: %f ms, gpu run time: %f ms\n", elapsed_time1, elapsed_time2);return 0;
}
calculate_histogram.cpp:#include "funset.hpp"
#include <chrono>
#include "common.hpp"int calculate_histogram_cpu(const unsigned char* data, int length, size_t* hist, size_t& value, float* elapsed_time)
{auto start = std::chrono::steady_clock::now();for (int i = 0; i < length; ++i) {++hist[data[i]];}value = 0;for (int i = 0; i < 256; ++i) {value += hist[i];}auto end = std::chrono::steady_clock::now();auto duration = std::chrono::duration_cast<std::chrono::nanoseconds>(end - start);*elapsed_time = duration.count() * 1.0e-6;return 0;
}
calculate_histogram.cu:#include "funset.hpp"
#include <iostream>
#include <algorithm>
#include <memory>
#include <vector>
#include <cuda_runtime.h> // For the CUDA runtime routines (prefixed with "cuda_")
#include <device_launch_parameters.h>
#include "common.hpp"/* __global__: 函数类型限定符;在设备上运行;在主机端调用,计算能力3.2及以上可以在
设备端调用;声明的函数的返回值必须是void类型;对此类型函数的调用是异步的,即在
设备完全完成它的运行之前就返回了;对此类型函数的调用必须指定执行配置,即用于在
设备上执行函数时的grid和block的维度,以及相关的流(即插入<<< >>>运算符);
a kernel,表示此函数为内核函数(运行在GPU上的CUDA并行计算函数称为kernel(内核函
数),内核函数必须通过__global__函数类型限定符定义); */
__global__ static void calculate_histogram(const unsigned char* data, int length, size_t* hist)
{/* __shared__: 变量类型限定符;使用__shared__限定符,或者与__device__限定符连用,此时声明的变量位于block中的共享存储器空间中,与block具有相同的生命周期,仅可通过block内的所有线程访问;__shared__和__constant__变量默认为是静态存储;在__shared__前可以加extern关键字,但表示的是变量大小由执行参数确定;__shared__变量在声明时不能初始化;可以将CUDA C的关键字__shared__添加到变量声明中,这将使这个变量驻留在共享内存中;CUDA C编译器对共享内存中的变量与普通变量将分别采取不同的处理方式 */// clear out the accumulation buffer called temp since we are launched with// 256 threads, it is easy to clear that memory with one write per thread__shared__ size_t temp[256]; // 共享内存缓冲区temp[threadIdx.x] = 0;/* __syncthreads: 对线程块中的线程进行同步;CUDA架构将确保,除非线程块中的每个线程都执行了__syncthreads(),否则没有任何线程能执行__syncthreads()之后的指令;在同一个block中的线程通过共享存储器(sharedmemory)交换数据,并通过栅栏同步(可以在kernel函数中需要同步的位置调用__syncthreads()函数)保证线程间能够正确地共享数据;使用clock()函数计时,在内核函数中要测量的一段代码的开始和结束的位置分别调用一次clock()函数,并将结果记录下来。由于调用__syncthreads()函数后,一个block中的所有thread需要的时间是相同的,因此只需要记录每个block执行需要的时间就行了,而不需要记录每个thread的时间 */__syncthreads();/* gridDim: 内置变量,用于描述线程网格的维度,对于所有线程块来说,这个变量是一个常数,用来保存线程格每一维的大小,即每个线程格中线程块的数量.一个grid最多只有二维,为dim3类型;blockDim: 内置变量,用于说明每个block的维度与尺寸.为dim3类型,包含了block在三个维度上的尺寸信息;对于所有线程块来说,这个变量是一个常数,保存的是线程块中每一维的线程数量;blockIdx: 内置变量,变量中包含的值就是当前执行设备代码的线程块的索引;用于说明当前thread所在的block在整个grid中的位置,blockIdx.x取值范围是[0,gridDim.x-1],blockIdx.y取值范围是[0, gridDim.y-1].为uint3类型,包含了一个block在grid中各个维度上的索引信息;threadIdx: 内置变量,变量中包含的值就是当前执行设备代码的线程索引;用于说明当前thread在block中的位置;如果线程是一维的可获取threadIdx.x,如果是二维的还可获取threadIdx.y,如果是三维的还可获取threadIdx.z;为uint3类型,包含了一个thread在block中各个维度的索引信息 */// calculate the starting index and the offset to the next block that each thread will be processingint i = threadIdx.x + blockIdx.x * blockDim.x;int stride = blockDim.x * gridDim.x;while (i < length) {/* atomicAdd: 原子操作,底层硬件将确保当执行这些原子操作时,其它任何线程都不会读取或写入地址addr上的值。原子函数(atomicfunction)对位于全局或共享存储器的一个32位或64位字执行read-modify-write的原子操作。也就是说,当多个线程同时访问全局或共享存储器的同一位置时,保证每个线程能够实现对共享可写数据的互斥操作:在一个操作完成之前,其它任何线程都无法访问此地址。之所以将这一过程称为原子操作,是因为每个线程的操作都不会影响到其它线程。换句话说,原子操作能够保证对一个地址的当前操作完成之前,其它线程都不能访问这个地址。atomicAdd(addr,y):将生成一个原子的操作序列,这个操作序列包括读取地址addr处的值,将y增加到这个值,以及将结果保存回地址addr。 */atomicAdd(&temp[data[i]], 1);i += stride;}// sync the data from the above writes to shared memory then add the shared memory values to the values from// the other thread blocks using global memory atomic adds same as before, since we have 256 threads,// updating the global histogram is just one write per thread!__syncthreads();// 将每个线程块的直方图合并为单个最终的直方图atomicAdd(&(hist[threadIdx.x]), temp[threadIdx.x]);
}int calculate_histogram_gpu(const unsigned char* data, int length, size_t* hist, size_t& value, float* elapsed_time)
{/* cudaEvent_t: CUDA event types,结构体类型, CUDA事件,用于测量GPU在某个任务上花费的时间,CUDA中的事件本质上是一个GPU时间戳,由于CUDA事件是在GPU上实现的,因此它们不适于对同时包含设备代码和主机代码的混合代码计时 */cudaEvent_t start, stop;// cudaEventCreate: 创建一个事件对象,异步启动cudaEventCreate(&start);cudaEventCreate(&stop);// cudaEventRecord: 记录一个事件,异步启动,start记录起始时间cudaEventRecord(start, 0);unsigned char* dev_buffer{ nullptr };size_t* dev_hist{ nullptr };// cudaMalloc: 在设备端分配内存cudaMalloc(&dev_buffer, length);cudaMalloc(&dev_hist, 256 * sizeof(size_t));/* cudaMemcpy: 在主机端和设备端拷贝数据,此函数第四个参数仅能是下面之一:(1). cudaMemcpyHostToHost: 拷贝数据从主机端到主机端(2). cudaMemcpyHostToDevice: 拷贝数据从主机端到设备端(3). cudaMemcpyDeviceToHost: 拷贝数据从设备端到主机端(4). cudaMemcpyDeviceToDevice: 拷贝数据从设备端到设备端(5). cudaMemcpyDefault: 从指针值自动推断拷贝数据方向,需要支持统一虚拟寻址(CUDA6.0及以上版本)cudaMemcpy函数对于主机是同步的 */cudaMemcpy(dev_buffer, data, length, cudaMemcpyHostToDevice);/* cudaMemset: 存储器初始化函数,在GPU内存上执行。用指定的值初始化或设置设备内存 */cudaMemset(dev_hist, 0, 256 * sizeof(size_t));// cudaDeviceProp: cuda设备属性结构体// kernel launch - 2x the number of mps gave best timingcudaDeviceProp prop;// cudaGetDeviceProperties: 获取GPU设备相关信息cudaGetDeviceProperties(&prop, 0);// cudaDeviceProp::multiProcessorCount: 设备上多处理器的数量int blocks = prop.multiProcessorCount;fprintf(stderr, "multiProcessorCount: %d\n", blocks);/* <<< >>>: 为CUDA引入的运算符,指定线程网格和线程块维度等,传递执行参数给CUDA编译器和运行时系统,用于说明内核函数中的线程数量,以及线程是如何组织的;尖括号中这些参数并不是传递给设备代码的参数,而是告诉运行时如何启动设备代码,传递给设备代码本身的参数是放在圆括号中传递的,就像标准的函数调用一样;不同计算能力的设备对线程的总数和组织方式有不同的约束;必须先为kernel中用到的数组或变量分配好足够的空间,再调用kernel函数,否则在GPU计算时会发生错误,例如越界等;使用运行时API时,需要在调用的内核函数名与参数列表直接以<<<Dg,Db,Ns,S>>>的形式设置执行配置,其中:Dg是一个dim3型变量,用于设置grid的维度和各个维度上的尺寸.设置好Dg后,grid中将有Dg.x*Dg.y个block,Dg.z必须为1;Db是一个dim3型变量,用于设置block的维度和各个维度上的尺寸.设置好Db后,每个block中将有Db.x*Db.y*Db.z个thread;Ns是一个size_t型变量,指定各块为此调用动态分配的共享存储器大小,这些动态分配的存储器可供声明为外部数组(extern __shared__)的其他任何变量使用;Ns是一个可选参数,默认值为0;S为cudaStream_t类型,用于设置与内核函数关联的流.S是一个可选参数,默认值0. */// 当线程块的数量为GPU中处理器数量的2倍时,将达到最优性能calculate_histogram << <blocks * 2, 256 >> >(dev_buffer, length, dev_hist);cudaMemcpy(hist, dev_hist, 256 * sizeof(size_t), cudaMemcpyDeviceToHost);value = 0;for (int i = 0; i < 256; ++i) {value += hist[i];}// cudaFree: 释放设备上由cudaMalloc函数分配的内存cudaFree(dev_buffer);cudaFree(dev_hist);// cudaEventRecord: 记录一个事件,异步启动,stop记录结束时间cudaEventRecord(stop, 0);// cudaEventSynchronize: 事件同步,等待一个事件完成,异步启动cudaEventSynchronize(stop);// cudaEventElapseTime: 计算两个事件之间经历的时间,单位为毫秒,异步启动cudaEventElapsedTime(elapsed_time, start, stop);// cudaEventDestroy: 销毁事件对象,异步启动cudaEventDestroy(start);cudaEventDestroy(stop);return 0;
}
执行结果如下:可见使用C++和CUDA实现的结果是完全一致的:GitHub:https://github.com/fengbingchun/CUDA_Test
相关文章:

glusterfs基本操作
基本操作 集群节点 扩展集群 1,必须做hosts域名解析其实通过IP地址也能做集群,但是不建议这种方式. 192.168.1.210 glusterfs04 2, 添加节点到集群中,在当前所有集群节点中都需要执行 gluster peer probe glusterfs04 3,查看对等状态 gluster peer status 查看集群节点信息 gl…

100多次竞赛后,他研发了一个几乎可以解决所有机器学习问题的框架
(图片由AI科技大本营付费下载自视觉中国)作者 | XI YANG来源 | 知乎(机器学习之路)一个叫 Abhishek Thakur 的数据科学家,在他的 Linkedin 发表了一篇文章 Approaching (Almost) Any Machine Learning Problem…

mysql中char与varchar的区别分析(补充一句,int和integer没区别)
转自:http://www.jb51.net/article/23575.htm 在mysql教程中char与varchar的区别呢,都是用来存储字符串的,只是他们的保存方式不一样罢了,char有固定的长度,而varchar属于可变长的字符类型。har与varchar的区别 &#…
CUDA Samples: Streams' usage
以下CUDA sample是分别用C和CUDA实现的流的使用code,并对其中使用到的CUDA函数进行了解说,code参考了《GPU高性能编程CUDA实战》一书的第十章,各个文件内容如下:funset.cpp:#include "funset.hpp" #include <random&…

你的神经网络不起作用的37个理由
(图片由AI科技大本营付费下载自视觉中国)作者 | Slav Ivanov译者 | 吴金笛校对 | 丁楠雅、林亦霖编辑 | 王菁来源 | 数据派THU(ID:DatapiTHU)【导语】本文列举了在搭建神经网络过程中的37个易错点,并给出了…

菜鸟Vue学习笔记(三)
菜鸟Vue学习笔记(三)本周使用了Vue来操作表单,接下来说下Vue中双向绑定表单元素的用法。Vue中双向绑定是使用的v-model,所谓的双向绑定即改变变量的值,表单元素的值也会改变,同样的,改变表单元素…

Python中的注释(转)
一、单行注释单行注释以#开头,例如:print 6 #输出6二、多行注释(Python的注释只有针对于单行的注释(用#),这是一种变通的方法)多行注释用三引号将注释括起来,例如:多行注释多行注释三…
CUDA Samples: dot product(使用零拷贝内存)
以下CUDA sample是分别用C和CUDA实现的点积运算code,CUDA包括普通实现和采用零拷贝内存实现两种,并对其中使用到的CUDA函数进行了解说,code参考了《GPU高性能编程CUDA实战》一书的第十一章,各个文件内容如下:funset.cp…

一文读懂线性回归、岭回归和Lasso回归
(图片由AI科技大本营付费下载自视觉中国)作者 | 文杰编辑 | yuquanle本文介绍线性回归模型,从梯度下降和最小二乘的角度来求解线性回归问题,以概率的方式解释了线性回归为什么采用平方损失,然后介绍了线性回归中常用的…

tf.matmul / tf.multiply
import tensorflow as tfimport numpy as np 1.tf.placeholder placeholder()函数是在神经网络构建graph的时候在模型中的占位,此时并没有把要输入的数据传入模型,它只会分配必要的内存。 等建立session,在会话中,运行模型的时候通…

Java 匿名类也能使用构造函数
为什么80%的码农都做不了架构师?>>> 匿名类虽然没有名字,但可以有一个初始化块来充当构造函数。 public enum Ops {ADD, SUB} public class Calculator { private int i, j, result; public Calculator() {} public Calculator(int _i, …
CUDA Samples: matrix multiplication(C = A * B)
以下CUDA sample是分别用C和CUDA实现的两矩阵相乘运算code即C A*B,CUDA中包含了两种核函数的实现方法,第一种方法来自于CUDA Samples\v8.0\0_Simple\matrixMul,第二种采用普通的方法实现,第一种方法较快,但有些复杂&am…

业界首个实时多目标跟踪系统开源
(图片由AI科技大本营付费下载自视觉中国)作者 | CV君来源 | 我爱计算机视觉(ID:aicvml)相对业界研究比较多的单目标跟踪,多目标跟踪(Multi-Object Tracking,MOT)系统在实…

python基础 练习题
【练习题1】实现一个整数加法计算器如 content input(">>> ") # 59 , 64 count0 while 1:contentinput(>>>)s1 content.split()print(s1)count 0for i in s1:count int(i)print(count) 【练习题2】请编写1 - 100 所有数的和 sum0 for i in r…

[再寄小读者之数学篇](2014-04-18 from 352558840@qq.com [南开大学 2014 年高等代数考研试题]二次型的零点)...
(2014-04-18 from 352558840qq.com [南开大学 2014 年高等代数考研试题]) 设 ${\bf A}$ 为实对称矩阵, 存在线性无关的向量 ${\bf x}_1,{\bf x}_2$, 使得 ${\bf x}_1^T{\bf A}{\bf x}_1>0$, ${\bf x}_2^T{\bf A}{\bf x}_2<0$. 证明: 存在线性无关的向量 ${\bf x}_3,{\bf …

从0到1详解推荐系统中的嵌入方法,原理、算法到应用都讲明白了
(图片由AI科技大本营付费下载自视觉中国)作者丨gongyouliu编辑丨lily来源 | 大数据与人工智能(ID:)前言作者曾在这篇文章中提到,矩阵分解算法是一类嵌入方法,通过将用户行为矩阵分解为用户特征矩…

iOS-Swift中的递增(++)和递减(--)被取消的原因-官方答复
众所周知,在很多编程语言中,对一个变量递增1用,递减1用--,在Swift3之前也是可以这么用的,但之后被取消了。 所以在目前Swift5的版本中,只能用1和-1来进行递增和递减了 如果坚持用或--将会提示以下错误&…
CUDA Samples: 获取设备属性信息
通过调用CUDA的cudaGetDeviceProperties函数可以获得指定设备的相关信息,此函数会根据GPU显卡和CUDA版本的不同得到的结果也有所差异,下面code列出了经常用到的设备信息:#include "funset.hpp" #include <iostream> #include…

apache代理模块proxy使用
1、安装proxy模块[rootlocalhost modules]# cd /usr/local/src/httpd-2.2.16 [rootlocalhost httpd-2.2.16]# cd modules [rootlocalhost modules]# ls aaa config5.m4 debug filters ldap Makefile.in NWGNUmakefile ssl arch database echo …
CUDA Samples: image normalize(mean/standard deviation)
以下CUDA sample是分别用C和CUDA实现的通过均值和标准差对图像进行类似归一化的操作,并对其中使用到的CUDA函数进行了解说,各个文件内容如下:关于均值和标准差的计算公式可参考: http://blog.csdn.net/fengbingchun/article/detai…

中文预训练ALBERT模型来了:小模型登顶GLUE,Base版模型小10倍、速度快1倍
(图片由AI科技大本营付费下载自视觉中国)作者 | 徐亮(实在智能算法专家) 来源 | AINLP(ID:nlpjob)谷歌ALBERT论文刚刚出炉一周,中文预训练ALBERT模型来了,感兴趣的同学可以直接尝鲜试…

树莓派安装go
简介 大学的时候在使用openfalcon的时候讲过这个东西,但是那时候是介绍open-falcon的,所以感觉不是很具体,所以今天在安装frp的时候也碰到了这个问题,我就具体的说下 安装go1.4 编译最新版本的go的时候一定要先编译安装go1.4&…

设计模式中的原则
设计模式(详情click)这个术语是由Erich Gamma等人在1990年代从建筑设计领域引入到计算机科学的。它是对软件设计中普遍存在(反复出现)的各种问题,所提出的解决方案。 设计模式并不直接用来完成代码的编写,而是描述在各种不同情况下…
CUDA Samples: approximate image reverse
以下CUDA sample是分别用C和CUDA实现的对图像进行某种类似reverse的操作,并对其中使用到的CUDA函数进行了解说,各个文件内容如下:common.hpp:#ifndef FBC_CUDA_TEST_COMMON_HPP_ #define FBC_CUDA_TEST_COMMON_HPP_#include<random> #i…

超详细支持向量机知识点,面试官会问的都在这里了
(图片付费下载自视觉中国)作者 | 韦伟来源 | 知乎导语:持续准备面试中,准备的过程中,慢慢发现,如果死记硬背的话很难,可当推导一遍并且细细研究里面的缘由的话,面试起来应该什么都不…

vue-router点击切换路由报错
报错: 报错原因: 设置mode:history解决方法: 将router的mode设置为‘hash就不报错了 原因下次再分析?

gvim配置相关
用 vundle 来管理 vim 插件(包含配置文件vimrc和gvimrc) gvim插件管理神器:vundle的安装与使用 Vim插件管理Vundle Linux 下VIM的配置 Vim配置系列(一) ---- 插件管理 Vim配置系列(二) —- 好看的statusline vim优秀插件整理 一些有用的 VIM …

深度学习有哪些接地气又好玩的应用?
过去几年中,深度学习中的很多技术如计算机视觉、自然语言处理等被应用在很多实际问题中,而且相关成果也表明深度学习能让人们的工作效果比以前更好。我们收集了一些深度学习方面的创意应用,虽然没有对每项应用进行详尽描述,但是希…

Ubuntu下通过CMake文件编译CUDA+OpenCV代码操作步骤
在 CUDA_Test 工程中,CUDA测试代码之前仅支持在Windows10 VS2013编译,今天在Ubuntu 14.04下写了一个CMakeLists.txt文件,支持在Linux下也可以通过CMake编译CUDA_Test工程,CMakeLists.txt文件内容如下:# CMake file f…

JAVA 多用户商城系统b2b2c-Spring Cloud常见问题与总结(一)
在使用Spring Cloud的过程中,难免会遇到一些问题。所以对Spring Cloud的常用问题做一些总结。需要JAVA Spring Cloud大型企业分布式微服务云构建的B2B2C电子商务平台源码 一零三八七七四六二六 一、Eureka常见问题 1.1 Eureka 注册服务慢 默认情况下,服务…