当前位置: 首页 > news >正文

我的第一个CUDA程序

MatAdd算法

实现两个矩阵对应元素相加

 

#include <stdio.h>
#include <stdlib.h>// 矩阵加法函数
void MatAdd(int height, int width)
{// 在主机内存中为 A、B 和 C 分配内存float* A = (float*)malloc(height * width * sizeof(float));float* B = (float*)malloc(height * width * sizeof(float));float* C = (float*)malloc(height * width * sizeof(float));// 初始化输入矩阵 A 和 Bfor (int i = 0; i < height; i++){for (int j = 0; j < width; j++){// 例如,初始化为 i+j 的和*(A + i * width + j) = i + j;*(B + i * width + j) = i - j;}}// 执行矩阵加法运算,将结果存储在矩阵 C 中for (int i = 0; i < height; i++){for (int j = 0; j < width; j++){*(C + i * width + j) = *(A + i * width + j) + *(B + i * width + j);}}// 输出结果矩阵 Cprintf("Matrix C (result of A + B):\n");for (int i = 0; i < height; i++){for (int j = 0; j < width; j++){printf("%f ", *(C + i * width + j));}printf("\n");}// 释放分配的内存free(A);free(B);free(C);
}int main()
{int height = 3; // 矩阵的高度int width = 3;  // 矩阵的宽度// 执行矩阵加法MatAdd(height, width);return 0;
}

 输出:

Matrix C (result of A + B):
0.000000 0.000000 0.000000 
2.000000 2.000000 2.000000 
4.000000 4.000000 4.000000 

MatAdd算法的GPU实现

MatAdd算法的GPU实现

  • CPU端为输入矩阵A和B、输出矩阵C分配空间,并进行初始化
  • CPU端分配设备端内存,并将A和B传输到GPU上
  • 定义数据和线程的映射关系,并确定线程的开启数量和组织方式
  1. 每个线程负责输出矩阵C的一个元素的计算,全局ID为(i,j)的线程计算索引为(i,j)的矩阵元素
  2. 当矩阵规模为width*height时,共开启width * height个线程
  3. 每个block包含256个线程,采用(16,16)的组织形式
  • 编写计算kernel,完成计算任务
  • CPU端将计算结果从Device内存拷贝到Host内存

内存分配 数据传输

开启线程 启动kernel 结果返回

GPU kernel

#include <stdio.h>
#include <cuda_runtime.h>// CUDA 核函数,用于矩阵加法
__global__ void MatAddKernel(float* A, float* B, float* C, int height, int width) {// 获取线程的全局IDint i = blockIdx.x * blockDim.x + threadIdx.x; // 计算全局行索引int j = blockIdx.y * blockDim.y + threadIdx.y; // 计算全局列索引// 确保索引在矩阵范围内if (i < width && j < height) {// 计算当前线程对应的元素索引int index = j * width + i;// 从矩阵 A 和 B 中读取数据float src_data_A = A[index];float src_data_B = B[index];// 执行加法运算float result = src_data_A + src_data_B;// 将结果写入矩阵 CC[index] = result;}
}void MatAdd(int height, int width) {// 在主机内存中分配 A、B 和 Cfloat* A = (float*)malloc(height * width * sizeof(float));float* B = (float*)malloc(height * width * sizeof(float));float* C = (float*)malloc(height * width * sizeof(float));// 初始化输入矩阵 A 和 Bfor (int i = 0; i < height; i++) {for (int j = 0; j < width; j++) {A[i * width + j] = i + j; // 简单初始化,A的元素为行索引+列索引B[i * width + j] = i - j; // 简单初始化,B的元素为行索引-列索引}}// 第一步:在设备内存中为矩阵 A、B 和 C 分配内存float* d_A;cudaMalloc(&d_A, height * width * sizeof(float)); // 分配矩阵 A 的设备内存float* d_B;cudaMalloc(&d_B, height * width * sizeof(float)); // 分配矩阵 B 的设备内存float* d_C;cudaMalloc(&d_C, height * width * sizeof(float)); // 分配矩阵 C 的设备内存// 第二步:将矩阵 A 和 B 从主机内存复制到设备内存cudaMemcpy(d_A, A, height * width * sizeof(float), cudaMemcpyHostToDevice); // 复制 AcudaMemcpy(d_B, B, height * width * sizeof(float), cudaMemcpyHostToDevice); // 复制 B// 第三步:调用 CUDA 核函数dim3 threadsPerBlock(16, 16); // 定义每个块中的线程数dim3 numBlocks((width + threadsPerBlock.x - 1) / threadsPerBlock.x, (height + threadsPerBlock.y - 1) / threadsPerBlock.y); // 计算网格中的块数MatAddKernel<<<numBlocks, threadsPerBlock>>>(d_A, d_B, d_C, height, width); // 启动 CUDA 核函数// 第四步:将结果从设备内存复制回主机内存cudaMemcpy(C, d_C, height * width * sizeof(float), cudaMemcpyDeviceToHost);// 输出结果矩阵 Cprintf("Matrix C (result of A + B):\n");for (int i = 0; i < height; i++) {for (int j = 0; j < width; j++) {printf("%f ", C[i * width + j]);}printf("\n");}// 释放设备内存cudaFree(d_A);cudaFree(d_B);cudaFree(d_C);// 释放主机内存free(A);free(B);free(C);
}int main() {int height = 3; // 矩阵的高度int width = 3;  // 矩阵的宽度// 调用矩阵加法函数MatAdd(height, width);return 0;
}

