CUDA02_04GPU计算线程分配

2020-01-19  本文已影响0人  杨强AT南京

  本主题体会下GPU计算的结构设计对并发编程的天然支持;使用GPU处理并发任务,简洁方便。
  本主题主要内容就是怎么计算线程分配
    1. GPU C/C++的扩展语法
    2. Grid的结构;
    3. Block的结构;
  最后给出了一个使用GPU处理图像的例子。


序言

  1. GPU采用的是并行处理来提高速度,主要是因为:

    • GPU高度并行;
    • 多线程;
    • 多核处理器;
    • 极高的内存带宽。
  2. GPU性能提升证据图

    • 每秒的浮点运算次数数据。明显单精度浮点数运算很彪悍。
    • 下图来自官方文档,截止时间2020年1月。


      浮点数运算次数提升与比较
  3. GPU内存带宽提升证据图

    • 是每秒的内存IO的理论峰值。
    • 下图来自官方文档。


      内存带宽提升图
  4. GPU的设计使用更具的计算单元而不是存储单元。

    • 官方说法:GPU将更多的晶体管用于数据处理(ALU:算术逻辑单元Arithmetic Logical Unit)。
    • 下图是CPU与GPU的存储与计算晶体管的比较。绿色部分是计算单元。


      CPU与GPU性能提升的设计比较
  1. GPU的核动态可分配性
    • GPU是围绕流式多处理器(SMs)阵列构建的。
      • 每个SM分配多核块;
      • 每个块可以指定线程数(线程数会根据处理器的情况来充分利用GPU的处理器)
        • 比如:5个线程,一个处理器,处理器调用5次;
        • 比如:5个线程,5个处理器,处理器调用1次;
    • 多线程程序被划分成独立执行的线程块,这样多处理器的GPU将比少处理器的GPU在更短的时间内自动执行程序。

GPU计算的SM模型

GPU的C/C++扩展向量数据类型

基本类型的向量扩展

Type Alignment
char1, uchar1 1
char2, uchar2 2
char3, uchar3 1
char4, uchar4 4
short1, ushort1 2
short2, ushort2 4
short3, ushort3 2
short4, ushort4 8
int1, uint1 4
int2, uint2 8
int3, uint3 4
int4, uint4 16
long1, ulong1 4 if sizeof(long) is equal to sizeof(int) 8, otherwise
long2, ulong2 8 if sizeof(long) is equal to sizeof(int), 16, otherwise
long3, ulong3 4 if sizeof(long) is equal to sizeof(int), 8, otherwise
long4, ulong4 16
longlong1, ulonglong1 8
longlong2, ulonglong2 16
longlong3, ulonglong3 8
longlong4, ulonglong4 16
float1 4
float2 8
float3 4
float4 16
double1 8
double2 16
double3 8
double4 16

dim3类型

  1. 这个类型太过常用,所以实际上是把uint3定义成dim3。
  2. 这个类型默认初始化为(1,1,1)

数据变量的初始化

扩展数据类型使用例子

#include <stdio.h>
#include <cuda.h>

int main(int argc, const char **argv){
    dim3  pos;
//     pos.x = 10;
//     pos.y = 20;
//     pos.z = 30;
    printf("%d,%d,%d\n", pos.x, pos.y, pos.z);

    float4 v4 = make_float4(1.1f, 2.2f, 3.3f, 4.4f);
    printf("%f,%f,%f,%f\n", v4.x, v4.y, v4.z, v4.w);
    return 0;
}

// nvcc -o main.exe  -Xcompiler /source-charset:utf-8 c01_datatype.cu

GPU扩展内置变量

gridDim变量

  1. 类型

    • dim3
  2. 理解SM(Grid,Block,Thread三者的关系)


    Grid,Block,Thread的三级计算资源管理结构:SM管理方式
  1. gridDim的功能
    • 描述需要分配分类的block数量(见上图)
      • block的数量 = gridDim.x * gridDim.y * gridDim.z

blockDim变量

  1. 类型:

    • dim3
  2. 功能:

    • 用来描述每个block的线程数量,见上图。
    • thread的数量 = blockDim.x * blockDim.y * blockDim.z

