CUDA学习(六)之使用共享内存(shared memory)进行归约求和(M个包含N个线程的线程块)

https://www.cnblogs.com/xiaoxiaoyibu/p/11402607.html中介绍了使用一个包含N个线程的线程块和共享内存进行数组归约求和,

基本思路:

  定义M个包含N个线程的线程块时(NThreadX = ((NX + ThreadX - 1) / ThreadX)),全局线程索引需使用tid = blockIdx.x * blockDim.x + threadIdx.x,而在每个线程块中局部线程索引是i = threadIdx.x,

每个线程块只计算一部分求和,求和结果保存在该线程块中的共享内存数组0号元素中,线程结束后将该值赋给对应全局数组(blockIdx.x * blockDim.x)元素中,最后在CPU端使用循环将每个线程块所求和相加,即得到最后结果。

代码如下:

#pragma once
#include "cuda_runtime.h"
#include "device_launch_parameters.h"
#include "device_functions.h"

#include <iostream>

using namespace std;
const int NX = 10240;            //数组长度
const int ThreadX = 256;        //线程块大小
//使用shared memory和多个线程块
__global__ void d_SharedMemoryTest(double *para)
{
    int i = threadIdx.x;                                    //该线程块中线程索引
    int tid = blockIdx.x * blockDim.x + threadIdx.x;        //M个包含N个线程的线程块中相对应全局内存数组的索引(全局线程)

    __shared__ double s_Para[ThreadX];                        //定义固定长度(线程块长度)的共享内存数组
    if (tid < NX)                                            //判断全局线程小于整个数组长度NX,防止数组越界
        s_Para[i] = para[tid];                                //将对应全局内存数组中一段元素的值赋给共享内存数组
    __syncthreads();                                         //(红色下波浪线提示由于VS不识别,不影响运行)同步,等待所有线程把自己负责的元素载入到共享内存再执行下面代码

    for (int index = 1; index < blockDim.x; index *= 2)        //归约求和
    {
        __syncthreads();
        if (i % (2 * index) == 0)
        {
            s_Para[i] += s_Para[i + index];
        }
    }

    if (i == 0)                                                //求和完成,总和保存在共享内存数组的0号元素中
        para[blockIdx.x * blockDim.x + i] = s_Para[i];        //在每个线程块中,将共享内存数组的0号元素赋给全局内存数组的对应元素,即线程块索引*线程块维度+i(blockIdx.x * blockDim.x + i)

}

//使用shared memory和多个线程块
void s_ParallelTest()
{
    double *Para;
    cudaMallocManaged((void **)&Para, sizeof(double) * NX);        //统一内存寻址,CPU和GPU都可以使用

    double ParaSum = 0;
    for (int i = 0; i<NX; i++)
    {
        Para[i] = (i + 1) * 0.01;                        //数组赋值
        ParaSum += Para[i];                                //CPU端数组累加
    }

    cout << " CPU result = " << ParaSum << endl;        //显示CPU端结果
    double d_ParaSum;

    int NThreadX = ((NX + ThreadX - 1) / ThreadX);
    cout << " 线程块大小 :" << ThreadX << "                线程块数量 :" << NThreadX << endl;

    d_SharedMemoryTest << < NThreadX, ThreadX >> > (Para);                //调用核函数(M个包含N个线程的线程块)

    cudaDeviceSynchronize();                            //同步

    for (int i=0; i<NThreadX; i++)
    {
        d_ParaSum += Para[i*ThreadX];                    //将每个线程块相加求的和(保存在对应全局内存数组中)相加求和
    }

    cout << " GPU result = " << d_ParaSum << endl;        //显示GPU端结果

}

int main() {

    s_ParallelTest();

    system("pause");
    return 0;
}

结果如下(CPU和GPU结果一致):

原文地址:https://www.cnblogs.com/xiaoxiaoyibu/p/11403003.html

时间: 2024-08-02 21:36:58

CUDA学习(六)之使用共享内存(shared memory)进行归约求和(M个包含N个线程的线程块)的相关文章

python学习笔记——多进程中共享内存Value &amp; Array

1 共享内存 基本特点: (1)共享内存是一种最为高效的进程间通信方式,进程可以直接读写内存,而不需要任何数据的拷贝. (2)为了在多个进程间交换信息,内核专门留出了一块内存区,可以由需要访问的进程将其映射到自己的私有地址空间.进程就可以直接读写这一块内存而不需要进行数据的拷贝,从而大大提高效率.(文件映射) (3)由于多个进程共享一段内存,因此也需要依靠某种同步机制. 优缺点: 优点:快速在进程间传递数据 缺点: 数据安全上存在风险,内存中的内容会被其他进程覆盖或 者篡改 注: 经常和同步互斥

Linux进程间通信--进程,信号,管道,消息队列,信号量,共享内存