输出:

Matrix C (result of A + B):
0.000000 0.000000 0.000000 
2.000000 2.000000 2.000000 
4.000000 4.000000 4.000000 

CUDA程序编译

CUDA程序性能测试

 使用 CUDA GPU Timers 实际要循环100次求平均值

#include <stdio.h>
#include <cuda_runtime.h>// CUDA 核函数,用于矩阵加法
__global__ void MatAddKernel(float* A, float* B, float* C, int height, int width) {// 获取线程的全局IDint i = blockIdx.x * blockDim.x + threadIdx.x; // 计算全局行索引int j = blockIdx.y * blockDim.y + threadIdx.y; // 计算全局列索引// 确保索引在矩阵范围内if (i < width && j < height) {// 计算当前线程对应的元素索引int index = j * width + i;// 从矩阵 A 和 B 中读取数据float src_data_A = A[index];float src_data_B = B[index];// 执行加法运算float result = src_data_A + src_data_B;// 将结果写入矩阵 CC[index] = result;}
}void MatAdd(int height, int width) {// 在主机内存中分配 A、B 和 Cfloat* A = (float*)malloc(height * width * sizeof(float));float* B = (float*)malloc(height * width * sizeof(float));float* C = (float*)malloc(height * width * sizeof(float));// 初始化输入矩阵 A 和 Bfor (int i = 0; i < height; i++) {for (int j = 0; j < width; j++) {A[i * width + j] = i + j; // 简单初始化,A的元素为行索引+列索引B[i * width + j] = i - j; // 简单初始化,B的元素为行索引-列索引}}// 第一步:在设备内存中为矩阵 A、B 和 C 分配内存float* d_A;cudaMalloc(&d_A, height * width * sizeof(float)); // 分配矩阵 A 的设备内存float* d_B;cudaMalloc(&d_B, height * width * sizeof(float)); // 分配矩阵 B 的设备内存float* d_C;cudaMalloc(&d_C, height * width * sizeof(float)); // 分配矩阵 C 的设备内存// 第二步:将矩阵 A 和 B 从主机内存复制到设备内存cudaMemcpy(d_A, A, height * width * sizeof(float), cudaMemcpyHostToDevice); // 复制 AcudaMemcpy(d_B, B, height * width * sizeof(float), cudaMemcpyHostToDevice); // 复制 B// 第三步:定义事件变量,用于测量核函数执行时间cudaEvent_t start, stop;float time;cudaEventCreate(&start); // 创建开始事件cudaEventCreate(&stop);  // 创建停止事件// 第四步:调用 CUDA 核函数dim3 threadsPerBlock(16, 16); // 定义每个块中的线程数dim3 numBlocks((width + threadsPerBlock.x - 1) / threadsPerBlock.x, (height + threadsPerBlock.y - 1) / threadsPerBlock.y); // 计算网格中的块数cudaEventRecord(start, 0); // 记录开始时间MatAddKernel<<<numBlocks, threadsPerBlock>>>(d_A, d_B, d_C, height, width); // 启动 CUDA 核函数cudaEventRecord(stop, 0);  // 记录结束时间cudaEventSynchronize(stop); // 等待事件完成cudaEventElapsedTime(&time, start, stop); // 计算核函数执行时间printf("Kernel execution time: %f ms\n", time); // 输出核函数执行时间// 销毁事件cudaEventDestroy(start);cudaEventDestroy(stop);// 第五步:将结果从设备内存复制回主机内存cudaMemcpy(C, d_C, height * width * sizeof(float), cudaMemcpyDeviceToHost);// 输出结果矩阵 Cprintf("Matrix C (result of A + B):\n");for (int i = 0; i < height; i++) {for (int j = 0; j < width; j++) {printf("%f ", C[i * width + j]);}printf("\n");}// 释放设备内存cudaFree(d_A);cudaFree(d_B);cudaFree(d_C);// 释放主机内存free(A);free(B);free(C);
}int main() {int height = 3; // 矩阵的高度int width = 3;  // 矩阵的宽度// 调用矩阵加法函数MatAdd(height, width);return 0;
}

输出: 

Kernel execution time: 0.086016 ms
Matrix C (result of A + B):
0.000000 0.000000 0.000000 
2.000000 2.000000 2.000000 
4.000000 4.000000 4.000000 

 使用 CPU Timers 实际要循环100次求平均值