blockIdx变量

  1. 类型:

    • uint3
  2. 功能:

    • 用来记录核函数调用的时候,当前块的编号,应为函数同时被多个线程同时调用,为了避免数据脏,需要编号来维持数据的清晰。

threadIdx变量

  1. 类型:
    • uint3
  2. 功能:
    • 用来记录函数调用时的线程编号,如果结合到块编号,我们可以知道函数当前被第几次调用。

warpSize变量

  1. 类型
    • int
  2. 功能:
    • 多处理器创建、管理、调度和执行一组N个并行线程,称为warps。组成一个warp的各个线程在同一个程序地址一起启动,但是它们有自己的指令地址计数器和寄存器状态,因此可以独立地进行分支和执行。
    • warpSize就是一个并行线程组的数量。

理解内置变量的代码

// 定义核函数,用来实现并行计算
__global__ void record_var(int *p, int *bk_idx, int *th_idx){
    // 使用内置变量得到核函数被调用的编号。这个编号与线程调度,线程调度与gpu核有关。
    // 计算block的编号
    int block_id;
    int thread_id;
    block_id = blockIdx.y * gridDim.x + blockIdx.x;
    // 计算thread的编号  
    thread_id = block_id * blockDim.x * blockDim.y + threadIdx.y * blockDim.x + threadIdx.x;
    // 计算索引
    p[thread_id] *= 5;
    bk_idx[thread_id] = block_id;   // 记录block的id。一共25个block,分成5*5
    th_idx[thread_id] = thread_id;  // 记录thread的id,每个block一共4个线程,分成3*3
}

int main(int argc, const char **argv){
    // 准备cpu数据 (gridDim:5*5 + blockDim:3*3), 一共100个线程,每个线程计算一个元素
    int  *data_cpu = (int*)malloc(5 * 5 * 3 * 3 * sizeof(int));
    int  *bkid_cpu = (int*)malloc(5 * 5 * 3 * 3 * sizeof(int));
    int  *thid_cpu = (int*)malloc(5 * 5 * 3 * 3 * sizeof(int));
    
    ..... <省略若干代码>
    
    // GPU内存
    int  *data_gpu, *bkid_gpu, *thid_gpu; 
   
    ... <省略若干代码>
    
    // 调用核函数
    dim3 grid(5, 5);
    dim3 block(3, 3);

    record_var<<<grid, block>>>(data_gpu, bkid_gpu, thid_gpu);

    return 0;
}
#include <iostream>
#include <cuda.h>
/*
 * 记录内置变量
 */

// 定义核函数,用来实现并行计算
__global__ void record_var(int *p, int *bk_idx, int *th_idx){
    // 使用内置变量得到核函数被调用的编号。这个编号与线程调度,线程调度与gpu核有关。
    // 计算block的编号
    int block_id;
    int thread_id;
    block_id = blockIdx.y * gridDim.x + blockIdx.x;
    // 计算thread的编号  
    thread_id = block_id * blockDim.x * blockDim.y + threadIdx.y * blockDim.x + threadIdx.x;
    // 计算索引
    p[thread_id] *= 5;
    bk_idx[thread_id] = block_id;   // 记录block的id。一共25个block,分成5*5
    th_idx[thread_id] = thread_id;  // 记录thread的id,每个block一共4个线程,分成3*3
}

