C++ GPU编程(英伟达CUDA)

news2024/11/29 12:32:48

安装编译环境
https://developer.download.nvidia.com/compute/cuda/12.5.0/local_installers/cuda_12.5.0_555.85_windows.exe

CMakeLists.txt

cmake_minimum_required(VERSION 3.10)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_BUILD_TYPE Release)
#set(CMAKE_CUDA_ARCHITECTURES 52;70;75;86)
set(CMAKE_CUDA_SEPARABLE_COMPILATION ON)

project(hellocuda LANGUAGES CXX CUDA)

add_executable(main main.cu hello.cu)

target_include_directories(main PUBLIC ../../include)
target_compile_options(main PUBLIC $<$<COMPILE_LANGUAGE:CUDA>:--extended-lambda>)
target_compile_options(main PUBLIC $<$<COMPILE_LANGUAGE:CUDA>:--expt-relaxed-constexpr>)
# --use_fast_math   sinf 替换成 __sinf
# --ftz=true 会把极小数(denormal)退化为0
# --prec-div=false 降低除法的精度换取速度。
# --prec-sqrt=false 降低开方的精度换取速度。
# --fmad 因为非常重要,所以默认就是开启的,会自动把 a * b + c 优化成乘加(FMA)指令。
# 开启 --use_fast_math 后会自动开启上述所有

 CudaAllocator.h

template <class T>
struct CudaAllocator {
    using value_type = T;

    T* allocate(size_t size) {
        T* ptr = nullptr;
        checkCudaErrors(cudaMallocManaged(&ptr, size * sizeof(T)));
        return ptr;
    }

    void deallocate(T* ptr, size_t size = 0) {
        checkCudaErrors(cudaFree(ptr));
    }

    template <class ...Args>
    void construct(T* p, Args &&...args) {
        if constexpr (!(sizeof...(Args) == 0 && std::is_pod_v<T>))
            ::new((void*)p) T(std::forward<Args>(args)...);
    }
};

 hello.cu

#include <cstdio>
#include <cuda_runtime.h>

__device__ void say_hello3() {  // 定义
    printf("Hello, world!\n");
}

 main.cu

#include <cstdio>
#include <cuda_runtime.h>
#include "helper_cuda.h"
#include <vector>
#include "CudaAllocator.h"
#include <thrust/universal_vector.h>
#include <thrust/device_vector.h>
#include <thrust/host_vector.h>
#include <thrust/generate.h>
#include <thrust/for_each.h>

__device__ void say_hello3();  // 声明

__device__ __inline__ void say_hello() {
    printf("Hello, world, world from GPU!\n");
}

__host__ void say_hello_host() {
    printf("Hello, world from CPU!\n");
}

__host__ __device__ void say_hello2() {
#ifdef __CUDA_ARCH__
    printf("Hello, world from GPU architecture %d!\n", __CUDA_ARCH__);
#else
    printf("Hello, world from CPU!\n");
#endif
}

__global__ void another() {
    int localVal = 1;
    printf("another: Thread %d of %d\n", threadIdx.x, blockDim.x);
}

__global__ void kernel() {
    unsigned int tid = blockDim.x * blockIdx.x + threadIdx.x;
    unsigned int tnum = blockDim.x * gridDim.x;
    printf("Flattened Thread %d of %d\n", tid, tnum);
    say_hello2();
    say_hello3();
    another << <1, tnum >> > ();  // 核函数调用核函数
    printf("kernel: called another with %d threads\n", tnum);
}

__global__ void kernel2() {
    printf("Block (%d,%d,%d) of (%d,%d,%d), Thread (%d,%d,%d) of (%d,%d,%d)\n",
        blockIdx.x, blockIdx.y, blockIdx.z,
        gridDim.x, gridDim.y, gridDim.z,
        threadIdx.x, threadIdx.y, threadIdx.z,
        blockDim.x, blockDim.y, blockDim.z);
}

// 默认host
constexpr const char* cuthead(const char* p) {
    return p + 1;
}

// 内存访问
__global__ void kernel_memory(int* pret) {
    *pret = 42;
}

// 数组并行处理
__global__ void kernel_array(int* arr, int n) {
    for (int i = blockDim.x * blockIdx.x + threadIdx.x;
        i < n; i += blockDim.x * gridDim.x) {
        arr[i] = i;
    }
}