Linux进程间通信--进程,信号,管道,消息队列,信号量,共享内存 参考:<linux编程从入门到精通>,<Linux C程序设计大全>,<unix环境高级编程> 参考:C和指针学习 说明:本文非常的长,也是为了便于查找和比较,所以放在一起了 Linux 传统的进程间通信有很多,如各类管道.消息队列.内存共享.信号量等等.但它们都无法介于内核态与用户态使用,原因如表 通信方法 无法介于内核态与用户态的原因 管道(不包括命名管道) 局限于父子进程间的通信. 消息队列 在

管道,消息队列,共享内存!

2015.1.26星期一,阴天 linux中使用的较多的进程通信方式主要有一下几种:1.管道(Pipe):管道可用于具有亲缘关系进程间的通信,有名管道,除了具有管道所具有功能外,它 还允许无亲缘关系进程的通信2.信号(signal):信号是在软件层次上对中断机制的一种模拟,它是比较复杂的通信方式,用于通知进程 有某事发生,一个进程收到一个信号与处理器收到一个中断请求效果上可以说是一样的3.消息队列:(Messge Queue):消息队列是消息的连接表,包括Posix消息队列SystemV消息队列

进程间的八种通信方式----共享内存是最快的 IPC 方式

1.无名管道( pipe ):管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用.进程的亲缘关系通常是指父子进程关系. 2.高级管道(popen):将另一个程序当做一个新的进程在当前程序进程中启动,则它算是当前程序的子进程,这种方式我们成为高级管道方式. 3.有名管道 (named pipe) : 有名管道也是半双工的通信方式,但是它允许无亲缘关系进程间的通信. 4.消息队列( message queue ) : 消息队列是由消息的链表,存放在内核中并由消息队列标识

Linux共享内存(一)

inux系统编程我一直看 <GNU/LINUX编程指南>,只是讲的太简单了,通常是书和网络上的资料结合着来掌握才比较全面 .在掌握了书上的内容后,再来都其他资料 . 原文链接 http://www.cnblogs.com/skyme/archive/2011/01/04/1925404.html 共享内存是系统出于多个进程之间通讯的考虑,而预留的的一块内存区.在/proc/sys/kernel/目录下,记录着共享内存的一些限制,如一个共享内存区的最大字节数shmmax,系统范围内最大共享内存区

Posix与System V共享内存函数区别

Posix标准shm_open:打开或创建一个共享内存区shm_unlink:删除一个共享内存区ftruncate:调整文件或共享内存区大小sem_open:创建信号量sem_wait:等待信号量sem_post:发送信号量sem_close:关闭信号量 System V标准ftok:生成keyshmget:建立共享内存shmdt:删除共享内存区semget:创建信号量semop:操作信号量semctl:控制信号量 简单解释一下ipcs命令和ipcrm命令.取得ipc信息:ipcs [-m|-q

进程间通信IPC之--共享内存

每个进程各自有不同的用户地址空间,任何一个进 程的全局变量在另一个进程中都看不到,所以进程之间要交换数据必须通过内核,在内核中开辟一块缓冲 区,进程1把数据从用户空间拷到内核缓冲区,进程2再从内核缓冲区把数据读走,内核提供的这种机制称为进程间通信(IPC,InterProcess Communication) 如下图所示: 进程间通信共七种方式: 第一类:传统的unix通信机制: # 管道( pipe ):管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用.进程的亲

Linux进程间通信:管道,信号量,消息队列,信号,共享内存,套接字

Linux下的进程通信手段基本上是从UNIX平台上的进程通信手段继承而来的.而对UNIX发展做出重大贡献的两大主力AT&T的贝尔实验室及BSD(加州大学伯克利分校的伯克利软件发布中心)在进程间的通信方面的侧重点有所不同.前者是对UNIX早期的进程间通信手段进行了系统的改进和扩充,形成了“system V IPC”,其通信进程主要局限在单个计算机内:后者则跳过了该限制,形成了基于套接口(socket)的进程间通信机制.而Linux则把两者的优势都继承了下来 linux进程间通信(IPC)有几种方式

unix/linux共享内存应用与陷阱

unix/linux共享内存应用与陷阱 (2012-06-12 14:32) 标签:  linux  内存  分类: linux应用  共享内存是系统出于多个进程之间通讯的考虑,而预留的的一块内存区.在/proc/sys/kernel/目录下,记录着共享内存的一些限制,如一个共享内存区的最大字节数shmmax,系统范围内最大共享内存区标识符数shmmni等,可以手工对其调整,但不推荐这样做.一.应用共享内存的使用,主要有以下几个API:ftok().shmget().shmat().shmdt(

CUDA学习(五)之使用共享内存(shared memory)进行归约求和

共享内存(shared memory)是位于SM上的on-chip(片上)一块内存,每个SM都有,就是内存比较小,早期的GPU只有16K(16384),现在生产的GPU一般都是48K(49152). 共享内存由于是片上内存,因而带宽高,延迟小(较全局内存而言),合理使用共享内存对程序效率具有很大提升. 下面是使用共享内存对一个数组进行求和,使用全局内存进行归约求和可以浏览https://www.cnblogs.com/xiaoxiaoyibu/p/11397205.html #pragma on