#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h> // 包含 gettimeofday 的头文件
#include <cuda_runtime.h>// CUDA 核函数,用于矩阵加法
__global__ void MatAddKernel(float* A, float* B, float* C, int height, int width) {// 获取线程的全局IDint i = blockIdx.x * blockDim.x + threadIdx.x; // 计算全局行索引int j = blockIdx.y * blockDim.y + threadIdx.y; // 计算全局列索引// 确保索引在矩阵范围内if (i < width && j < height) {// 计算当前线程对应的元素索引int index = j * width + i;// 从矩阵 A 和 B 中读取数据float src_data_A = A[index];float src_data_B = B[index];// 执行加法运算float result = src_data_A + src_data_B;// 将结果写入矩阵 CC[index] = result;}
}void MatAdd(int height, int width) {// 在主机内存中分配 A、B 和 Cfloat* A = (float*)malloc(height * width * sizeof(float));float* B = (float*)malloc(height * width * sizeof(float));float* C = (float*)malloc(height * width * sizeof(float));// 初始化输入矩阵 A 和 Bfor (int i = 0; i < height; i++) {for (int j = 0; j < width; j++) {A[i * width + j] = i + j; // 简单初始化,A的元素为行索引+列索引B[i * width + j] = i - j; // 简单初始化,B的元素为行索引-列索引}}// 第一步:在设备内存中为矩阵 A、B 和 C 分配内存float* d_A;cudaMalloc(&d_A, height * width * sizeof(float)); // 分配矩阵 A 的设备内存float* d_B;cudaMalloc(&d_B, height * width * sizeof(float)); // 分配矩阵 B 的设备内存float* d_C;cudaMalloc(&d_C, height * width * sizeof(float)); // 分配矩阵 C 的设备内存// 第二步:将矩阵 A 和 B 从主机内存复制到设备内存cudaMemcpy(d_A, A, height * width * sizeof(float), cudaMemcpyHostToDevice); // 复制 AcudaMemcpy(d_B, B, height * width * sizeof(float), cudaMemcpyHostToDevice); // 复制 B// 第三步:定义时间测量变量struct timeval start, end;double elapsedTime;// 记录开始时间gettimeofday(&start, NULL);// 启动 CUDA 核函数dim3 threadsPerBlock(16, 16); // 定义每个块中的线程数dim3 numBlocks((width + threadsPerBlock.x - 1) / threadsPerBlock.x, (height + threadsPerBlock.y - 1) / threadsPerBlock.y); // 计算网格中的块数MatAddKernel<<<numBlocks, threadsPerBlock>>>(d_A, d_B, d_C, height, width); // 启动 CUDA 核函数cudaDeviceSynchronize(); // 等待核函数完成// 记录结束时间gettimeofday(&end, NULL);// 计算时间差elapsedTime = (end.tv_sec - start.tv_sec) * 1000.0; // 秒转毫秒elapsedTime += (end.tv_usec - start.tv_usec) / 1000.0; // 微秒转毫秒// 输出核函数执行时间printf("Kernel execution time: %f ms\n", elapsedTime);// 第四步:将结果从设备内存复制回主机内存cudaMemcpy(C, d_C, height * width * sizeof(float), cudaMemcpyDeviceToHost);// 输出结果矩阵 Cprintf("Matrix C (result of A + B):\n");for (int i = 0; i < height; i++) {for (int j = 0; j < width; j++) {printf("%f ", C[i * width + j]);}printf("\n");}// 释放设备内存cudaFree(d_A);cudaFree(d_B);cudaFree(d_C);// 释放主机内存free(A);free(B);free(C);
}int main() {int height = 3; // 矩阵的高度int width = 3;  // 矩阵的宽度// 调用矩阵加法函数MatAdd(height, width);return 0;
}

 输出:

Kernel execution time: 0.101000 ms
Matrix C (result of A + B):
0.000000 0.000000 0.000000 
2.000000 2.000000 2.000000 
4.000000 4.000000 4.000000 

 MatAdd程序之变一:每个线程处理4个元素

  • CPU端为输入矩阵A和B、输出矩阵C分配空间,并进行初始化
  • CPU端分配设备端内存,并将A和B传输到GPU上
  • 定义数据和线程的映射关系,并确定线程的开启数量和组织方式
  1. 每个线程负责输出矩阵C的四个元素的计算,全局ID为(i,j)的线程计算索引为(i,4*j~4*j+3)的矩阵元素
  2. 当矩阵规模为width*height时,共开启width/4 * height个线程
  3. 每个block包含256个线程,采用(16,16)的组织形式
  • 编写计算kernel,完成计算任务
  • CPU端将计算结果从Device内存拷贝到Host内存

