CUDA C编程:第一个程序 向量相加点积

我的电脑没有装CUDA,所以使用租了带GPU的云服务器,然后使用vscode SSH远程连接云服务器。云GPU使用的是智星云,0.8元/h。

智星云

可以使用nvcc --version查看系统中安装的CUDA版本。

然后写第一个CUDA程序,两个向量相加结果给到第三个向量

#include <cuda_runtime.h>
#include <iostream>#define CHECK(call) \
{ \const cudaError_t error = call; \if (error != cudaSuccess) { \std::cerr << "Error: " << __FILE__ << ", line " << __LINE__ << ": " \<< cudaGetErrorString(error) << std::endl; \exit(1); \} \
}__global__ void addArrays(const int *A, const int *B, int *C, int N) {int idx = blockIdx.x * blockDim.x + threadIdx.x;if (idx < N)C[idx] = A[idx] + B[idx];
}int main() {const int N = 100; // 数组大小int A[N], B[N], C[N];// 初始化数组A和Bfor(int i = 0; i < N; ++i) {A[i] = i;B[i] = i * 2;}int *d_A, *d_B, *d_C;// 分配GPU内存CHECK(cudaMalloc((void**)&d_A, N * sizeof(int)));CHECK(cudaMalloc((void**)&d_B, N * sizeof(int)));CHECK(cudaMalloc((void**)&d_C, N * sizeof(int)));// 将数据从主机复制到设备CHECK(cudaMemcpy(d_A, A, N * sizeof(int), cudaMemcpyHostToDevice));CHECK(cudaMemcpy(d_B, B, N * sizeof(int), cudaMemcpyHostToDevice));// 调用核函数addArrays<<<10, 10>>>(d_A, d_B, d_C, N);// 同步以确保核函数执行完成cudaDeviceSynchronize();// 将结果从设备复制回主机CHECK(cudaMemcpy(C, d_C, N * sizeof(int), cudaMemcpyDeviceToHost));// 释放GPU内存CHECK(cudaFree(d_A));CHECK(cudaFree(d_B));CHECK(cudaFree(d_C));// 输出结果for(int i = 0; i < N; ++i)std::cout << C[i] << " "; // 应该输出 i + i*2return 0;
}

nvcc -o add add.cu编译程序

./add运行程序 

程序说明

#include <cuda_runtime.h>

引入cuda运行时环境

#define CHECK(call) \
{ \const cudaError_t error = call; \if (error != cudaSuccess) { \std::cerr << "Error: " << __FILE__ << ", line " << __LINE__ << ": " \<< cudaGetErrorString(error) << std::endl; \exit(1); \} \
}

用来提供CUDA报错信息的宏,用CHECK宏嵌套每一个将要调用的函数,便于调试。

#define CHECK(call) 定义了一个名为CHECK的宏,它接受一个参数call,这个参数是想检查的CUDA API调用。接下来的花括号 { ... } 包围了宏展开后将要执行的代码块。

const cudaError_t error=call;执行传入的CUDA API调用(即call),并将其返回的错误状态保存在变量error中。

if(error!=cudaSuccess){...}:检查error是否等于cudaSuccess,这是CUDA中表示操作成功的常量。如果不等于(即操作失败),则执行大括号内的错误处理代码。

std::cerr<< "Error: " <<__FILE__<<", line "<<__LINE__<<": "<<cudaGetErrorString(error)<< std::endl; 这行代码打印错误信息到标准错误输出。包括了出错的文件名(由__FILE__宏提供)、行号(由__LINE__宏提供),以及通过cudaGetErrorString(error)获取的错误描述字符串。exit(1); 如果确实发生了错误,程序会调用exit(1)立即终止,返回码1通常表示异常终止。

__global__ void addArrays(const int *A, const int *B, int *C, int N) {int idx = blockIdx.x * blockDim.x + threadIdx.x;if (idx < N)C[idx] = A[idx] + B[idx];
}

__global__ 是一个关键字,用于声明一个在GPU上执行的函数,也称为全局函数或内核函数。这些函数由主机(CPU)调用,但在设备(GPU)上的多个线程并行执行。

void addArrays(const int *A,const int *B,int *C, int N)定义了内核函数addArrays。

