CUDA入门(七)流

程序通过流来管理并发,每个流按顺序执行操作,不同流之间可能并行也可能乱序执行。流的作用是使一个流的计算与另一个流的传输同时进行,能有效提高对GPU的利用率。
流的定义方法,是创建一个cudaStream_t对象,并在启动内核和进行memcpy时将该对象作为参数传入。
在运用流时必然会用到异步执行,异步执行就是CPU主机端的API函数和内核函数启动的结束和GPU端真正完成这些操作是异步的。通过函数的异步,CPU可以在GPU端进行运算或数据传输时进行其他操作,更加有效地利用系统中的计算资源。
在用流进行处理时,流处理的对象必须为pinned memory才能实现并行;
流处理有几个API函数:
cudaStreamCreate,创建流;
cudaStreamDestory,释放流;
cudaStreamQuery,查询流完成的状态,所有都完成返回cudaSuccess;否则返回cudaErrorNotReady;
cudaStreamSynchronize,等待所有流任务的完成;
流在CUDASDK中有一个简单的例子samplestream:

#include<stdlib.h>
#include "cuda_runtime.h"
#include "device_launch_parameters.h"
#include <stdio.h>
#include<string.h>

__global__ void init_array(int *g_data, int *factor, int num_iterations)
{
    int idx = blockIdx.x*blockDim.x + threadIdx.x;
    for (int i = 0; i < num_iterations; i++)
        g_data[idx] += *factor;
}

int correct_data(int *a, const int n, const int c)
{
    for (int i = 0; i < n; i++)
    {
        if (a[i] != c)
        {
            printf("%d:%d %d\n", i, a[i], c);
            return 0;
        }

    }
}

int main(int argc, char *argv[])
{
    int CUDA_device = 0;
    int nstream = 4;
    int nreps = 10;
    int n = 6 * 1024 * 1024;
    int nbytes = n*sizeof(int);
    dim3 threads, blocks;
    float elapsed_time, time_memcpy, time_kernel;

    int niterations;
    int num_devices = 0;
    cudaGetDeviceCount(&num_devices);

    cudaSetDevice(CUDA_device);
    cudaDeviceProp device_properties;
    cudaGetDeviceProperties(&device_properties, CUDA_device);
    niterations = 5;

    printf("running on: %s\n\n", device_properties.name);

    int c = 5;
    int *a = 0;
    cudaMallocHost((void**)&a, nbytes);
    int *d_a = 0, *d_c = 0;
    cudaMalloc((void**)&d_a, nbytes);
    cudaMalloc((void**)&d_c, sizeof(int));
    cudaMemcpy(d_c, &c, sizeof(int), cudaMemcpyHostToDevice);

    cudaStream_t *streams = (cudaStream_t *)malloc(nstream* sizeof(cudaStream_t));
    for (int i = 0; i < nstream; i++)
    {
        cudaStreamCreate(&(streams[i]));

    }
    cudaEvent_t start_event, stop_event;
    cudaEventCreate(&start_event);
    cudaEventCreate(&stop_event);
    cudaEventRecord(start_event, 0);
    cudaMemcpyAsync(a, d_a, nbytes, cudaMemcpyDeviceToHost, streams[0]);
    cudaEventRecord(stop_event, 0);
    cudaEventSynchronize(stop_event);
    cudaEventElapsedTime(&time_memcpy, start_event, stop_event);
    printf("memcpy:\t%.2f\n", time_memcpy);

    threads = dim3(512, 1);
    blocks = dim3(n / threads.x, 1);
    cudaEventRecord(start_event, 0);
    init_array << <blocks, threads, 0, streams[0] >> >(d_a, d_c, niterations);
    cudaEventRecord(stop_event, 0);
    cudaEventSynchronize(stop_event);
    cudaEventElapsedTime(&time_kernel, start_event, stop_event);
    printf("kernel:\t\t%.2f\n", time_kernel);

    threads = dim3(512, 1);
    blocks = dim3(n / threads.x, 1);
    cudaEventRecord(start_event, 0);
    for (int i = 0; i < nreps; i++)
    {
        init_array << <blocks, threads >> >(d_a, d_c, niterations);
        cudaMemcpy(a, d_a, nbytes, cudaMemcpyDeviceToHost);

    }
    cudaEventRecord(stop_event, 0);
    cudaEventSynchronize(stop_event);
    cudaEventElapsedTime(&elapsed_time, start_event, stop_event);
    printf("non-stream:\t%.2f(%.2f expected)\n", elapsed_time / nreps, time_kernel + time_memcpy);

    threads = dim3(512, 1);
    blocks = dim3(n / (nstream*threads.x), 1);
    memset(a, 255, nbytes);
    cudaMemset(d_a, 0, nbytes);
    cudaEventRecord(start_event, 0);

    for (int k = 0; k < nreps; k++)
    {
        for (int i = 0; i < nstream; i++)
            init_array << <blocks, threads, 0, streams[i] >> >(d_a + i*n / nstream, d_c, niterations);
        //printf("1");
        for (int i = 0; i < nstream; i++)
            cudaMemcpyAsync(a + i*n / nstream, d_a + i*n / nstream, nbytes / nstream, cudaMemcpyDeviceToHost, streams[i]);

    }
    cudaEventRecord(stop_event, 0);
    cudaEventSynchronize(stop_event);
    cudaEventElapsedTime(&elapsed_time, start_event, stop_event);
    printf("%d streams:\t%.2f\n", nstream, elapsed_time / nreps);

    printf("--------------------------------\n");
    if (correct_data(a, n, c*nreps*niterations))
        printf("TEST PASSED\n");
    else
        printf("TEST FAILED\n");
    for (int i = 0; i < nstream; i++)
    {
        cudaStreamDestroy(streams[i]);

    }
    cudaEventDestroy(start_event);
    cudaEventDestroy(stop_event);
    cudaFreeHost(a);
    cudaFree(d_a);
    cudaFree(d_c);
    getchar();
    cudaThreadExit();

}