#include <cuda_runtime.h>
#include <iostream>// 自定义向上取整除法函数
int div_up(int a, int b) {return (a + b - 1) / b;
}
// CUDA核函数,执行矩阵加法
__global__ void MatAdd(float *A, float *B, float *C, int height, int width)
{// 获取线程的全局IDint i = blockIdx.x * blockDim.x + threadIdx.x;int j = blockIdx.y * blockDim.y + threadIdx.y;// 检查线程是否在有效的计算范围内// 每个线程处理4个连续的元素,因此检查4*i是否在宽度范围内if ((4 * i) < width && j < height){// 获取线程与数据之间的映射关系int index = j * width + 4 * i;// 从源矩阵读取数据float4 src_data_A = *((float4 *)(A + index));float4 src_data_B = *((float4 *)(B + index));// 执行加法运算// 执行加法运算,逐个对float4的分量进行加法float4 result;result.x = src_data_A.x + src_data_B.x;result.y = src_data_A.y + src_data_B.y;result.z = src_data_A.z + src_data_B.z;result.w = src_data_A.w + src_data_B.w;// 将结果写回结果矩阵*((float4 *)(C + index)) = result;}
}// 矩阵加法函数,处理矩阵的加法运算
void MatAdd(int height, int width)
{float *A, *B, *C;         // 主机内存指针float *d_A, *d_B, *d_C;   // 设备内存指针// 分配主机内存A = (float*)malloc(height * width * sizeof(float));B = (float*)malloc(height * width * sizeof(float));C = (float*)malloc(height * width * sizeof(float));// 初始化矩阵A和B(这里省略具体的初始化代码)// 初始化输入矩阵 A 和 Bfor (int i = 0; i < height; i++) {for (int j = 0; j < width; j++) {A[i * width + j] = i + j; // 简单初始化,A的元素为行索引+列索引B[i * width + j] = i - j; // 简单初始化,B的元素为行索引-列索引}}// 分配设备内存cudaMalloc((void**)&d_A, height * width * sizeof(float));cudaMalloc((void**)&d_B, height * width * sizeof(float));cudaMalloc((void**)&d_C, height * width * sizeof(float));// 将数据从主机内存复制到设备内存cudaMemcpy(d_A, A, height * width * sizeof(float), cudaMemcpyHostToDevice);cudaMemcpy(d_B, B, height * width * sizeof(float), cudaMemcpyHostToDevice);// 设置线程块的大小,每个块中有16x16个线程dim3 threadsPerBlock(16, 16);// 计算需要多少个线程块来覆盖整个矩阵区域// 宽度除以4是因为每个线程处理4个元素dim3 numBlocks(div_up(width / 4, threadsPerBlock.x), div_up(height, threadsPerBlock.y));// 调用核函数,执行矩阵加法运算MatAdd<<<numBlocks, threadsPerBlock>>>(d_A, d_B, d_C, height, width);// 将计算结果从设备内存复制到主机内存cudaMemcpy(C, d_C, height * width * sizeof(float), cudaMemcpyDeviceToHost);
// 输出结果矩阵 Cprintf("Matrix C (result of A + B):\n");for (int i = 0; i < height; i++) {for (int j = 0; j < width; j++) {printf("%f ", C[i * width + j]);}printf("\n");}// 释放设备内存cudaFree(d_A);cudaFree(d_B);cudaFree(d_C);// 释放主机内存free(A);free(B);free(C);
}int main() {int height = 8; // 示例矩阵的高度int width = 8;  // 示例矩阵的宽度// 调用矩阵加法函数MatAdd(height, width);return 0;
}

 输出:

Matrix C (result of A + B):
0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 
2.000000 2.000000 2.000000 2.000000 2.000000 2.000000 2.000000 2.000000 
4.000000 4.000000 4.000000 4.000000 4.000000 4.000000 4.000000 4.000000 
6.000000 6.000000 6.000000 6.000000 6.000000 6.000000 6.000000 6.000000 
8.000000 8.000000 8.000000 8.000000 8.000000 8.000000 8.000000 8.000000 
10.000000 10.000000 10.000000 10.000000 10.000000 10.000000 10.000000 10.000000 
12.000000 12.000000 12.000000 12.000000 12.000000 12.000000 12.000000 12.000000 
14.000000 14.000000 14.000000 14.000000 14.000000 14.000000 14.000000 14.000000 

MatAdd程序之变二

矩阵A、B、C都为NxN的方阵,A和B为已知矩阵,C[i][j] = A[i][j] + B[j][i]。

 

  • CPU端为输入矩阵A和B、输出矩阵C分配空间,并进行初始化
  • CPU端分配设备端内存,并将A和B传输到GPU上
  • 定义数据和线程的映射关系,并确定线程的开启数量和组织方式
  1. 每个线程负责输出矩阵C的一个元素的计算,全局ID为(i,j)的线程计算索引为(i,j)的矩阵元素
  2. 当矩阵规模为width*height时,共开启width * height个线程
  3. 每个block包含256个线程,采用(16,16)的组织形式
  • 编写计算kernel,完成计算任务
  • CPU端将计算结果从Device内存拷贝到Host内存