int main(int argc, const char **argv){
    // 准备cpu数据 (gridDim:5*5 + blockDim:3*3), 一共100个线程,每个线程计算一个元素
    int  *data_cpu = (int*)malloc(5 * 5 * 3 * 3 * sizeof(int));
    int  *bkid_cpu = (int*)malloc(5 * 5 * 3 * 3 * sizeof(int));
    int  *thid_cpu = (int*)malloc(5 * 5 * 3 * 3 * sizeof(int));
    // 初始化为3
    for(int i = 0; i < 5 * 5 * 3 * 3; i++){
        data_cpu[i] = 3;
        bkid_cpu[i] = -1;
        thid_cpu[i] = -1;
    }

    // GPU内存
    int  *data_gpu, *bkid_gpu, *thid_gpu; 
    cudaMalloc((void**)&data_gpu, 5 * 5 * 3 * 3 * sizeof(int));
    cudaMalloc((void**)&bkid_gpu, 5 * 5 * 3 * 3 * sizeof(int));
    cudaMalloc((void**)&thid_gpu, 5 * 5 * 3 * 3 * sizeof(int));
    // 拷贝数据
    cudaMemcpy((void*)data_gpu, (void*)data_cpu, 5 * 5 * 3 * 3 * sizeof(int), cudaMemcpyHostToDevice);
    cudaMemcpy((void*)bkid_gpu, (void*)bkid_cpu, 5 * 5 * 3 * 3 * sizeof(int), cudaMemcpyHostToDevice);
    cudaMemcpy((void*)thid_gpu, (void*)thid_cpu, 5 * 5 * 3 * 3 * sizeof(int), cudaMemcpyHostToDevice);

    // 调用核函数
    dim3 grid(5, 5);
    dim3 block(3, 3);

    record_var<<<grid, block>>>(data_gpu, bkid_gpu, thid_gpu);

    // 计算完毕,拷贝数据到host
    cudaMemcpy((void*)data_cpu, (void*)data_gpu, 5 * 5 * 3 * 3 * sizeof(int), cudaMemcpyDeviceToHost);
    cudaMemcpy((void*)bkid_cpu, (void*)bkid_gpu, 5 * 5 * 3 * 3 * sizeof(int), cudaMemcpyDeviceToHost);
    cudaMemcpy((void*)thid_cpu, (void*)thid_gpu, 5 * 5 * 3 * 3 * sizeof(int), cudaMemcpyDeviceToHost);

    // 输出数据观察结果
    for(int i = 0; i < 5 * 5 * 3 * 3; i++){
        std::cout << data_cpu[i] << ", ";
    }
    std::cout <<std::endl;
    for(int i = 0; i < 5 * 5 * 3 * 3; i++){
        std::cout << bkid_cpu[i] << ", ";
    }
    std::cout <<std::endl;
    for(int i = 0; i < 5 * 5 * 3 * 3; i++){
        std::cout << thid_cpu[i] << ", ";
    }
    std::cout <<std::endl;

    // 养成释放内存的习惯
    cudaFree(data_gpu);
    cudaFree(bkid_gpu);
    cudaFree(thid_gpu);

    free(data_cpu);
    free(bkid_cpu);
    free(thid_cpu);
    return 0;
}

15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 
0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19, 19, 19, 19, 19, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24,

0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224,

GPU核数与线程数

  1. 获取CPU的核数
int main(int argc, char const *argv[]){
    // 为了代码的简洁性,省略了异常处理
    int kernel_count;
    cudaDeviceGetAttribute (&kernel_count, cudaDevAttrMultiProcessorCount, 0);
    printf("GPU设备上的核数量:%d\n", kernel_count);
    return 0;
}
  1. 获取每个核关联的GPU Core。
    • GPU Core与GPU的计算能力版本有关。下面是计算能力版本的对应关系。
    sSMtoCores nGpuArchCoresPerSM[] = {
        {0x30, 192},
        {0x32, 192},
        {0x35, 192},
        {0x37, 192},
        {0x50, 128},
        {0x52, 128},
        {0x53, 128},
        {0x60,  64},
        {0x61, 128},    // 本人机器的兼容版本
        {0x62, 128},
        {0x70,  64},
        {0x72,  64},
        {0x75,  64},
        {-1, -1}
    };

#include <iostream>
#include <cuda.h>

inline int _ConvertSMVer2CoresDRV(int major, int minor) {
    typedef struct {
        int SM;     // 0xMm (hexidecimal notation), M = SM Major version, and m = SM minor version
        int Cores;
    } sSMtoCores;

    sSMtoCores nGpuArchCoresPerSM[] = {
        {0x30, 192},
        {0x32, 192},
        {0x35, 192},
        {0x37, 192},
        {0x50, 128},
        {0x52, 128},
        {0x53, 128},
        {0x60,  64},
        {0x61, 128},
        {0x62, 128},
        {0x70,  64},
        {0x72,  64},
        {0x75,  64},
        {-1, -1}
    };

    int index = 0;

    while (nGpuArchCoresPerSM[index].SM != -1) {
        if (nGpuArchCoresPerSM[index].SM == ((major << 4) + minor)) {
            return nGpuArchCoresPerSM[index].Cores;
        }

        index++;
    }
    printf("MapSMtoCores for SM %d.%d is undefined.  Default to use %d Cores/SM\n",major, minor, nGpuArchCoresPerSM[index - 1].Cores);
    return nGpuArchCoresPerSM[index - 1].Cores;
}

