zoukankan      html  css  js  c++  java
  • 0_Simple__simpleCallback

    ▶ 学习回调函数的基本概念,并在CUDA的任务流中插入基于CPU的主机函数,作为回调函数使用。

    ▶ 源代码(合并了 3 个源文件,删掉了没有用到的部分)

      1 // simpleCallback.cu
      2 #include <stdio.h>
      3 #include <windows.h>
      4 #include <cuda_runtime.h>
      5 #include "device_launch_parameters.h"
      6 #include <helper_functions.h>
      7 #include <helper_cuda.h>
      8 
      9 #define N_WORKLOAD  8
     10 #define BLOCK       512
     11 #define ELEMENT     100000
     12 
     13 struct CUTBarrier                       // 线程墙
     14 {
     15     CRITICAL_SECTION criticalSection;   // Windows 中有关线程的结构
     16     HANDLE barrierEvent;
     17     int releaseCount;
     18     int count;
     19 };
     20 
     21 CUTBarrier thread_barrier;
     22 
     23 struct heterogeneous_workload   // 用于分配工作的结构
     24 {
     25     int id;                     // 工作编号
     26     int cudaDeviceID;           // 执行工作的设备号
     27     int *h_data;                
     28     int *d_data;                
     29     cudaStream_t stream;        // 使用的流号(一个工作使用一条流)
     30     bool success;               // 检查结果是否正确的标志
     31 };
     32 
     33 HANDLE cutStartThread(unsigned (WINAPI * func)(void *), void *data) // 创建新线程,注意函数指针的形式
     34 {
     35     return CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)func, data, 0, NULL);
     36 }
     37 
     38 CUTBarrier cutCreateBarrier(int releaseCount)                       // 创建线程墙
     39 {
     40     CUTBarrier barrier;
     41     InitializeCriticalSection(&barrier.criticalSection);
     42     barrier.barrierEvent = CreateEvent(NULL, TRUE, FALSE, TEXT("BarrierEvent"));
     43     barrier.count = 0;
     44     barrier.releaseCount = releaseCount;
     45     return barrier;
     46 }
     47 
     48 void cutIncrementBarrier(CUTBarrier *barrier)                       // 线程墙判断线程工作是否已经全部结束
     49 {
     50     int myBarrierCount;
     51     EnterCriticalSection(&barrier->criticalSection);
     52     myBarrierCount = ++barrier->count;
     53     LeaveCriticalSection(&barrier->criticalSection);
     54     if (myBarrierCount >= barrier->releaseCount)                    // 发出的线程已经全部结束
     55         SetEvent(barrier->barrierEvent);
     56 }
     57 
     58 void cutWaitForBarrier(CUTBarrier *barrier)                         // 回收线程墙
     59 {
     60     WaitForSingleObject(barrier->barrierEvent, INFINITE);
     61 }
     62 
     63 __global__ void incKernel(int *data, int N)                         // 将 data 中所有元素递增总线程个数次
     64 {
     65     int idx = blockIdx.x * blockDim.x + threadIdx.x;
     66 
     67     if (idx < N)
     68         data[idx]++;
     69 }
     70 
     71 unsigned WINAPI postprocess(void *void_arg)
     72 {
     73     heterogeneous_workload *workload = (heterogeneous_workload *)void_arg;
     74     cudaSetDevice(workload->cudaDeviceID);
     75     
     76     getLastCudaError("Kernel execution failed");                            // 检查GPU计算结果
     77     workload->success = true;
     78     for (int i = 0; i < N_WORKLOAD; ++i)
     79         workload->success &= (workload->h_data[i] == workload->id + i + 1);
     80    
     81     cudaFree(workload->d_data);
     82     cudaFreeHost(workload->h_data);
     83     cudaStreamDestroy(workload->stream);
     84 
     85     printf("Workload %d finished!
    ", workload->id);                        // 回调函数工作完成
     86     cutIncrementBarrier(&thread_barrier);                                   // 向线程墙发送工作完成的信号
     87     return 0;
     88 }
     89 
     90 void CUDART_CB myStreamCallback(cudaStream_t stream, cudaError_t status, void *data)// 回调函数,参数格式固定
     91 {    
     92     cutStartThread(postprocess, data);                                      // 调用函数 postprocess 完成结果检查和内存释放
     93 }
     94 
     95 unsigned WINAPI launch(void *void_arg)
     96 {
     97     heterogeneous_workload *workload = (heterogeneous_workload *)void_arg;  // 初始化工作参数  
     98     cudaSetDevice(workload->cudaDeviceID);                                  
     99     cudaStreamCreate(&workload->stream);
    100     cudaMalloc(&workload->d_data, ELEMENT * sizeof(int));
    101     cudaHostAlloc(&workload->h_data, ELEMENT * sizeof(int), cudaHostAllocPortable);
    102     for (int i = 0; i < ELEMENT; ++i)
    103         workload->h_data[i] = workload->id + i;
    104 
    105     // 每个 CPU 线程对应一条 CUDA 流,分别调度流的工作,可以并行,不阻塞 CPU 线程
    106     cudaMemcpyAsync(workload->d_data, workload->h_data, ELEMENT * sizeof(int), cudaMemcpyHostToDevice, workload->stream);
    107     incKernel << <(ELEMENT + BLOCK - 1) / BLOCK, BLOCK, 0, workload->stream >> > (workload->d_data, ELEMENT);
    108     cudaMemcpyAsync(workload->h_data, workload->d_data, ELEMENT * sizeof(int), cudaMemcpyDeviceToHost, workload->stream);
    109     
    110     cudaStreamAddCallback(workload->stream, myStreamCallback, workload, 0); // 回调函数,调用主机函数放入 CUDA 流中,在这里用于检查 GPU 结果和回收内存
    111     return 0;
    112 }
    113 
    114 int main(int argc, char **argv)
    115 {
    116     printf("	Start.
    ");
    117 
    118     heterogeneous_workload *workloads = (heterogeneous_workload *)malloc(N_WORKLOAD * sizeof(heterogeneous_workload));  // 创建工作表    
    119     thread_barrier = cutCreateBarrier(N_WORKLOAD);                          // 创建线程墙,以便所有工作结束后回收    
    120     
    121     for (int i = 0; i < N_WORKLOAD; ++i)                                    // 分配任务
    122     {
    123         workloads[i].id = i;
    124         workloads[i].cudaDeviceID = 0;                                      // 将任务全部分配给 0 号设备
    125         cutStartThread(launch, &workloads[i]);
    126     }
    127     
    128     cutWaitForBarrier(&thread_barrier);                                     // 回收线程
    129     printf("
    %d workloads all finished.
    ", N_WORKLOAD);    
    130     
    131     int success = 1;
    132     for (int i = 0; i < N_WORKLOAD; success &= workloads[i].success, ++i);  // 检查正确性
    133     printf("
    	%s
    ", success ? "Correct." : "Failure.");
    134 
    135     free(workloads);
    136     getchar();
    137     return success;
    138 }

    ● 输出结果

            Start.
    Work 2 finished!
    Work 3 finished!
    Work 7 finished!
    Work 1 finished!
    Work 5 finished!
    Work 4 finished!
    Work 6 finished!
    Work 0 finished!
    
    8 workloads all finished.
    
            Correct.

    ▶ 涨姿势

    ● 回调函数的使用:首先在 cuda_runtime_api.h 中给出了能作为回调函数的主机函数格式,然后给出了回调函数的定义。回调函数需要给出流编号,回调函数指针,回调函数需要的参数,以及一个标志(不太清楚其意义,可能与回调函数是否等待流中所有其他任务是否完成后再开始有关)

    1 #define CUDART_CB __stdcall
    2 #define CUDARTAPI  __stdcall
    3 
    4 // cudaStreamCallback_t 的定义
    5 typedef void (CUDART_CB *cudaStreamCallback_t)(cudaStream_t stream, cudaError_t status, void *userData);
    6 
    7 // cudaStreamAddCallback 的定义
    8 extern __host__ cudaError_t CUDARTAPI cudaStreamAddCallback(cudaStream_t stream, cudaStreamCallback_t callback, void *userData, unsigned int flags);

    ● 有关线程创建的一些参数

    1 //winnt.h
    2 typedef void* HANDLE;       // HANDLE 原来就是 void*
    3 //minwindef.h
    4 typedef unsigned long DWORD;// DWORD 原来就是 unsigned long
  • 相关阅读:
    windows程序设计第四章 system2.c 新增滚动条功能
    const指南
    转:android listView 继承ListActivity的用法
    iPhone使用委托在不同的窗口之间传递数据
    (PHP) imagecreatefrombmp 从 BMP 文件或 URL 新建一图像
    iPhone解析非UTF8的XML
    php生成略缩图(转载)
    iphone下载进度条,显示下载字节数与百分比
    android中实现消息推送
    Android动画开发——Animation动画效果
  • 原文地址:https://www.cnblogs.com/cuancuancuanhao/p/7782800.html
Copyright © 2011-2022 走看看