const int *A 和 const int *B指向输入数组A和B的指针,在内核中只读。

int *C输出数组C的指针,存放A和B对应元素的和。

N:需要相加的元素个数。

int idx = blockIdx.x * blockDim.x + threadIdx.x; 计算当前线程的全局索引 idx。

这里是CUDA线程组织方式的一个体现:

blockIdx.x 是当前线程所在的块(block)在网格(grid)中的x轴索引。

blockDim.x 是每个块中线程的数量(块的尺寸)在x轴方向。

threadIdx.x 是当前线程在块内的x轴索引。 通过这样的计算,每个线程都能知道自己在整个计算任务中的唯一位置,从而决定应该处理哪个数组元素。

if (idx < N) 是一个边界检查,确保线程不会访问超过数组界限。因为CUDA会为整个网格启动比实际需要更多的线程以充分利用硬件资源,所以这种检查是必要的。

C[idx] = A[idx] + B[idx]; 如果索引idx在有效范围内,这个语句就执行数组A和B中相应位置的元素相加,并将结果存储到数组C的相同位置。

(这个地方还是没怎么看懂)。

cudaMalloc((void**)&d_A, N * sizeof(int))

给设备分配N个int类型的内存,使用指针变量d_A指示。

cudaMemcpy(d_A, A, N * sizeof(int), cudaMemcpyHostToDevice)

内存拷贝,从Host拷贝到Device。A数组赋值给d_A数组。

计算两个数组的点积

理解CUDA内核调用中的<<< >>>语法。

向量点积计算伪代码

function dotProduct(A, B, N):// 初始化点积结果为0dotProductResult := 0// 遍历两个向量的每个元素并相乘累加for i from 0 to N-1 dodotProductResult := dotProductResult + (A[i] * B[i])

假设有一个简单的CUDA内核函数,用于计算两个数组的点积,并将结果存储在一个变量中。

__global__ void dotProductKernel(const float* A, const float* B, 
float* result, int N) {extern __shared__ float partialSums[];unsigned int tid = threadIdx.x;unsigned int i = blockIdx.x * blockDim.x + threadIdx.x;float sum = 0.0f;if (i < N) {for (unsigned int j = 0; j < N; j++) {sum += A[j * blockDim.x + tid] * B[j * blockDim.x + tid];}}partialSums[tid] = sum;__syncthreads(); // 确保所有线程完成上面的计算// 如果是块内的第一个线程,则累加所有部分和if (tid == 0) {for (unsigned int i = 0; i < blockDim.x; i++) {*result += partialSums[i];}}
}

在这个内核中,我们想要计算两个长度为N的一维数组A和B的点积。为了简化说明,我们忽略了一些优化(如减少共享内存的银行冲突),专注于展示如何调用这个内核。
现在,让我们看看如何使用<<< >>>来调用这个内核函数,并配置执行环境:
 

int main() {const int N = 1024; // 假设数组长度为1024const int blockSize = 256; // 每个块包含256个线程const int gridSize = (N + blockSize - 1) / blockSize; // 计算所需块的数量float *d_A, *d_B, *d_result;float h_result = 0.0f;float h_A[N], h_B[N]; // 主机端的数组// 初始化h_A和h_B数组,略...// 分配和复制数据到GPU,略...// 调用内核函数,注意 <<<gridSize, blockSize, sizeof(float)*blockSize>>> 的用法dotProductKernel<<<gridSize, blockSize, blockSize * sizeof(float)>>>(d_A, d_B, &h_result, N);// 将结果从GPU复制回CPU,略...// 释放GPU资源,略...return 0;
}

dotProductKernel<<<gridSize, blockSize, blockSize * sizeof(float)>>>(d_A, d_B, &h_result, N);是内核调用的实例。

gridSize和blockSize分别定义了执行该内核的网格和块的大小。

gridSize = 4因为1024个元素,每个块处理256个,共需要4个块和blockSize = 256。

第三个参数blockSize * sizeof(float)指定每个块需要的共享内存大小,这里每个线程计算一个部分和,然后存入共享内存,所以我们需要为每个块分配足够大的共享内存来存储这些部分和。

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

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