int main(int argc, char const *argv[]){
    cuInit(0);   
    CUdevice device;
    cuDeviceGet (&device, 0);

    int kernel_count;
    cudaDeviceGetAttribute (&kernel_count, cudaDevAttrMultiProcessorCount, 0);
    std::cout<< "设备上的核数量:" <<  kernel_count << std::endl;

    int major, minor;
    cuDeviceGetAttribute(&major, CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MAJOR, device);
    cuDeviceGetAttribute(&minor, CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MINOR, device);

    // GPU Core数量
    int  gpu_core = _ConvertSMVer2CoresDRV(major, minor);
    std::cout<< "GPU Core / SM:" << gpu_core << std::endl;
    std::cout<< "GPU Core Total:" << gpu_core * kernel_count << std::endl;

    return 0;
}

// nvcc -o main.exe  -l cuda -Xcompiler /source-charset:utf-8 c03_gpu_core.cu

C:\01works\02cuda\c04c_extension>main
设备上的核数量:10
GPU Core / SM:128
GPU Core Total:1280
  1. GPU的grid与block的支持的限制
    • grid没有限制;
    • 但是每个块的线程数是有限制的,最大1024.
      • 因为块的所有线程都应该位于同一个处理器核心上,并且必须共享该核心的有限内存资源。在当前GPU上,一个线程块最多可以包含1024个线程。

GPU计算的应用例子

GPU的计算能力的特征

  1. -官方地址

    • https://docs.nvidia.com/cuda/cuda-c-programming-guide/index.html#compute-capabilities
  2. 计算能力特征截图


    计算能力特征
  3. 计算能力的技术参数

    • 只截图我们关注的部分。


      计算能力技术参数
  4. gridDim的要求

  1. blockDim的要求
  1. thread线程数 / block限制:
    1. 每个块的最大线程数: \le 1024

核心代码

1.函数定义

- 注意:其中线程编号的计算是与调用的时候的线程分配结构有关系的。

__global__ void shift_color_channels(uchar4 *data){
    // 计算索引
    int  idx = blockIdx.x * blockDim.x + threadIdx.x;   // 因为是1维,获取x就是索引。
    // 处理像素
    unsigned char red   = data[idx].x;
    unsigned char green = data[idx].y;
    unsigned char blue  = data[idx].z;
    // unsigned char alpha = pixel.w;

    data[idx].x = blue;
    data[idx].y = red;
    data[idx].z = green;
    // alpha不动
}

  1. 函数调用:
    • 注意因为行超过了1024,所以折半后的数据作为block的线程数。这类没有使用多维结构了!喜欢多维的可以自己设计下。
    • 如果超过1024,则CUDA不工作。
      • x,y不能超过1024,z不能超过64,而且 x \times y \times z也不能超过1024。
    ////////////////////////////////GPU处理调用
    //为了简单,按照图像的行列分配线程,行用来定义块,列用来定义线程, 每个线程处理4个char,并交换像素通道
    dim3 grid(header.height * 2);    // C/C++的除法对整数是整除。
    dim3 block(header.width / 2);    // 注意:每个block做多1024个线程。因为块的所有线程都应该位于同一个处理器核心上


    shift_color_channels<<<grid, block>>>(img_gpu);

    ////////////////////////////////

完整代码

  1. 代码
#include <cuda.h>
#include <stdio.h>
#include <stdlib.h>
// 结构体定义
#pragma pack(1)
struct img_header{
    // 文件头
    char                  magic[2];                  // 魔法字
    unsigned int          file_size;                 // 文件大小
    unsigned char         reserve1[4];               // 跳4字节
    unsigned int          data_off;                  // 数据区开始位置
    // 信息头
    unsigned char         reserve2[4];               // 跳4字节
    int                   width;                     // 图像宽度
    int                   height;                    // 图像高度
    unsigned char         reserve3[2];               // 跳2字节
    unsigned short int    bit_count;                 // 图像位数1,4,8,16,24,32
    unsigned char         reserve4[24];              // 跳24字节
};