#include <cuda_runtime.h>
#include <iostream>#define N 1024 // 矩阵的大小(N x N)// CUDA核函数,执行矩阵加法运算
__global__ void MatAdd(float *A, float *B, float *C, int height, int width)
{// 获取线程的全局IDint i = blockIdx.x * blockDim.x + threadIdx.x;int j = blockIdx.y * blockDim.y + threadIdx.y;// 确保线程在有效的计算范围内if(i < width && j < height){// 获取矩阵元素的索引int index_A = j * width + i;int index_B = i * width + j;int index_C = index_A;// 从矩阵A和B读取数据并计算结果float src_data_A = *(A + index_A);float src_data_B = *(B + index_B);float result = src_data_A + src_data_B;// 将结果写入矩阵C*(C + index_C) = result;}
}// 主函数,处理矩阵的初始化、调用CUDA核函数以及结果输出
void MatAdd(int height, int width)
{// 在主机内存中为A、B和C分配空间float* A = (float*)malloc(height * width * sizeof(float));float* B = (float*)malloc(height * width * sizeof(float));float* C = (float*)malloc(height * width * sizeof(float));// 初始化矩阵A和Bfor (int i = 0; i < height; i++) {for (int j = 0; j < width; j++) {*(A + i * width + j) = static_cast<float>(i + j); // 示例初始化*(B + i * width + j) = static_cast<float>(i - j); // 示例初始化}}// 分配设备内存float *d_A, *d_B, *d_C;cudaMalloc((void**)&d_A, height * width * sizeof(float));cudaMalloc((void**)&d_B, height * width * sizeof(float));cudaMalloc((void**)&d_C, height * width * sizeof(float));// 将数据从主机内存复制到设备内存cudaMemcpy(d_A, A, height * width * sizeof(float), cudaMemcpyHostToDevice);cudaMemcpy(d_B, B, height * width * sizeof(float), cudaMemcpyHostToDevice);// 配置CUDA网格和线程块大小dim3 threadsPerBlock(16, 16);dim3 numBlocks((width + threadsPerBlock.x - 1) / threadsPerBlock.x,(height + threadsPerBlock.y - 1) / threadsPerBlock.y);// 调用CUDA核函数,执行矩阵加法运算MatAdd<<<numBlocks, threadsPerBlock>>>(d_A, d_B, d_C, height, width);// 将计算结果从设备内存复制回主机内存cudaMemcpy(C, d_C, height * width * sizeof(float), cudaMemcpyDeviceToHost);// 输出部分计算结果用于验证std::cout << "C[0][0] = " << C[0] << std::endl;std::cout << "C[N-1][N-1] = " << C[(N-1) * width + (N-1)] << std::endl;// 释放设备内存cudaFree(d_A);cudaFree(d_B);cudaFree(d_C);// 释放主机内存free(A);free(B);free(C);
}int main() {// 调用矩阵加法函数MatAdd(N, N);return 0;
}

输出:

C[0][0] = 0
C[N-1][N-1] = 2046

MatAdd程序之变三

  •  矩阵A、B都为MxN的矩阵,矩阵C为(M/2)*(N/2)的矩阵, A和B为已知矩阵,C[i][j] = A[2*i][2*j] *B[2*i][2*j] +  A[2*i][2*j+1] *B[2*i][2*j+1]   + A[2*i+1][2*j] *B[2*i+1][2*j]  + A[2*i+1][2*j +1] *B[2*i+1][2*j+1] 。 CPU端为输入矩阵A和B、输出矩阵C分配空间,并进行初始化
  • CPU端分配设备端内存,并将A和B传输到GPU上
  • 定义数据和线程的映射关系,并确定线程的开启数量和组织方式
  1. 每个线程负责输出矩阵C的一个元素的计算,全局ID为(i,j)的线程计算索引为(i,j)的矩阵元素
  2. 当矩阵规模为(M/2)*(N/2)时,共开启(M/2)*(N/2)个线程,每个线程对应A和B的四个元素
  3. 每个block包含256个线程,采用(16,16)的组织形式
  • 编写计算kernel,完成计算任务
  • CPU端将计算结果从Device内存拷贝到Host内存

#include <iostream>
#include <cuda_runtime.h>#define BLOCK_SIZE 16__global__ void MatAdd(float *A, float *B, float *C, int height, int width)
{// Get thread IDint i = blockIdx.x * blockDim.x + threadIdx.x;int j = blockIdx.y * blockDim.y + threadIdx.y;if (i < width / 2 && j < height / 2){// Get the mapping between thread and dataint index_A_1 = 2 * j * width + 2 * i;int index_A_2 = 2 * j * width + 2 * i + 1;int index_A_3 = (2 * j + 1) * width + 2 * i;int index_A_4 = (2 * j + 1) * width + 2 * i + 1;int index_B_1 = index_A_1; // Indexes for matrix B are the same as Aint index_B_2 = index_A_2;int index_B_3 = index_A_3;int index_B_4 = index_A_4;int index_C = j * (width / 2) + i;// Read data from source matricesfloat A1 = A[index_A_1];float A2 = A[index_A_2];float A3 = A[index_A_3];float A4 = A[index_A_4];float B1 = B[index_B_1];float B2 = B[index_B_2];float B3 = B[index_B_3];float B4 = B[index_B_4];// Computefloat result = A1 * B1 + A2 * B2 + A3 * B3 + A4 * B4;// Write resultC[index_C] = result;}
}int main()
{int M = 4; // Example dimensions for the matricesint N = 4;int size_A = M * N * sizeof(float);int size_B = M * N * sizeof(float);int size_C = (M / 2) * (N / 2) * sizeof(float);// Allocate host memoryfloat *h_A = (float*)malloc(size_A);float *h_B = (float*)malloc(size_B);float *h_C = (float*)malloc(size_C);// Initialize matrices A and Bfor (int i = 0; i < M * N; ++i){h_A[i] = i + 1; // Example initializationh_B[i] = i + 1;}// Allocate device memoryfloat *d_A, *d_B, *d_C;cudaMalloc(&d_A, size_A);cudaMalloc(&d_B, size_B);cudaMalloc(&d_C, size_C);// Copy data from host to devicecudaMemcpy(d_A, h_A, size_A, cudaMemcpyHostToDevice);cudaMemcpy(d_B, h_B, size_B, cudaMemcpyHostToDevice);// Define block and grid sizedim3 blockDim(BLOCK_SIZE, BLOCK_SIZE);dim3 gridDim((N / 2 + BLOCK_SIZE - 1) / BLOCK_SIZE, (M / 2 + BLOCK_SIZE - 1) / BLOCK_SIZE);// Launch kernelMatAdd<<<gridDim, blockDim>>>(d_A, d_B, d_C, M, N);// Copy result from device to hostcudaMemcpy(h_C, d_C, size_C, cudaMemcpyDeviceToHost);// Print resultfor (int i = 0; i < M / 2; ++i){for (int j = 0; j < N / 2; ++j){std::cout << h_C[i * (N / 2) + j] << " ";}std::cout << std::endl;}// Free device memorycudaFree(d_A);cudaFree(d_B);cudaFree(d_C);// Free host memoryfree(h_A);free(h_B);free(h_C);return 0;
}

 输出:

66 138 
546 746 
  • 异构计算整成为当前计算领域的重点方向
  • GPGPU是异构计算的主要形式
  • GPGPU是一款大规模细粒度并行处理器,并行思维是进行GPGPU编程的重要前提
  • NVIDIA是当前GPGPU领域当之无愧的霸主
  • GPGPU编程的重点是定义明确的线程和数据间的映射

相关文章:

我的第一个CUDA程序

MatAdd算法 实现两个矩阵对应元素相加 #include <stdio.h> #include <stdlib.h>// 矩阵加法函数 void MatAdd(int height, int width) {// 在主机内存中为 A、B 和 C 分配内存float* A (float*)malloc(height * width * sizeof(float));float* B (float*)malloc…...

workerman下的webman路由浏览器跨域的一种问题

软件版本 "php": ">7.2", "workerman/webman-framework": "^1.5.0",问题情景 使用“分组路由”做API接口前后端分离跨域&#xff0c;在接口测试工具调试是能正常获取数据的&#xff1b;但在网页浏览器上调试就遇到了CORS、404的错…...

Windows11 -MASKRCNN-部署测试

文章目录 Detectron2环境配置搭建python 环境安装Cuda \CUDNN 、PyTorch、 torchvision、cudatoolkit1、Cuda \CUDNN2、 PyTorch、 torchvision、cudatoolkit进入python测试&#xff1a;错误信息 3、detectron2环境在安装detecteron中&#xff0c;遇到报错&#xff1a;编译的时…...

函数(子程序)的常见、易混淆概念详解【对初学者有帮助】

C语⾔中的函数也被称做子程序&#xff0c;意思就是⼀个完成某项特定的任务的⼀小段代码。 C语⾔标准中提供了许多库函数&#xff0c;点击下面的链接可以查看c语言的库函数和头文件。 C/C官⽅的链接&#xff1a;https://zh.cppreference.com/w/c/header 目录 一、函数头与函…...

TiDB-从0到1-DM工具

TiDB从0到1系列 TiDB-从0到1-体系结构TiDB-从0到1-分布式存储TiDB-从0到1-分布式事务TiDB-从0到1-MVCCTiDB-从0到1-部署篇TiDB-从0到1-配置篇TiDB-从0到1-集群扩缩容TiDB-从0到1-数据导出导入TiDB-从0到1-BR工具 一、DM原理 支持全量抽取数据\检测新的数据变化同步到下游实例…...

AppScan——Web 应用安全扫描的得力工具

一、引言 在当今数字化时代&#xff0c;Web 应用成为企业业务的重要支撑&#xff0c;但同时也面临着各种安全威胁。AppScan 作为一款专业的 Web 应用安全扫描工具&#xff0c;为保障 Web 应用的安全性提供了有力的支持。本文将对 AppScan 进行详细介绍&#xff0c;包括其功能、…...

虚幻5|AI行为树,进阶篇

一&#xff0c;打开敌人的角色蓝图&#xff0c;编写以下蓝图&#xff0c;该蓝图只是创建一个敌人并非ai行为树 1.编写蓝图 2.打开主界面&#xff0c;创建一个导航网格体积&#xff0c;上一章都有讲&#xff0c;在添加体积这里面&#xff0c;找到导航网格体积&#xff0c;点击创…...

在 Spring Boot 中配置 Tomcat 监听多个端口

在现代微服务架构中&#xff0c;应用程序可能需要监听多个端口&#xff0c;以支持不同的服务或协议。Spring Boot 提供了灵活的配置选项&#xff0c;使得这一需求变得简单而高效。本文将介绍如何在 Spring Boot 中配置 Tomcat 以监听多个端口&#xff0c;并简要说明其中一些关键…...

stm32f407新建项目工程及烧录

1、新建一个文件夹&#xff0c;打开keil5将项目工程放入文件夹中 2、弹出选择对应型号设备 3、弹出选择对应库 可以看见出现下图&#xff1a;感叹号表示有错 最后如图所示&#xff1a;点击ok就行了 4、创建对应的文件夹存放文件 4、建立main.c 5、添加对应的设置 最后写一个空白…...

c++中加不加const的值传递和引用传递的区别

文章目录 可以修改参数值的比较值传递(int x)和引用传递(int &x)使用const不修改参数值的比较值传递(const int x)和引用传递(const int &x)1. const int x 示例2. const int &x 示例 可以修改参数值的比较值传递(int x)和引用传递(int &x) #include <iost…...

Qt的窗口设置