// 并行处理模板
template <class Func>
__global__ void parallel_for(int n, Func func) {
    for (int i = blockDim.x * blockIdx.x + threadIdx.x;
        i < n; i += blockDim.x * gridDim.x) {
        func(i);
    }
}

// 自定义加减乘除
__device__ __inline__ int float_atomic_add(float* dst, float src) {
    int old = __float_as_int(*dst), expect;
    do {
        expect = old;
        old = atomicCAS((int*)dst, expect,
            __float_as_int(__int_as_float(expect) + src));
    } while (expect != old);
    return old;
}


// map
template <int blockSize, class T>
__global__ void parallel_sum_kernel(T* sum, T const* arr, int n) {
    __shared__ volatile int local_sum[blockSize];
    int j = threadIdx.x;
    int i = blockIdx.x;
    T temp_sum = 0;
    for (int t = i * blockSize + j; t < n; t += blockSize * gridDim.x) {
        temp_sum += arr[t];
    }
    local_sum[j] = temp_sum;
    __syncthreads();
    if constexpr (blockSize >= 1024) {
        if (j < 512)
            local_sum[j] += local_sum[j + 512];
        __syncthreads();
    }
    if constexpr (blockSize >= 512) {
        if (j < 256)
            local_sum[j] += local_sum[j + 256];
        __syncthreads();
    }
    if constexpr (blockSize >= 256) {
        if (j < 128)
            local_sum[j] += local_sum[j + 128];
        __syncthreads();
    }
    if constexpr (blockSize >= 128) {
        if (j < 64)
            local_sum[j] += local_sum[j + 64];
        __syncthreads();
    }
    if (j < 32) {
        if constexpr (blockSize >= 64)
            local_sum[j] += local_sum[j + 32];
        if constexpr (blockSize >= 32)
            local_sum[j] += local_sum[j + 16];
        if constexpr (blockSize >= 16)
            local_sum[j] += local_sum[j + 8];
        if constexpr (blockSize >= 8)
            local_sum[j] += local_sum[j + 4];
        if constexpr (blockSize >= 4)
            local_sum[j] += local_sum[j + 2];
        if (j == 0) {
            sum[i] = local_sum[0] + local_sum[1];
        }
    }
}

// reduce
template <int reduceScale = 4096, int blockSize = 256, int cutoffSize = reduceScale * 2, class T>
int parallel_sum(T const* arr, int n) {
    if (n > cutoffSize) {
        std::vector<int, CudaAllocator<int>> sum(n / reduceScale);
        parallel_sum_kernel<blockSize> << <n / reduceScale, blockSize >> > (sum.data(), arr, n);
        return parallel_sum(sum.data(), n / reduceScale);
    }
    else {
        checkCudaErrors(cudaDeviceSynchronize());
        T final_sum = 0;
        for (int i = 0; i < n; i++) {
            final_sum += arr[i];
        }
        return final_sum;
    }
}

// 共享内存
template <int blockSize, class T>
__global__ void parallel_transpose(T* out, T const* in, int nx, int ny) {
    int x = blockIdx.x * blockSize + threadIdx.x;
    int y = blockIdx.y * blockSize + threadIdx.y;
    if (x >= nx || y >= ny) return;
    __shared__ T tmp[blockSize * blockSize];
    int rx = blockIdx.y * blockSize + threadIdx.x;
    int ry = blockIdx.x * blockSize + threadIdx.y;
    tmp[threadIdx.y * blockSize + threadIdx.x] = in[ry * nx + rx];
    __syncthreads();
    out[y * nx + x] = tmp[threadIdx.x * blockSize + threadIdx.y];
}

void testArray() {
    int n = 65535;
    int* arr;
    checkCudaErrors(cudaMallocManaged(&arr, n * sizeof(int)));

    int nthreads = 128;
    int nblocks = (n + nthreads + 1) / nthreads;
    kernel_array << <nblocks, nthreads >> > (arr, n);

    checkCudaErrors(cudaDeviceSynchronize());
    for (int i = 0; i < n; i++) {
        printf("arr[%d]: %d\n", i, arr[i]);
    }

    cudaFree(arr);
}