相关文章

Windows:管理用户账户,密码策略和安全配置

在Windows操作系统中&#xff0c;管理用户账户和密码策略是确保系统安全的关键步骤。本文将探讨如何通过PowerShell和其他Windows工具管理用户账户&#xff0c;包括查看和设置密码策略、检查用户状态&#xff0c;以及导出和导入安全策略。这些管理任务对于系统管理员尤其重要&a…

动态表名 的使用方法

动态表名插件的底层是 拦截器 1&#xff0c;创建一个拦截器 Configuration public class MybatisConfiguration {Beanpublic DynamicTableNameInnerInterceptor dynamicTableNameInnerInterceptor() {// 准备一个Map&#xff0c;用于存储TableNameHandlerMap<String, Table…

centos7.9升级4.19内核

centos默认的内核版本是3.10 通过命令 uname -a 输出系统的详细信息 在部署k8s集群时使用默认的3.10版本的内核&#xff0c;容易出各种奇奇怪怪的问题、可以理解为docker和k8s与该内核版本不兼容&#xff0c;所以在部署k8s集群时&#xff0c;务必要升级内核&#xff0c;这里…

【用文本生成歌声】Learn2Sing 2.0——歌声转换算法及梅尔频谱详解

一. 频谱图与梅尔谱图的介绍 频谱图&#xff1a;频谱图可以理解为一堆垂直堆叠在一起的快速傅里叶变换结果。 1.1 信号 在进入频谱图模块之前&#xff0c;首先我们需要了解信号是什么。 信号就是某一特定量随时间变化&#xff0c;对于音频来说&#xff0c;这个特定的变化量就…

死锁的概念与处理策略

目录 一. 死锁的概念1.1 什么是死锁1.2 死锁、饥饿、死循环的区别1.3 死锁产生的条件*1.4 什么情况下会导致死锁 二. 死锁的处理策略- -预防死锁2.1 破坏互斥条件2.2 破坏不剥夺条件2.3 破坏 请求和保持条件2.4 破坏循环等待条件 三. 死锁的处理策略- -避免死锁(重要)3.1 什么是…

2024.05.10作业

TCP服务器 头文件 #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QTcpServer> #include <QTcpSocket> #include <QList> #include <QMessageBox> #include <QDebug>QT_BEGIN_NAMESPACE namespace Ui { class Widget; …

求阶乘n!末尾0的个数溢出了怎么办

小林最近遇到一个问题&#xff1a;“对于任意给定的一个正整数n&#xff0c;统计其阶乘n&#xff01;的末尾中0的个数”&#xff0c;这个问题究竟该如何解决&#xff1f; 先用n5来解决这个问题。n的阶乘即n!5!5*4*3*2*1120&#xff0c;显然应该为2个数相乘等于10才能得到一个结…

笔记本电脑怎么查看硬盘型号?无需额外软件,五招让你轻松掌握

随着科技的进步&#xff0c;笔记本电脑已经成为我们日常生活和工作中不可或缺的工具。而在选购或维护笔记本电脑时&#xff0c;了解硬盘的型号和性能是至关重要的。本文以windows10系统为例&#xff0c;将向您介绍几招&#xff0c;帮助您轻松掌握查看笔记本电脑硬盘型号的方法。…

福昕PDF阅读器取消手型工具鼠标点击翻页

前言&#xff1a; 本文介绍如何关闭福昕PDF阅读器取消手型工具鼠标点击翻页&#xff0c;因为这样真的很容易误触发PDF翻页&#xff0c;使用起来让人窝火。 引用&#xff1a; NA 正文&#xff1a; 新版的福昕PDF阅读器默认打开了“使用手型工具阅读文章”这个勾选项&#x…

【文化课学习笔记】【物理】功与能

【物理】功与能 功 基础概念 定义 一个物体在力的作用下&#xff0c;沿力的方向&#xff0c;通过一段距离(位移)&#xff0c;则称这个力做了功。 公式 功的定义式&#xff1a; \[W Fx \] 这里的 \(x\) 指的是物体沿力的方向上发生的位移。由于力 \(F\) 和位移 \(x\) 都是矢量&…

BLIP和BLIP2 论文讲解