本文介绍Qt的窗口设置。 采用Qt开发界面程序&#xff0c;会涉及到窗口的设置&#xff0c;如窗口标题栏是否显示&#xff0c;是否有最小&#xff0c;最大化按钮等&#xff0c;窗口当前显示最小化&#xff0c;最大化等。本文简要介绍常用的窗口设置方法。 1.窗口属性 窗口属性…...

51单片机-LCD1602显示屏

简介 是一个液晶显示屏&#xff0c;通过电压对显示区域进行控制&#xff0c;有电就显示。 能够同时显示32个字符&#xff0c;分为两行&#xff0c;一行显示16个字符。可以显示的内容只能是字母、数字或者一些特殊符号。 使用ASCII码来让LCD1602来显示对应的字符。 电路图 …...

多模态分析代理 MAIA:多智能体解决 视觉模型 黑盒问题

多模态分析代理 MAIA&#xff1a;多智能体解决 视觉模型 黑盒问题 论文&#xff1a;https://arxiv.org/pdf/2404.14394 代码&#xff1a;https://github.com/multimodal-interpretability/maia 提出背景 神经网络方法提取的特征&#xff0c;没有可解释性。 数据在通过多个层…...

AT360-6T杭州中科微单频高精度授时模块场景应用

AT360-6T是一款高性能多系统卫星定位授时模块&#xff0c;基于自主研发的北斗多系统SOC芯片&#xff0c;可以同时接收中国的BDS(北斗二号和北斗三号)、美国的GPS、俄罗斯的GLONASS、欧盟的 GALILEO 和日本的QZSS等多个卫星导航系统的GNSS信号来实现多系统联合定位授时&#xff…...

Python酷库之旅-第三方库Pandas(081)

目录 一、用法精讲 336、pandas.Series.str.rpartition方法 336-1、语法 336-2、参数 336-3、功能 336-4、返回值 336-5、说明 336-6、用法 336-6-1、数据准备 336-6-2、代码示例 336-6-3、结果输出 337、pandas.Series.str.slice方法 337-1、语法 337-2、参数 …...

C语言基础⑩——构造类型(结构体)

一、数据类型分类 1、基本类型 整数型 短整型&#xff1a;short&#xff08;2个字节&#xff09;&#xff1b;整型&#xff08;默认&#xff09;&#xff1a;int&#xff08;4个字节&#xff09;&#xff1b;长整型&#xff1a;long&#xff08;8个字节&#xff09;&#xf…...

宝兰德荣获openEuler项目群青铜捐赠人称号,共筑开源生态繁荣新篇章

近日&#xff0c;开放原子开源基金会正式公布了新增捐赠人名单&#xff0c;宝兰德凭借在开源领域的卓越贡献与深厚实力&#xff0c;被授予openEuler项目群青铜捐赠人称号。 开放原子开源基金会是致力于推动全球开源事业发展的非营利机构&#xff0c;于2020年6月在北京成立。开放…...

【Python单元测试】学习笔记3

文章目录 08.PyTest框架什么是PyTestPyTest的优点PyTest的测试环境PyTest常用参数跳过测试 09.PyTest fixture基础PyTest fixture定义和使用引用多个Fixture 10. conftest.pyconftest.py的用途 11. 参数化测试用例为什么需要参数化测试用例使用parameterizer插件实现使用pytest…...

OpenSSL源码编译及Debug

** 1. 环境 Linux 5.19.0-14-generic 22.04.1-Ubuntu 2. 所需工具 gcc version 11.3.0 (Ubuntu 11.3.0-1ubuntu1~22.04) cmake version 3.22.1 3. 步骤 3.1 获取openssl源码 方法可以git clone获得源码&#xff0c;或者直接去GitHub上下载压缩包&#xff0c;GitHub网址&#xf…...

go之goburrow/modbus 学习

goburrow/modbus 是一个用Go语言实现的Modbus协议库&#xff0c;提供了Modbus主机&#xff08;Master&#xff09;和从机&#xff08;Slave&#xff09;的实现&#xff0c;支持两种主要的Modbus传输模式&#xff1a;Modbus TCP和Modbus RTU。 功能介绍 1. 支持的传输模式 Mod…...

开放词汇目标检测(Open-Vocabulary Object Detection, OVOD)算法是什么?

开放词汇目标检测&#xff08;Open-Vocabulary Object Detection, OVOD&#xff09;算法是什么&#xff1f; 随着计算机视觉技术的快速发展&#xff0c;目标检测&#xff08;Object Detection&#xff09;已经在各种应用场景中得到了广泛的应用。然而&#xff0c;传统的目标检…...

【教程】Ubuntu给pycharm添加侧边栏快捷方式

转载请注明出处&#xff1a;小锋学长生活大爆炸[xfxuezhagn.cn] 如果本文帮助到了你&#xff0c;欢迎[点赞、收藏、关注]哦~ 以下教程不仅限于pycharm&#xff0c;其他软件也是一样操作 1、进入到pycharm的目录&#xff0c;先通过命令行打开pycharm&#xff1a; ./bin/pycharm…...

三个月外贸小白好迷茫,该何去何从?

最近看到一个共性的问题&#xff0c;也许对于大多数外贸新人来说&#xff0c;都有过这样的困扰和无力感&#xff0c;也许对于每一个没有强大背景的外贸小伙伴来说&#xff0c;可能都是这样一路成长起来的。 大家好&#xff0c;我是一名普通二本英专生&#xff0c;八月中旬入职…...