// 偷懒写一个匿名全局类

// 全局数据
struct img_header  header;
uchar4             *img;                             // 使用gpu的扩展类型
uchar4             *img_gpu; 

// 输入/输出文件名
const char *in_filename  = "gpu.bmp";
const char *out_filename = "gpu_out.bmp";

// 打开图像
void read_bmp();                                     // 无参数,采用全局成员
// 保存图像
void save_bmp();
// GPU运算的数据
void move_to_device();  
// 取得数据
void move_to_host();                            
// GPU处理
__global__ void shift_color_channels(uchar4 *data);
// 内存释放
void free_mem();

int main(int argc, const char **argv){
    read_bmp();
    move_to_device();
    ////////////////////////////////GPU处理调用
    //为了简单,按照图像的行列分配线程,行用来定义块,列用来定义线程, 每个线程处理4个char,并交换像素通道
    dim3 grid(header.height * 2);    // C/C++的除法对整数是整除。
    dim3 block(header.width / 2);    // 注意:每个block做多1024个线程。因为块的所有线程都应该位于同一个处理器核心上


    shift_color_channels<<<grid, block>>>(img_gpu);

    ////////////////////////////////
    move_to_host();
    save_bmp();
    free_mem();
    return 0;
}

__global__ void shift_color_channels(uchar4 *data){
    // 计算索引
    int  idx = blockIdx.x * blockDim.x + threadIdx.x;   // 因为是1维,获取x就是索引。
    // 处理像素
    unsigned char red   = data[idx].x;
    unsigned char green = data[idx].y;
    unsigned char blue  = data[idx].z;
    // unsigned char alpha = pixel.w;

    data[idx].x = blue;
    data[idx].y = red;
    data[idx].z = green;
    // alpha不动
}
void move_to_host(){
    cudaMemcpy((void*)img, (void*)img_gpu, header.height * header.width * sizeof(uchar4), cudaMemcpyDeviceToHost);
}
void move_to_device(){
    // 分配GPU内存
    cudaMalloc((void**)&img_gpu, header.height * header.width * sizeof(uchar4));   // 返回指针,则参数就需要二重指针。
    // 拷贝数据
    cudaMemcpy((void*)img_gpu, (void*)img,  header.height * header.width * sizeof(uchar4), cudaMemcpyHostToDevice);

}
void read_bmp(){ 
    /* 读取头,分配内存,读取数据,这里数据采用了一维数组,使用的时候,需要转换处理下。*/
    FILE *file = fopen(in_filename, "rb");
    // 读取头
    size_t n_bytes = fread(&header, 1, 54, file); 
    
    // 计算读取的大大小,并分配空间,并读取。
    header.height = header.height >= 0? header.height : -header.height;
    img = (uchar4 *)malloc(header.height * header.width * sizeof(uchar4));
    n_bytes = fread(img, sizeof(uchar4), header.height * header.width, file);  // 因为是4倍数对齐的,所以可以直接读取

    fclose(file); // 关闭文件
    
}
void save_bmp(){
    /* 使用与读取一样的头信息保存图像 */
    FILE *file = fopen(out_filename, "wb");
    // 写头
    header.height = -header.height;
    size_t n_bytes = fwrite(&header, 1, 54, file);
    header.height = -header.height;
    // 写图像数据
    n_bytes = fwrite(img, sizeof(uchar4), header.height * header.width, file);
    // 关闭文件
    fclose(file);
}
void free_mem(){
    /* 释放Host与Device内存 */
    free(img); // 直接释放(不需要指定大小,malloc系列函数有内部变量管理分配的内存)
    cudaFree(img_gpu);
}

// nvcc -o main.exe  -Xcompiler /source-charset:utf-8 c04_gpu_app.cu

  1. 编译
    • 上面的代码保存为c04_gpu_app.cu文件名。
main:
    @nvcc -o main.exe  -Xcompiler /source-charset:utf-8 c04_gpu_app.cu
clean:
    @del *.exe *.exp *.lib  gpu_out.bmp 2>Nul  
    
 
  1. 效果截图


    GPU运算的应用例子

附录


上一篇下一篇

猜你喜欢

热点阅读