CUDA By Examples 10 - Streams

一. Page-locked Host Memory

  1. 使用cudaHostAlloc()为Host分配内存属于页锁定内存, 这种内存不会被OS转移到disk上, 只能存在于物理内存中. 访问更安全. 但是只使用物理内存会使内存占用比例升高, 影响程序性能.
  2. 使用Page-locked Host Memory可以提高Host和GPU间的数据传输速度.
  3. 多用在cudaMemcpy的src或者dst上.
  4. Page-locked Host Memory要及时使用cudaFreeHost释放.

用不同内存分配方式测试数据传输速度:
(辅助代码见: http://blog.csdn.net/full_speed_turbo/article/details/71107132)

#include "../common/book.h"

#define SIZE    (100*1024*1024)

float cuda_malloc_test( int size, bool up)
{
    cudaEvent_t start, stop;
    int *a, *dev_a;
    float elapsedTime;

    HANDLE_ERROR( cudaEventCreate( &start ) );
    HANDLE_ERROR( cudaEventCreate( &stop ) );

    HANDLE_ERROR( cudaMalloc( (void**)&dev_a, size*sizeof(int) ) );
    a = (int*)malloc(size * sizeof(int) );
    HANDLE_NULL(a);

    HANDLE_ERROR( cudaEventRecord( start, 0) );

    for (int i=0; i<100; i++)
    {
        if (up)
        {
            HANDLE_ERROR( cudaMemcpy( dev_a, a, size*sizeof(int), cudaMemcpyHostToDevice) );
        } 
        else
        {
            HANDLE_ERROR( cudaMemcpy( a, dev_a, size*sizeof(int), cudaMemcpyDeviceToHost ) );
        }
    }

    HANDLE_ERROR( cudaEventRecord( stop, 0 ) );
    HANDLE_ERROR( cudaEventSynchronize( stop ) );
    HANDLE_ERROR( cudaEventElapsedTime( &elapsedTime, start, stop ) );

    HANDLE_ERROR( cudaEventDestroy( start ) );
    HANDLE_ERROR( cudaEventDestroy( stop ) );
    HANDLE_ERROR( cudaFree(dev_a) );
    free(a);

    return elapsedTime;
}

float cuda_alloc_host_test(int size, bool up)
{
    cudaEvent_t start, stop;
    int *a, *dev_a;
    float elapsedTime;

    HANDLE_ERROR( cudaEventCreate( &start ) );
    HANDLE_ERROR( cudaEventCreate( &stop ) );

    HANDLE_ERROR( cudaMalloc( (void**)&dev_a, size*sizeof(int) ) );
    HANDLE_ERROR( cudaHostAlloc( (void**)&a, size *sizeof(int), cudaHostAllocDefault ) );

    HANDLE_ERROR( cudaEventRecord( start, 0 ) );

    for (int i=0; i<100; i++)
    {
        if (up)
        {
            HANDLE_ERROR( cudaMemcpy(dev_a, a, size*sizeof(int), cudaMemcpyHostToDevice ) );
        } 
        else
        {
            HANDLE_ERROR( cudaMemcpy(a, dev_a, size*sizeof(int), cudaMemcpyDeviceToHost ) );
        }
    }

    HANDLE_ERROR( cudaEventRecord( stop, 0 ) );
    HANDLE_ERROR( cudaEventSynchronize( stop ) );
    HANDLE_ERROR( cudaEventElapsedTime( &elapsedTime, start, stop ) );

    HANDLE_ERROR( cudaFreeHost( a ) );
    HANDLE_ERROR( cudaFree( dev_a ) );
    HANDLE_ERROR( cudaEventDestroy( start ) );
    HANDLE_ERROR( cudaEventDestroy( stop ) );

    return elapsedTime;
}

int main(void)
{
    float elapsedTime;
    float MB = (float)100*SIZE*sizeof(int)/1024/1024;
    elapsedTime = cuda_malloc_test( SIZE, true );
    printf( "Time using malloc: %3.1f ms\n", elapsedTime );
    printf( "\t MB/s during copy up: %3.1f\n", MB/(elapsedTime/1000) );

    elapsedTime = cuda_malloc_test( SIZE, false );
    printf( "Time using malloc: %3.1f ms\n", elapsedTime );
    printf( "\t MB/s during copy down: %3.1f\n", MB/(elapsedTime/1000) );

    elapsedTime = cuda_alloc_host_test( SIZE, true );
    printf( "Time using HostAlloc: %3.1f ms\n", elapsedTime );
    printf( "\t MB/s during copy up: %3.1f\n", MB/(elapsedTime/1000) );

    elapsedTime = cuda_alloc_host_test( SIZE, false );
    printf( "Time using HostAlloc: %3.1f ms\n", elapsedTime );
    printf( "\t MB/s during copy down: %3.1f\n", MB/(elapsedTime/1000) );

    return 0;

}

输出:
这里写图片描述

2. streams

  1. stream可以看做GPU的一个task队列.
  2. 使用的Host内存需要用cudaHostAlloc分配.
  3. stream操作需要cudaStreamSynchronize同步.
  4. 多个stream配合时, 安排任务时要使用广度优先准则. 不要一次性把一个stream安排满了再去安排另外的stream. 否则, 另外的stream会被阻塞.

使用深度优先准则安排stream工作队列:

#include "../common/book.h"

#define N (1024*1024)
#define FULL_DATA_SIZE (N*20)

__global__ void kernel( int *a, int *b, int *c)
{
    int idx = threadIdx.x + blockIdx.x * blockDim.x;
    if(idx < N)
    {
        int idx1 = (idx + 1) % 256;
        int idx2 = (idx + 2) % 256;
        float as = (a[idx] + a[idx1] + a[idx2]) / 3.0f;
        float bs = (b[idx] + b[idx1] + b[idx2]) / 3.0f;
        c[idx] = (as + bs) / 2;
    }
}

int main(void)
{
    cudaDeviceProp prop;
    int whichDevice;
    HANDLE_ERROR( cudaGetDevice( &whichDevice ) );
    HANDLE_ERROR( cudaGetDeviceProperties( &prop, whichDevice ) );

    if(!prop.deviceOverlap)
    {
        printf("Device will not handle overlaps, so no speed up from streams\n");
        return 0;
    }

    cudaEvent_t start, stop;
    float elapsedTime;

    //开启计时器
    HANDLE_ERROR( cudaEventCreate( &start ) );
    HANDLE_ERROR( cudaEventCreate( &stop ) );

    //初始化streams
    cudaStream_t stream0, stream1;
    HANDLE_ERROR( cudaStreamCreate( &stream0 ) );
    HANDLE_ERROR( cudaStreamCreate( &stream1 ) );

    //分配内存
    int *host_a, *host_b, *host_c;
    int *dev_a0, *dev_b0, *dev_c0;
    int *dev_a1, *dev_b1, *dev_c1;

    HANDLE_ERROR( cudaMalloc( (void**)&dev_a0, N*sizeof(int) ) );
    HANDLE_ERROR( cudaMalloc( (void**)&dev_b0, N*sizeof(int) ) );
    HANDLE_ERROR( cudaMalloc( (void**)&dev_c0, N*sizeof(int) ) );

    HANDLE_ERROR( cudaMalloc( (void**)&dev_a1, N*sizeof(int) ) );
    HANDLE_ERROR( cudaMalloc( (void**)&dev_b1, N*sizeof(int) ) );
    HANDLE_ERROR( cudaMalloc( (void**)&dev_c1, N*sizeof(int) ) );

    HANDLE_ERROR( cudaHostAlloc( (void**)&host_a, FULL_DATA_SIZE*sizeof(int), cudaHostAllocDefault ) );
    HANDLE_ERROR( cudaHostAlloc( (void**)&host_b, FULL_DATA_SIZE*sizeof(int), cudaHostAllocDefault ) );
    HANDLE_ERROR( cudaHostAlloc( (void**)&host_c, FULL_DATA_SIZE*sizeof(int), cudaHostAllocDefault ) );

    for (int i=0; i<FULL_DATA_SIZE; i++)
    {
        host_a[i] = rand();
        host_b[i] = rand();
    }

    //分段计算
    HANDLE_ERROR( cudaEventRecord( start, 0 ) );
    for (int i=0; i<FULL_DATA_SIZE; i+=N*2)
    {
        //先安排stream0.
        HANDLE_ERROR( cudaMemcpyAsync( dev_a0, host_a+i,
                                        N * sizeof(int),
                                        cudaMemcpyHostToDevice,
                                        stream0 ) );
        HANDLE_ERROR( cudaMemcpyAsync( dev_b0, host_b+i,
                                        N *sizeof(int),
                                        cudaMemcpyHostToDevice,
                                        stream0 ) );
        kernel<<<N/256, 256, 0, stream0>>>( dev_a0, dev_b0, dev_c0 );
        HANDLE_ERROR( cudaMemcpyAsync( host_c+i, dev_c0,
                                        N * sizeof(int),
                                        cudaMemcpyDeviceToHost,
                                        stream0 ) );
        //安排stream1.
        HANDLE_ERROR( cudaMemcpyAsync( dev_a1, host_a+i+N,
                                        N * sizeof(int),
                                        cudaMemcpyHostToDevice,
                                        stream1 ) );
        HANDLE_ERROR( cudaMemcpyAsync( dev_b1, host_b+i+N,
                                        N * sizeof(int),
                                        cudaMemcpyHostToDevice,
                                        stream1 ) );
        kernel<<<N/256,256,0,stream1>>>( dev_a1, dev_b1, dev_c1 );
        HANDLE_ERROR( cudaMemcpyAsync( host_c+i+N, dev_c1,
                                        N*sizeof(int),
                                        cudaMemcpyDeviceToHost,
                                        stream1 ) );
    }
    //同步streams.
    HANDLE_ERROR( cudaStreamSynchronize( stream0 ) );
    HANDLE_ERROR( cudaStreamSynchronize( stream1 ) );

    //获取时间
    HANDLE_ERROR( cudaEventRecord( stop, 0 ) );
    HANDLE_ERROR( cudaEventSynchronize( stop ) );
    HANDLE_ERROR( cudaEventElapsedTime( &elapsedTime,
                                        start,
                                        stop ) );
    printf( "Time taken: %3.1f ms\n", elapsedTime );

    //释放streams
    HANDLE_ERROR( cudaStreamDestroy( stream0 ) );
    HANDLE_ERROR( cudaStreamDestroy( stream1 ) );
    //释放Host内存
    HANDLE_ERROR( cudaFreeHost( host_a ) );
    HANDLE_ERROR( cudaFreeHost( host_b ) );
    HANDLE_ERROR( cudaFreeHost( host_c ) );
    //释放GPU内存
    HANDLE_ERROR( cudaFree( dev_a0 ) );
    HANDLE_ERROR( cudaFree( dev_b0 ) );
    HANDLE_ERROR( cudaFree( dev_c0 ) );
    HANDLE_ERROR( cudaFree( dev_a1 ) );
    HANDLE_ERROR( cudaFree( dev_b1 ) );
    HANDLE_ERROR( cudaFree( dev_c1 ) );

    return 0;

}

这里写图片描述
使用广度优先原则安排stream任务队列:

    //分段计算
    HANDLE_ERROR( cudaEventRecord( start, 0 ) );
    for (int i=0; i<FULL_DATA_SIZE; i+=N*2)
    {
        //安排stream0.
        HANDLE_ERROR( cudaMemcpyAsync( dev_a0, host_a+i,
                                        N * sizeof(int),
                                        cudaMemcpyHostToDevice,
                                        stream0 ) );
        //安排stream1.
        HANDLE_ERROR( cudaMemcpyAsync( dev_a1, host_a+i+N,
                                        N * sizeof(int),
                                        cudaMemcpyHostToDevice,
                                        stream1 ) );
        //安排stream0.
        HANDLE_ERROR( cudaMemcpyAsync( dev_b0, host_b+i,
                                        N *sizeof(int),
                                        cudaMemcpyHostToDevice,
                                        stream0 ) );
        //安排stream1.
        HANDLE_ERROR( cudaMemcpyAsync( dev_b1, host_b+i+N,
                                        N * sizeof(int),
                                        cudaMemcpyHostToDevice,
                                        stream1 ) );
        //安排stream0.
        kernel<<<N/256, 256, 0, stream0>>>( dev_a0, dev_b0, dev_c0 );
        //安排stream1.
        kernel<<<N/256, 256, 0, stream1>>>( dev_a1, dev_b1, dev_c1 );
        //安排stream0.
        HANDLE_ERROR( cudaMemcpyAsync( host_c+i, dev_c0,
                                        N * sizeof(int),
                                        cudaMemcpyDeviceToHost,
                                        stream0 ) );
        //安排stream1.
        HANDLE_ERROR( cudaMemcpyAsync( host_c+i+N, dev_c1,
                                        N*sizeof(int),
                                        cudaMemcpyDeviceToHost,
                                        stream1 ) );
    }

其他代码不变.
这里写图片描述

其实, 前期分配内存的时间比for循环里用的时间长的多.
所以, 对这个程序来说, 瓶颈在于Host和device之间的数据传输太慢了.

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: cesium-examples-master 是一个 Cesium 的示例项目。Cesium 是一个开源的3D地球可视化引擎,能够在Web上以浏览器为平台展示地球相关的数据和图形。cesium-examples-master 包含了一系列基于 Cesium 引擎的示例代码和样例数据,供开发人员学习和参考。 这个项目提供了丰富的示例,涵盖了各种场景和功能,如地形渲染、卫星图像展示、空中飞行效果、地球热力图、数据可视化等。每个示例都提供了完整的源代码和相关资源,开发人员可以直接运行和修改,快速了解 Cesium 的使用方式和功能特性。 cesium-examples-master 的目的是帮助开发人员加快上手 Cesium,提供具体的示例代码和实现思路,同时也是一个社区贡献的项目,任何人都可以向其中添加自己的示例代码。这对于想要共享自己的 Cesium 开发经验,或者想要通过Cesium实现自己的创意项目的开发者们来说都是很有帮助的。 总之,cesium-examples-master 是一个集合了Cesium引擎的示例代码和样例数据的项目,通过这个项目,开发人员可以学习和参考Cesium的使用方式和功能特性,同时也可以贡献自己的示例代码,为Cesium社区贡献自己的力量。 ### 回答2: cesium-examples-master是一个开源的Cesium.js示例库。Cesium.js是一个基于WebGL的开源JavaScript库,用于创建3D地球和地理信息可视化应用程序。 cesium-examples-master库中包含了大量的示例代码,用于演示如何使用Cesium.js库进行地球和地理数据可视化。这些示例涵盖了各种应用场景,包括地球浏览、地理数据可视化、飞行模拟、地球时间轴等等。 这个示例库非常有用,特别是对于那些想要利用Cesium.js构建自己的3D地球和地理信息应用程序的开发人员来说。通过学习和理解这些示例代码,开发人员可以快速上手并加快应用程序的开发速度。 此外,cesium-examples-master还可以作为一个学习资源,供初学者学习Cesium.js库的使用。通过运行和修改这些示例代码,初学者可以逐步掌握Cesium.js的各种功能和技术知识。 总之,cesium-examples-master是一个非常有用的示例库,可以帮助开发人员和初学者更好地了解和应用Cesium.js库。无论是开发3D地球和地理信息应用程序,还是学习Cesium.js库的使用,这个示例库都是一个很好的资源。如果你对Cesium.js感兴趣,不妨去查看cesium-examples-master库并尝试运行其中的示例代码。 ### 回答3: cesium-examples-master是一个Cesium的示例代码库。Cesium是一个开源的地球可视化库,用于在Web浏览器中创建交互式三维地球和地球数据的应用程序。cesium-examples-master提供了许多不同类型的示例,展示了使用Cesium创建各种地球可视化应用的能力。这些示例包括地球模型的加载、地形数据的展示、地图投影的转换、地球上的点、线和面的创建等等。通过这些示例,开发者可以学习如何使用Cesium的API来构建自己的地球可视化项目,并根据自己的需求进行修改和扩展。cesium-examples-master的代码注释详细,对于刚开始学习Cesium的开发者来说是一个很好的参考工具。在cesium-examples-master中,开发者可以找到各种应用场景的示例代码,例如飞行模拟、地球上的图层切换、轨迹的绘制和动态效果等等。总之,cesium-examples-master对于想要学习和探索Cesium地球可视化库的开发者来说是一个非常有用的资源。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值