MySQL数据库——基本查询(Create)

CRUD&#xff1a;Create&#xff08;创建&#xff09;Retrieve&#xff08;读取&#xff09;Update&#xff08;更新&#xff09;Delete&#xff08;删除&#xff09; 1.Create ①单行数据全列插入 insert [into] table_name [(colume[,colume]……)] values (value_list) […...

spring-security-1-快速入门

1 功能 身份认证(authentication)授权(authorization)防御常见攻击 身份认证&#xff1a;常见账号密码登录&#xff0c;短信登录 授权&#xff1a;什么样的角色&#xff0c;能看见什么菜单&#xff0c;能访问哪些接口。 2 pom <dependency><groupId>org.springf…...

5 大场景上手通义灵码企业知识库 RAG

大家好&#xff0c;我是通义灵码&#xff0c;你的智能编程助手&#xff01;最近我又升级啦&#xff0c;智能问答功能全面升级至 Qwen2&#xff0c;新版本在各个方面的性能和准确性都得到了显著提升。此外&#xff0c;行间代码补全效果也全面优化&#xff0c;多种编程语言生成性…...

免费远程控制电脑的软件有哪些?

什么是远程控制&#xff1f; 远程控制是一种通过网络从一台设备操作另一台设备的技术。连接后&#xff0c;用户可以直接远程操作那台电脑进行各种操作。随着科技的不断进步和用户需求的增加&#xff0c;远程控制市场日益蓬勃。远程控制不仅应用于远程办公和远程教学&#xff0…...

Linux软件包yum

目录 Linux软件包管理器 yum关于rzsz注意事项查看软件包如何安装软件卸载命令 Linux开发工具Linux编辑器-vim使用1. vim的基本概念2. vim的基本操作3. vim正常模式命令集4. vim末行模式命令集5. vim操作总结 小彩蛋 Linux软件包管理器 yum 软件包 在Linux下安装软件&#xff…...

网页的切换与嵌套

网页的切换与嵌套 网页的切换 在浏览器窗口中如果点击超链接标签会在当前的浏览器窗口中显示新的数据&#xff0c;但有些超链接标签点击后却会在一个新的窗口显示数据&#xff0c;这种情况下就无法对新的开的窗口页面进行操作了。 基于这种情况&#xff0c;我们就需要使用dri…...

基于飞桨框架的稀疏计算使用指南

本文作者-是 Yu 欸&#xff0c;华科在读博士生&#xff0c;定期记录并分享所学知识&#xff0c;博客关注者5w。本文将详细介绍如何在 PaddlePaddle 中利用稀疏计算应用稀疏 ResNet&#xff0c;涵盖稀疏数据格式的础知识、如何创建和操作稀疏张量&#xff0c;以及如何开发和训练…...

中国建设银行网站的机构/优化百度seo

一&#xff0c;建立 git 帐户1&#xff0c;在用做服务器的机器 Server 上建立 git 帐户。咱们能够经过 System Preferences->accounts 来添加。在这里我添加一个 git 的 administrator 帐户&#xff0c;administrator 不是必须的&#xff0c;在这里仅仅为了方便。2&#xff…...

什么是营销型的网站推广/网站制作公司网站

数据科学项目中使用Python编程语言的每个人的重要文章在Medium上&#xff0c;这个主题没有很好地介绍&#xff0c;因此我决定以一种易于理解的方式概述Python数据结构的时间复杂性。为什么我们需要知道时间复杂性&#xff1f;对于数据科学家程序员而言&#xff0c;为工作选择正…...

做3个网站需要多大的服务器/常州seo外包

visual studio code 主题颜色 VsCode-win32-1.47.1 基本 "contrastActiveBorder"/在活动元素周围额外的一层边框&#xff0c;用来提高对比度从而区别其他元素 "contrastBorder"在元素周围额外的一层边框&#xff0c;用来提高对比度从而区别其他元素 &qu…...

赣州市南康建设局网站/网站服务器多少钱一年

01 Elasticsearch x-pack 监控工具 x-pack 是 Elastic Stack 扩展的功能&#xff0c;提供安全性&#xff0c;警报&#xff0c;监视&#xff0c;报告&#xff0c;机器学习和许多其他功能。 ES7.0之后&#xff0c;默认情况下&#xff0c;当安装Elasticsearch时&#xff0c;会安装…...

wordpress文章 页面模板/东莞有限公司seo

今年6月是全国第19个“安全生产月”&#xff0c;连日来&#xff0c;全省消防救援部门紧紧围绕“消除事故隐患&#xff0c;筑牢安全防线”这一主题&#xff0c;开展了一系列助力平安活动&#xff0c;进一步普及消防安全知识&#xff0c;有效提高广大群众面对突发事故的自救和应急…...

个人短信接口wordpress/搜索引擎营销经典案例

因为近几年工信部对电话的打击和通讯规则的变化&#xff0c;传统的外呼模式已经不能满足企业电销业务的开展&#xff0c;光靠手机号卡没办法支撑得了频繁的经常外呼&#xff0c;而传统坐席因前期部署和后期维护的成本极高&#xff0c;也逐渐被市场淘汰。 基于目前的市场情况&am…...