执行结果如下图所示:

第一个时间memcopy表示单次拷贝所需的时间;
第二个表示kernel函数进行计算所用的时间;
第三个non-streamed表示在不使用流的情况下计算与传输的时间;
第四个表示在使用n个流的情况下计算与传输所用的时间;
能看到用减少时间的效果。

时间: 2024-09-21 21:39:30

CUDA入门(七)流的相关文章

Windows 8风格应用开发入门 七 页面视图概览

Windows 8风格应用中包含哪些视图 常用的几种视图包括: 开发入门 七 页面视图概览-页面视图"> 1. FullScreenLandscape(水平方向全屏视图) 2. Filled(填充视图) 3. Snap view(贴靠视图) 4. FullScreenPortrait(竖直方向全屏视图) 最小视图状态分辨率支持1024*768,全屏视图状态分辨率为1366*768以上. 注意:实现贴靠视图的最小分辨率为1366*768. Visual Studio 2012和模拟器中如何开

AppleWatch开发入门七——watchOS中通知的应用

AppleWatch开发入门七--watchOS中通知的应用 一.引言         在iOS系统中,支持的通知有两种类型:本地通知和远程通知.本地通知多用于计时类通知,远程的又称推送,多用于一些提示动态的提示信息.这里有相关通知的一些知识总结: 本地推送:http://my.oschina.net/u/2340880/blog/405491. 远程推送:http://my.oschina.net/u/2340880/blog/413584.         在watch中,通知是和iphon

CUDA入门(四)Visual Profiler

Visual Profiler 在CUDA程序开发的过程中是一个极有利的工具.可以帮助你找到程序中性能不足之处. 下面是<CUDA_Profiler_Users_Guide>对Visual Profiler描述 Visual Profiler 是是一个图形化的剖析工具,可以显示你的应用程序中CPU和GPU的活动情况,利用分析引擎帮助你寻找优化的机会. 其实除了可视化的界面,NVIDIA提供了命令行方式的剖析命令:nvprof.对于初学者,使用图形化的方式比较容易上手,所以本节使用Visual

