GPU编程
未读第五章:数学运算经典案例,并行地求 sin 值终于可以在GPU上干点正事了(bushi
就让我们在 GPU 上并行地计算从 sin(0) 到 sin(65535) 的值,并填入到数组 arr 中。
123456789101112131415161718192021222324252627282930#include <cuda_runtime.h>#include <cstdio>#include <vector>#include "CudaAllocator.h" // 封装刚才的内存分配器#include "helper_cuda.h"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) { ...
GPU编程
未读前言
Windows11或Windows 10 21H2版本以上操作系统,具有 Nvidia 的 GPU,且已安装显卡驱动
安装了WSL2,且准备好了Ubuntu或其他Linux操作系统
之后的操作都将基于WSL2中的Ubuntu环境。
环境验证打开Windows的Powershell,输入nvidia-smi检查自己的显卡驱动、CUDA支持。
注意左上角的 CUDA version 12.6 指的是当前环境所支持的CUDA的最高版本。如图,我当前的环境最高支持到12.6
目前我只用这个环境学习、测试CUDA编程,因此可以直接选择CUDA最新版本12.6
如果还需要使用Pytorch、TensorRT等深度学习相关内容,请先确认这些环境的需求。
原因是,CUDA一般更新后,Pytorch、TensorRT过一段时间才会跟进更新。防止出现装好CUDA后,发现Pytorch没有对应版本的尴尬。
之后进入WSL2的Linux环境,再次输入nvidia-smi验证WSL2能否正常连接 GPU
Windows 11 和 Windows 10 版本 21H2 已经支持在 WSL2 实例内 ...
GPU编程
未读第四章:C++封装GPU上的数组std::vector的秘密:第二模板参数**你知道吗?**std::vector 作为模板类,其实有两个模板参数:
1std::vector<T, AllocatorT>
那为什么我们平时只用了 std::vector 呢?因为第二个参数默认是 std::allocator。
也就是 std::vector 等价于 std::vector<T, std::allocator>。
std::allocator 的功能是负责分配和释放内存,初始化 T 对象等等。
它具有如下几个成员函数:
123T *allocate(size_t n) // 分配长度为n,类型为T的数组,返回其起始地址void deallocate(T *p, size_t n) // 释放长度为n,起始地址为p,类型为T的数组
抽象的 std::allocator 接口vector 会调用 std::allocator 的 allocate/deallocate 成员函数,他又会去调用标准库的 malloc/free 分配和释放内存空间( ...
GPU编程
未读第三章:GPU上的数组分配一个数组1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556#include <cuda_runtime.h>#include <cstdio>#include "helper_cuda.h"__global__ void kernel(int *arr, int n) { for (int i = 0; i < n; i++) { arr[i] = i; }}int main() { int n = 32; int *arr; checkCudaErrors(cudaMallocManaged(&arr, n * sizeof(int))); kernel<<<1, 1>>>(arr, n); checkCudaErrors(cudaDevice ...
GPU编程
未读第二章:内存管理怎样从核函数返回数据?我们试着把 kernel 的返回类型声明为 int,试图从 GPU 返回数据到 CPU。
123456789101112#include <cuda_runtime.h>#include <cstdio>__global__ int kernel() { return 42; }int main() { int ret = kernel<<<1, 1>>>(); cudaDeviceSynchronize(); printf("%d\n", ret); return 0;}
但发现这样做会在编译期出错,为什么?
刚刚说了 kernel 的调用是异步的,返回的时候,并不会实际让 GPU 把核函数执行完毕,必须 cudaDeviceSynchronize() 等待它执行完毕(和线程的 join 很像)。
所以,不可能从 kernel 里通过返回值获取 GPU 数据,因为 kernel 返回时核函数并没有真正在 GPU 上执 ...
GPU编程
未读第一章:线程与板块三重尖括号里的数字刚刚说了 CUDA 的核函数调用时需要用 kernel<<<1, 1>>>() 这种奇怪的语法,这里面的数字代表什么意思呢?
不妨把 <<<1, 1>>> 改成 <<<1, 3>>> 试试看。你会看到 Hello, world! 打印了三遍!
123456789101112131415#include <cstdio>#include <cuda_runtime.h>__global__ void kernel(){ printf("Hello, world\n");}int main(){ kernel<<<1, 3>>>(); cudaDeviceSynchronize(); return 0;}// Hello, world!// Hello, world!// Hello, world!
原来 ...
GPU编程
未读CUDA开始的GPU编程前置条件:
熟悉C/C++编程、熟悉STL、函数模板等
Nvidia GTX900及以上显卡、CUDA 11及以上
CMake 3.18及以上
由于文本编辑器不持支CUDA代码块,文中CUDA代码将使用cpp代码块进行高亮显示,请注意区分。
在开始之前,我想提醒读者,这篇博客将以工程应用的思路为主,不会深入探讨CUDA的底层原理。我们关注实际的使用案例和实践技巧。未来,我会逐步更新更为详尽的内容,敬请期待!
第0章:Hello, world from GPU!CMake中启用CUDA支持123456789# CMakeLists.txtcmake_minimum_required(VERSION 3.10)set(CMAKE_CXX_STANDARD 17)set(CMAKE_BUILD_TYPE Release)project(hellocuda LANGUAGES CXX CUDA) # 添加CUDA支持add_executable(main main.cu) # 添加.cu源文件
最新版的 CMake(3.18 以上),只需在 LANGUA ...
迭代器的分类
迭代器类型
描述
提供的运算符重载
具有此迭代器的容器
相应的 C++20 concept
输入迭代器
只读访问序列,允许单次读取每个元素
*(可读取),!=,==,++(一次性)
istream_iterator
input_iterator
输出迭代器
只写访问序列,允许将数据写入到序列中
*(可写入),!=,==,++(一次性)
back_insert_iterator
output_iterator
前向迭代器
允许读写访问,支持多次读取同一元素
*,!=,==,++
forward_list
forward_iterator
双向迭代器
支持前向和后向遍历
*,!=,==,++,–
set,map,list
bidirectional_iterator
随机访问迭代器
支持直接访问序列中的任意元素,提供随机访问能力
*,!=,==,++,–,+,-,+=,-=,[]
v ...
CPP
未读C++ 标准模板库(Standard Template Library,STL)是一套功能强大的 C++ 模板类和函数的集合,它提供了一系列通用的、可复用的算法和数据结构。
STL 的设计基于泛型编程,这意味着使用模板可以编写出独立于任何特定数据类型的代码。
STL 分为多个组件,包括容器(Containers)、迭代器(Iterators)、算法(Algorithms)、函数对象(Function Objects)和适配器(Adapters)等。
使用 STL 的好处:
代码复用:STL 提供了大量的通用数据结构和算法,可以减少重复编写代码的工作。
性能优化:STL 中的算法和数据结构都经过了优化,以提供最佳的性能。
泛型编程:使用模板,STL 支持泛型编程,使得算法和数据结构可以适用于任何数据类型。
易于维护:STL 的设计使得代码更加模块化,易于阅读和维护。
C++ 标准模板库的核心包括以下重要组件组件:
组件
描述
容器(Containers)
容器是 STL 中最基本的组件之一,提供了各种数据结构,包括向量(vector)、链表(list)、队列(queue) ...
vector容器构造显式构造指定一个参数 explicit vector(size_t n)1234567#include <vector>using namespace std;int main(){ vector<int> a; return 0;}
vector 的功能是长度可变的数组,他里面的数据存储在堆上。
使用sizeof(vector)会得到vector的大小是24,即三个指针的大小
第一个指针指向堆上内存的起始地址
第二个指针标志着有效元素结束位置
第三个指针指向分配给vector的总容量位置,表示vector可以存储多少元素而不需要重新分配内存
vector 是一个模板类,第一个模板参数是数组里元素的类型。
例如,声明一个元素是 int 类型的动态数组 a:vector<int> a;
1234567891011#include <vector>#include <iostream>using namespace std;int main(){ v ...