void testVector() {
    int n = 65536;
    std::vector<int, CudaAllocator<int>> arr(n);

    parallel_for << <32, 128 >> > (n, [arr = arr.data()] __device__(int i) {
        arr[i] = i;
    });

    checkCudaErrors(cudaDeviceSynchronize());
    for (int i = 0; i < n; i++) {
        printf("arr[%d] = %d\n", i, arr[i]);
    }

}

void testMath() {
    int n = 1 << 25;
    std::vector<float, CudaAllocator<float>> gpu(n);
    std::vector<float> cpu(n);

    for (int i = 0; i < n; i++) {
        cpu[i] = sinf(i);
    }

    parallel_for << <n / 512, 128 >> > (n, [gpu = gpu.data()] __device__(int i) {
        gpu[i] = __sinf(i);
    });
    checkCudaErrors(cudaDeviceSynchronize());

    //for (int i = 0; i < n; i++) {
        //printf("diff %d = %f\n", i, gpu[i] - cpu[i]);
    //}
}

void testTrust() {
    int n = 65536;
    thrust::universal_vector<float> x(n);   // CPU或GPU
    thrust::host_vector<float> x_host(n);   // CPU
    thrust::device_vector<float> x_dev = x_host;    // GPU

    thrust::for_each(x_dev.begin(), x_dev.end(), [] __device__(float& x) {
        x += 100.f;
    });
}

void testAtomic() {
    // atomicAdd(dst, src) 
    // atomicSub
    // atomicOr
    // atomicAnd
    // atomicXor
    // atomicMax
    // atomicMin
    // atomicExch   old = *dst; *dst = src
    // atomicCAS    automic::exchange
}

void test_share() {
    int nx = 1 << 14, ny = 1 << 14;
    std::vector<int, CudaAllocator<int>> in(nx * ny);
    std::vector<int, CudaAllocator<int>> out(nx * ny);
    parallel_transpose<32> << <dim3(nx / 32, ny / 32, 1), dim3(32, 32, 1) >> >
        (out.data(), in.data(), nx, ny);
}

// host 可以调用 global;global 可以调用 device;device 可以调用 device
// 实际上 GPU 的板块相当于 CPU 的线程,GPU 的线程相当于 CPU 的SIMD
// 数学函数sqrtf,rsqrtf,cbrtf,rcbrtf,powf,sinf,cosf,sinpif,cospif,sincosf,sincospif,logf,log2f,log10f,expf,exp2f,exp10f,tanf,atanf,asinf,acosf,fmodf,fabsf,fminf,fmaxf
// 低精度,高性能 __sinf,__expf、__logf、__cosf、__powf, __fdividef(x, y) 
// old = atomicAdd(dst, src) 相当于 old = *dst; *dst += src 
int main() {
    // 三重尖括号里的,第一个参数表示板块数量,第二个参数决定着启动 kernel 时所用 GPU 的线程数量
    kernel<<<1, 1>>>(); // CPU调用GPU函数异步执行
    kernel2<<<dim3(2, 1, 1), dim3(2, 2, 2)>>>();
    cudaDeviceSynchronize(); // CPU等待GPU完成
    say_hello_host();

    int* pret;
    checkCudaErrors(cudaMallocManaged(&pret, sizeof(int)));
    kernel_memory << <1, 1 >> > (pret);
    checkCudaErrors(cudaDeviceSynchronize());
    printf("result: %d\n", *pret);
    cudaFree(pret);

    testArray();

    float sin10 = sinf(10.f);

    /// map-reduce
    int n = 1 << 24;
    std::vector<int, CudaAllocator<int>> arr(n);
    int final_sum = parallel_sum(arr.data(), n);
    return 0;
}

参考

cuda-samples/Common/helper_string.h at 5f97d7d0dff880bc6567faa4c5e62e389a6d6999 · NVIDIA/cuda-samples · GitHub

 https://github.com/NVIDIA/cuda-samples/blob/5f97d7d0dff880bc6567faa4c5e62e389a6d6999/Common/helper_cuda.h#L31

https://www.nvidia.cn/docs/IO/51635/NVIDIA_CUDA_Programming_Guide_1.1_chs.pdf