CUDA入门(一)

CUDA环境的配置 在很多大数据处理.图像处理等环境下时,串行运算已经满足不了对速度的需求,这时就需要运用并行运算来提高运算的效率. CUDA(Compute Unified Device Architecture),是显卡厂商NVIDIA推出的运算平台,也是典型的并行运算所用的工具之一(毕竟NVIDIA是显卡界的老大,听说AMD显卡也开始支持CUDA). 首先先进行CUDA的程序的安装: 这里笔者用VS2013+CUDA7.5 (1):VS2013下载:http://192.168.155.1

CUDA入门(三) 初探线程与块

在配置GPU时一般都看重其的架构,流处理器数,以及显存数. 以英伟达的GPU为例架构一般以科学家的名字来命名,如Fermi(费米),Kepler(开普勒),现在主流的Maxwell(麦克斯韦),Pascal(帕斯卡),不同的架构主要体现在如纹理单元,流处理器,带宽等较为底层的东西不同,为线程与块中主要关心的是其流多处理器(streaming multiprocessor,SM)以及一个流多处理器包含的多个流处理器(scalar processor,SP) 或称为CUDA核(CUDA core).

CUDA入门(二)cuda编程的基本知识与第一个cuda程序

多首先,先来了解一下GPU与CPU的区别,如图 可以看到CPU(Central Processing Unit,中央处理单元),由Control(控制台),ALU(Arithmetic Logic Unit,逻辑计算单元),Cache(高速缓存),而GPU(Graphic Processing Unit,图形处理单元)也是由相同的部件组成,但GPU的计算单元远比CPU多,这就决定了GPU适合大量简单,精度要求低的计算,CPU则适合复杂的,精度要求高的计算.(如果还不了解,可以回学校恶补一下微机原

Kinect for Windows SDK开发入门(七)骨骼追踪基础 下

上一篇文章用在UI界面上绘制骨骼数据的例子展示了骨骼追踪系统涉及的主要对象,然后详细讨论了骨骼追踪中所涉及的对象模型.但是了解了基本概念是一回事,能够建立一个完整的可用的应用程序又是另外一回事,本文通过介绍一个简单的Kinect游戏来详细讨论如何应用这些对象来建立一个完整的Kinect应用,以加深对Kinect骨骼追踪所涉及的各个对象的了解. 1. Kinect连线游戏 相信大家在小时候都做过一个数学题目,就是在纸上将一些列数字(用一个圆点表示)从小到大用线连起来.游戏逻辑很简单,只不过我们在这

A*寻路算法入门(七)

大熊猫猪·侯佩原创或翻译作品.欢迎转载,转载请注明出处. 如果觉得写的不好请告诉我,如果觉得不错请多多支持点赞.谢谢! hopy ;) 免责申明:本博客提供的所有翻译文章原稿均来自互联网,仅供学习交流之用,请勿进行商业用途.同时,转载时不要移除本申明.如产生任何纠纷,均与本博客所有人.发表该翻译稿之人无任何关系.谢谢合作! 一只没有远见的猫咪 在上面的例子中,我们看到猫咪在选择最短路径的时候,它总是选择最好的方块(在未来最短路径中的一块) - 就像他它是一只有远见的猫咪一样! 但是如果这只猫咪不

CUDA从入门到精通(七):流并行

前面我们没有讲程序的结构,我想有些童鞋可能迫不及待想知道CUDA程序到底是怎么一个执行过程.好的,这一节在介绍流之前,先把CUDA程序结构简要说一下. CUDA程序文件后缀为.cu,有些编译器可能不认识这个后缀的文件,我们可以在VS2008的Tools->Options->Text Editor->File Extension里添加cu后缀到VC++中,如下图:   一个.cu文件内既包含CPU程序(称为主机程序),也包含GPU程序(称为设备程序).如何区分主机程序和设备程序?根据声明,