文章目录 BLIPIntroductionMethod模型架构预训练目标字幕和过滤&#xff08;Capfilt&#xff09; BLIP2IntroductionMethod模型结构Q-Former预训练第一阶段Q-Former预训练第二阶段 BLIP 论文&#xff1a; 《BLIP: Bootstrapping Language-Image Pre-training for Unified Visio…

DiskCatalogMaker for Mac:高效管理磁盘文件助手

DiskCatalogMaker for Mac&#xff0c;助您高效管理磁盘文件&#xff0c;让文件整理变得轻而易举&#xff01;这款软件以其出色的性能和人性化的设计&#xff0c;赢得了广大Mac用户的喜爱。 DiskCatalogMaker支持多种磁盘格式&#xff0c;让您轻松管理硬盘、U盘、光盘等存储设备…

景源畅信电商:抖音小店开店的步骤有哪些?

在移动互联网高速发展的今天&#xff0c;利用短视频平台进行商品营销已成为一种潮流。抖音作为其中的佼佼者&#xff0c;吸引了无数商家纷纷入驻&#xff0c;希望通过这个巨大的流量池实现产品推广和销售。然而&#xff0c;想要在抖音上开设一家小店并非想象中那么简单&#xf…

一文带你了解军用电源绝缘性能测试规范和标准

军用电源是指主要用于军事、航空航天等领域的电源模块&#xff0c;因其良好的稳定性和可靠性也在通信、交通、航空航海、加工工业等领域广泛应用。因此&#xff0c;对于军用电源的性能要求比较严格&#xff0c;性能测试是确保电源质量的关键环节。 那么&#xff0c;在测试军品电…

【前端基础】CSS样式+Vue中绘制时间轴

深度选择器 在 Vue.js 中&#xff0c;/deep/、>>>、:deep 和 ::v-deep 这些都是深度选择器&#xff0c;用于修改子组件的样式。它们主要用于解决作用域样式和组件样式之间的冲突问题。 1. /deep/ 或 >>> /deep/ 和 >>> 是相同的选择器&#xff0c;…

谈基于ATTCK框架的攻击链溯源

引言 网络安全在当今数字化时代变得尤为关键&#xff0c;而MITRE公司开发的ATT&CK框架则成为了安全专业人员的重要工具。ATT&CK是一种广泛使用的攻击行为分类和描述框架。其目的在于提供一个共同的语言&#xff0c;使安全专业人员能够更好地理解攻击者的行为和目标&…

并发问题系统学习(更新中)

进程、线程 进程&#xff1a;进程是代码在数据集合上的一次运行活动&#xff0c;是系统进行资源分配和调度的基本单位。可以理解为一个java应用。 线程&#xff1a;线程是进程的一个执行路径&#xff0c;一个进程中至少有一个线程&#xff0c;进程中的多个线程共享进程的资源。…

Qt三方库:QuaZIP介绍、编译和使用

前言 Qt使用一些压缩解压功能&#xff0c;探讨过libzip库&#xff0c;zlib库&#xff0c;libzip库比较原始&#xff0c;还有其他库&#xff0c;都比较基础&#xff0c;而在基础库之上&#xff0c;又有高级封装库&#xff0c;Qt中的QuaZIP是一个很好的选择。Quazip是一个用于压缩…

169.招式拆解 II(unordered_map)

刷算法题&#xff1a; 第一遍&#xff1a;1.看5分钟&#xff0c;没思路看题解 2.通过题解改进自己的解法&#xff0c;并且要写每行的注释以及自己的思路。 3.思考自己做到了题解的哪一步&#xff0c;下次怎么才能做对(总结方法) 4.整理到自己的自媒体平台。 5.再刷重复的类…

使用FFmpeg处理RTSP视频流并搭建RTMP服务器实现图片转直播全流程

目录 一、FFmpeg安装与配置教程二、搭建并配置Nginx RTMP服务器三、从RTSP视频流提取帧并保存为图片四、将图片序列转换为视频五、将视频推送为直播流六、将图片序列推送为直播流 在实时音视频领域&#xff0c;我们经常需要处理从各种源&#xff08;如摄像头&#xff09;获取的…