https://developer.download.nvidia.cn/CUDA/training/StreamsAndConcurrencyWebinar.pdf

CUDA Pro Tip: Write Flexible Kernels with Grid-Stride Loops | NVIDIA Technical Blog


创作不易,小小的支持一下吧!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1847916.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

手写方法实现字符串例如:“123“与整型例如:123相互转化(面试必会)

目录 二、字符串类型转化为整型 1. 初始化变量 2.定义字符串索引值 3.思考如何将字符1转化为数字1 4. 转化思路 5.考虑字符串转化负数例&#xff1a;-123456 6.完整代码 四、最后 一、前言 在c语言和c中&#xff0c;有许许多多的数据类型相互转化的方法&#xff0c;这里…

CARIS HIPS and SIPSv12 是专业的多波束水深数据和声呐图像处理软件

CARIS HIPS and SIPS是专业的多波束水深数据和声呐图像处理软件。CARIS HIPS and SIPS适用于海洋应用需求。其可靠性和可用性对多波束水深数据处理和声呐图像都是很重要的。CARIS HIPS用于处理多波束水深数据&#xff0c;CARIS SIPS用于处理侧扫声呐图像和多波束背向散射回波数…

卫星智慧停车导航系统有哪些优势

卫星智慧停车导航系统凭借其先进的技术和创新的理念&#xff0c;为现代城市停车问题提供了有效的解决方案。以下是卫星智慧停车导航系统的主要优势&#xff1a; 一、实时性与准确性 卫星智慧停车导航系统通过集成全球卫星定位系统(GPS)和地理信息系统(GIS)&#xff0c;能够实时…

Redis 持久化策略

Redis 提供了多种持久化机制&#xff0c;用于将数据保存到磁盘中&#xff0c;以防止因服务器重启或故障而导致的数据丢失。主要的持久化策略有两种&#xff1a;RDB (Redis Database) 和 AOF (Append Only File)&#xff0c;即当 Redis 服务器重新启动时&#xff0c;会读取相应的…

自研地面站!自主开源无人飞行系统 Prometheus V2 版重大升级详解

自主开源无人飞行系统 Prometheus V2 相对于 Prometheus V1 在多方面做了重大的升级&#xff0c;今天我们将聊聊 Prometheus V2 的地面站升级。 地面站的重大提升 熟悉 Prometheus 的小伙伴们可能知道&#xff0c;V1 版本是没有专门的地面站的。而在 Prometheus V2 中&#x…

【MAVEN学习 | 第2篇】Maven工程创建及核心功能

文章目录 一. 基于IDEA的Maven工程创建1.1 Maven工程GAVP属性&#xff08;1&#xff09;GroupID 格式&#xff08;2&#xff09;ArtifactID 格式&#xff08;3&#xff09;Version版本号格式&#xff08;4&#xff09;Packaging定义规则 1.2 IDEA构建Maven JavaSE工程1.3 IDEA构…

大模型培训 AUTOWEBGLM:自动网页导航智能体

大语言模型&#xff08;LLMs&#xff09;在智能代理任务中发挥着重要作用&#xff0c;尤其是在网络导航方面。然而&#xff0c;现有的代理在真实世界的网页上表现不佳&#xff0c;主要原因网络导航代理面临着三大挑战&#xff1a;网页上行动的多样性、HTML文本的处理限制以及开…

数据结构6---树

一、定义 树(Tree)是n(n>0)个结点的有限集。当n0时成为空树,在任意一棵非空树中: 1、有且仅有一个特定的称为根(Root)的结点; 2、当n>1时,其余结点可分为m(m>日)个互不相交的有限集T1、T2、...、 Tm&#xff0c;其中每一个集合本身又是一棵树&#xff0c;并且称为根的…

模块化沙箱

模块化沙箱是什么&#xff1f;模块化沙箱有什么作用&#xff1f; 模块化沙箱是一种高灵活性和高扩展性的数据安全产品&#xff0c;通过选择不同的沙箱模块&#xff0c;满足不同的安全需求。 同时&#xff0c;模块化沙箱也是零信任的重要一环&#xff0c;根据企事业单位各类国…

11、鸿蒙学习—UDID获取方法

一、手机的UDID获取方法如下&#xff1a; 1、打开“设置 > 关于手机”&#xff0c;多次点击版本号&#xff0c;打开开发者模式。 2、打开“设置 > 系统和更新”&#xff0c;在最下方找到“开发人员选项”&#xff0c;打开“USB调试”开关。 3、使用PC连接手机后&#…

网红和主播们是用的什么美颜工具?深入剖析美颜sdk与美颜Api

在现代社交媒体和直播平台的兴起中&#xff0c;网红和主播们依靠精美的外表吸引大量观众&#xff0c;获得高人气和收益已成为常态。这其中&#xff0c;美颜工具起到了至关重要的作用。这篇文章将深入剖析网红和主播们常用的美颜工具&#xff0c;特别是美颜SDK和美颜API的原理和…

想要成为程序员,首先你需要掌握这这三种编程语言!

作为程序员&#xff0c;掌握多种编程语言是非常有价值的&#xff0c;因为不同的编程语言有不同的优势和适用场景。然而&#xff0c;要指定“必须掌握”的三种编程语言是相当主观的&#xff0c;因为这取决于个人的职业目标、所在行业的需求以及技术趋势。不过&#xff0c;以下三…

NodeJs实现对本地 mysql 数据库的增删改查

写在前面 今天我们接着写nodejs对数据库的操作&#xff0c;今天实现简单的增删改查&#xff0c;读之前请先移步到这里NodeJs 连接本地 mySql 数据库获取数据,避免后续一些代码出险阅读断层。 安装 nodemon npm install nodemon因为 nodejs 的服务是本地启动&#xff0c;避免后…

数据治理工程师CDGA备考心得、时间安排、题库资源

1.写在前面 之前做一些数据质量控制、元数据、主数据相关工作&#xff0c;一直忙于工作&#xff0c;没有去往考证的方面想&#xff0c;去年年底心血来潮就决定考一考&#xff0c;证多不压身嘛&#xff08;也有部分学生向我咨询&#xff09;&#xff0c;资源在文章结尾&#xff…

图说SpringCloudStream消息驱动

SpringCloud Stream消息驱动实现原理 通过定义Binder绑定器作为中间层&#xff0c;实现了应用程序和消息中间件之间实现细节的隔离。通过向应用程序暴露统一的Channel通道&#xff0c;可以让应用程序不再需要考虑各种不同的消息中间件实现的兼容性问题。当需要升级消息中间件&a…

【软件测试入门】测试用例经典设计方法 — 因果图法

&#x1f345; 视频学习&#xff1a;文末有免费的配套视频可观看 &#x1f345; 点击文末小卡片 &#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 一、因果图设计测试用例的步骤 1、分析需求 阅读需求文档&#xff0c;如果User Case很复杂&am…

光大证券-放量恰是入市时:成交量择时初探

核心算法 1. 在熊市中&#xff0c;各成交量时序排名出现的频次基本随排名变小而单调增大&#xff1b;在牛市中&#xff0c;各成交量时序排名出现的频次基本随排名变小而单调减少&#xff1b;而在震荡市中&#xff0c;各成交量时序排名出现的频次两头大&#xff0c;中间小&…

C语言----C语言内存函数

1.memcpy--内存拷贝--使用和模拟实现 //memcpy基本格式&#xff1a; // 目标空间地址 原空间地址 被拷贝的字节个数 //void *memcpy(void * destination, const void * source,size_t num); //因为内存拷贝拷贝的数据有&#xff1a;整型数据、结构…

三丰云免费虚拟主机和免费云服务器评测

今天我要向大家推荐一款非常优秀的云服务提供商&#xff0c;那就是三丰云。三丰云提供了免费虚拟主机和免费云服务器&#xff0c;为用户提供了便捷高效的云计算服务。首先&#xff0c;让我们来看看三丰云的免费虚拟主机服务。三丰云的免费虚拟主机提供了稳定可靠的服务器资源&a…

【ClickHouse】副本、分片集群 (六)

副本 副本的目的主要是保障数据的高可用性&#xff0c;即使一台ClickHouse节点宕机&#xff0c;那么也可以从其他服务器获得相同的数据。 https://clickhouse.tech/docs/en/engines/table-engines/mergetree-family/replication/ 副本写入流程 写入流程如图-18所示: 图-18 写…