CUDA: 简单入门

CUDA: 简单入门 1. 准备makefile 为了避免每次都要键入nvcc的命令,要准备一个makefile。makefile如下: CUFLAG = -g -Xcompiler -v \ -gencode=arch=compute_20,code=sm_20\ -gencode=arch=compute_20,code=compute_20\ -O2 IFLA 阅读详情

1. 准备makefile

为了避免每次都要键入nvcc的命令,要准备一个makefile。makefile如下:

CUFLAG = -g  -Xcompiler -v \
        -gencode=arch=compute_20,code=sm_20\
        -gencode=arch=compute_20,code=compute_20\
        -O2
IFLAGS = -I$(CUDA_DIR)/include -I$(CUDA_SDK_DIR)/C/common/inc -I../include
LFLAGS = -L$(CUDA_DIR)/lib64 -L$(CUDA_SDK_DIR)/C/lib
PRG = cuda_test
$(PRG) : main.cu
	nvcc main.cu -o $(PRG) $(CUFLAG) $(IFLAGS) $(LFLAGS)

2 异构计算(Heterogeneous Computing)

以下为几个技术名词的简单介绍:

  • 主机(host):CPU及其内存(host memory)。
  • 设备(device):GPU及其内存(device memory)。
  • 主机代码(host code):运行在CPU上的(一般来说「串行执行」的)代码。
  • 设备代码(device code):运行在GPU上的并行执行的代码。
  • 异构计算:由主机代码(host code)和设备代码(device code)协同执行完成的计算。
宏观上看,GPU执行代码的流程如下:

  1. 将输入数据通过PCI总线从CPU内存拷贝到GPU的DRAM中。
  2. 从内存中加载需要执行的代码到GPU后。
  3. 数据和指令都就绪后,就可以执行了。注意,在执行的过程中,GPU会在片上缓存数据以提升性能。
  4. 计算完毕后,将结果从GPU的DRAM中拷回CPU的Memory中。
例1: Hello World

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

__global__ void mykernel(void) {
}

int main(void) {
	mykernel<<<1,1>>>();
	printf("Hello World!\n");
	return 0;
}

上述代码编译后运行生成可执行文件cuda_test,运行cuda_test后将输出:

Hello World!

注意:

  1. 调用kernel时需要三个尖括号
  2. 包含必要的头文件
CUDA C/C++中引入的新关键字__global__所修饰的函数有以下两方面含义:
  • 此函数代码由设备执行
  • 此函数由主机代码调用
nvcc将源代码分为设备函数和主机函数两大类:
  • 设备函数由NVIDA编译器编译
  • 主机函数由主机上配置的编译器编译
三个尖括号标志着一个从主机代码调用设备代码的函数,称为“启动内核”(kernel launch)

例2: 整数相加

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

__global__ void integer_add(int * a, int * b, int * c) {
	*c = *a + *b;
}

int main(void) {
	int a,b,c;
	int * d_a, * d_b, * d_c;
	int size = sizeof(int);
	cudaMalloc((void**)&d_a,size);
	cudaMalloc((void**)&d_b,size);
	cudaMalloc((void**)&d_c,size);
	printf("Enter two integers with a space to separate them:\n");
	scanf("%d %d",&a,&b);
	cudaMemcpy(d_a,&a,size,cudaMemcpyHostToDevice);
	cudaMemcpy(d_b,&b,size,cudaMemcpyHostToDevice);
	integer_add<<<1,1>>>(d_a,d_b,d_c);
	cudaMemcpy(&c,d_c,size,cudaMemcpyDeviceToHost);
	cudaFree(d_a);
	cudaFree(d_b);
	cudaFree(d_c);
	printf("Sum is %d\n",c);
	return 0;
}

__global__修饰的integer_add函数说明:
  • integer_add函数将在设备上执行
  • integer_add函数将被主机调用
由于integer_add函数在设备上执行,所以指针a,b,c应该指向设备内存。这说明需要在设备内存中为变量开辟内存。

设备内存和主机内存在物理上是完全分开的不同电子部件:
  • 设备指针指向GPU内存的某个位置。设备指针可以从主机端传给设备端或者从设备端传给主机端,但是设备指针不能在主机端解引用。
  • 主机指针指向CPU内存的某个位置。主机指针可以从设备端传给主机端或者从主机端传给设备端,但是主机指针不能在设备端解引用。
CUDA API提供的用于处理设备内存的函数有cudaMalloc, cudaFree, cudaMemcpy。语义上分别对应于C语言的malloc, free, memcpy函数。这几个函数的具体使用方法如例2所示。

3 块(Blocks)

GPU是用来实现大规模并行的,如何实现呢?将上述例子扩展一下,如果我们要实现两个向量相加:
add<<<1,1>>>() ---> add<<<N,1>>>
N表示同时调用N次add函数,这样就可以实现并行的向量相加了。
每个被并行调用的add函数称之为一个块(block)。
  • 块的集合称之为网格(grid).
  • 每个块可以使用索引值blockIdx.x
通过使用blockIdx.x作为索引,每个块可以处理数组元素中的一部分。
有了这些基础后,就可以实现并行版本的向量相加了。
例3:向量相加
#include<stdio.h>
#include<stdlib.h>
#include<cuda.h>
#include<cutil.h>
#include<time.h>


#define N 512


__global__ void vec_block_add(int * a, int * b, int * c) {
	c[blockIdx.x] = a[blockIdx.x] + b[blockIdx.x];
}


void rand_ints(int * arr, int count) {
	srand(time(NULL));
	for(int i=0;i<count;i++) {
		arr[i] = rand() % 100;
	}
}


int main(void) {
	int * a,* b,* c;
	int * d_a, * d_b, * d_c;
	int size = N * sizeof(int);
	cudaMalloc((void**)&d_a,size);
	cudaMalloc((void**)&d_b,size);
	cudaMalloc((void**)&d_c,size);
	
	a = (int *) malloc(size);
	rand_ints(a,N);
	b = (int *) malloc(size);
	rand_ints(b,N);
	c = (int *) malloc(size);
	
	cudaMemcpy(d_a,a,size,cudaMemcpyHostToDevice);
	cudaMemcpy(d_b,b,size,cudaMemcpyHostToDevice);
	vec_block_add<<<N,1>>>(d_a,d_b,d_c);
	cudaMemcpy(c,d_c,size,cudaMemcpyDeviceToHost);
	
#if 1
	for(int i=0;i<N;i++) {
		printf("%-5d: a:%-5d b:%-5d c:%-5d\n",i,a[i],b[i],c[i]);
	}
#endif
	
	cudaFree(d_a);
	cudaFree(d_b);
	cudaFree(d_c);
	
	free(a);
	free(b);
	free(c);
	return 0;
}


例3中最关键的代码为如下几行:
__global__ void vec_block_add(int * a, int * b, int * c) {
	c[blockIdx.x] = a[blockIdx.x] + b[blockIdx.x];
}
由于函数是并行执行的,和传统的串行程序在integer_add函数中使用循环来完成加法相比,相当于由GPU这个加速器使用硬件的方式进行了循环展开,展开后便可以并行执行了。所以在编写这段代码时,需要使用blockIdx.x来定位当前执行的是循环的哪个部分。
从硬件的角度看,相当于同时有多个块在并行执行:
块0: c[0]=a[0]+b[0]
块1: c[1]=a[1]+b[1]
块2: c[2]=a[2]+b[2]
块3: c[3]=a[3]+b[3]
....

4 线程(Threads)

简单的说就是一个块(Block)可以分割成多个线程(Threads).
所以可以将上述的多块单线程版本改成单块多线程版本。
例4: 向量相加
#include<stdio.h>
#include<stdlib.h>
#include<cuda.h>
#include<cutil.h>
#include<time.h>

#define N 512

__global__ void vec_thread_add(int * a, int * b, int * c) {
	c[threadIdx.x] = a[threadIdx.x] + b[threadIdx.x];
}

void rand_ints(int * arr, int count) {
	srand(time(NULL));
	for(int i=0;i<count;i++) {
		arr[i] = rand() % 100;
	}
}

int main(void) {
	int * a,* b,* c;
	int * d_a, * d_b, * d_c;
	int size = N * sizeof(int);
	cudaMalloc((void**)&d_a,size);
	cudaMalloc((void**)&d_b,size);
	cudaMalloc((void**)&d_c,size);
	
	a = (int *) malloc(size);
	rand_ints(a,N);
	b = (int *) malloc(size);
	rand_ints(b,N);
	c = (int *) malloc(size);
	
	cudaMemcpy(d_a,a,size,cudaMemcpyHostToDevice);
	cudaMemcpy(d_b,b,size,cudaMemcpyHostToDevice);
	vec_thread_add<<<1,N>>>(d_a,d_b,d_c);
	cudaMemcpy(c,d_c,size,cudaMemcpyDeviceToHost);
	
#if 1
	for(int i=0;i<N;i++) {
		printf("%-5d: a:%-5d b:%-5d c:%-5d\n",i,a[i],b[i],c[i]);
	}
#endif
	
	cudaFree(d_a);
	cudaFree(d_b);
	cudaFree(d_c);
	
	free(a);
	free(b);
	free(c);
	return 0;
}
上述代码vec_thread_add<<<1,N>>>指定向量相加操作仅有1个块完成,但是这个块可以分割成512个线程来完成这个计算。和块类似的,由于函数vec_thread_add是被多个线程并行展开循环的,所以需要根据线程编号来确定当前循环应该完成的计算部分。每个线程使用threadIdx.x来标识当前线程。

5 索引(Indexing)

有了以上基础,接下来我们就可以实现多块多线程的大规模并行程序了。实现多块多线程并行程序的核心是利用进行准确的索引计算,即准确的利用索引将某个线程和该线程要完成计算的数据对应起来。
对于多块多线程的情况,我们假设要处理的向量包含128个元素,可以设计16个块,每个块中再包含8个线程来完成计算(因为16*8=128)。对于任何一个元素,我们都应该能准确的计算出来由哪个线程来完成这个相加计算。比如对于编号为71的元素:
块号为:71/8=8
线程号为:71%8=7
所以71号元素应该由:
71 = threadIdx.x + blockIdx.x * 8 = 7 + 8 * 8
在CUDA中可以使用blockDim.x来表示每个块中的线程数量,所以以上的计算可以修改为
元素编号 = threadIdx.x + blockIdx.x * blockDim.x
接下来我们就可以编写多块多线程的向量相加程序了.
例5: 向量相加
#include<stdio.h>
#include<stdlib.h>
#include<cuda.h>
#include<cutil.h>
#include<time.h>

#define N (16*16)
#define THREAD_PER_BLOCK 32

__global__ void vec_block_thread_add(int * a, int * b, int * c) {
	int index = threadIdx.x + blockIdx.x * blockDim.x;
	c[index] = a[index] + b[index];
}

void rand_ints(int * arr, int count) {
	srand(time(NULL));
	for(int i=0;i<count;i++) {
		arr[i] = rand() % 100;
	}
}

int main(void) {
	int * a,* b,* c;
	int * d_a, * d_b, * d_c;
	int size = N * sizeof(int);
	cudaMalloc((void**)&d_a,size);
	cudaMalloc((void**)&d_b,size);
	cudaMalloc((void**)&d_c,size);
	
	a = (int *) malloc(size);
	rand_ints(a,N);
	b = (int *) malloc(size);
	rand_ints(b,N);
	c = (int *) malloc(size);
	
	cudaMemcpy(d_a,a,size,cudaMemcpyHostToDevice);
	cudaMemcpy(d_b,b,size,cudaMemcpyHostToDevice);
	vec_block_thread_add<<<N/THREAD_PER_BLOCK,THREAD_PER_BLOCK>>>(d_a,d_b,d_c);
	cudaMemcpy(c,d_c,size,cudaMemcpyDeviceToHost);
	
#if 1
	for(int i=0;i<N;i++) {
		printf("%-5d: a:%-5d b:%-5d c:%-5d\n",i,a[i],b[i],c[i]);
	}
#endif
	
	cudaFree(d_a);
	cudaFree(d_b);
	cudaFree(d_c);
	
	free(a);
	free(b);
	free(c);
	return 0;
}
以上代码的缺陷是需要元素的数量能够整除每块中线程数量,稍作修改便可以实现任意元素数量的向量相加了。
#include<stdio.h>
#include<stdlib.h>
#include<cuda.h>
#include<cutil.h>
#include<time.h>

#define N 100
#define M 32

__global__ void vec_block_thread_add(int * a, int * b, int * c, int n ) 
{
	int index = threadIdx.x + blockIdx.x * blockDim.x;
	if(index < n) {
		c[index] = a[index] + b[index];
	}
}

void rand_ints(int * arr, int count) {
	srand(time(NULL));
	for(int i=0;i<count;i++) {
		arr[i] = rand() % 100;
	}
}

int main(void) {
	int * a,* b,* c;
	int * d_a, * d_b, * d_c;
	int size = N * sizeof(int);
	cudaMalloc((void**)&d_a,size);
	cudaMalloc((void**)&d_b,size);
	cudaMalloc((void**)&d_c,size);
	
	a = (int *) malloc(size);
	rand_ints(a,N);
	b = (int *) malloc(size);
	rand_ints(b,N);
	c = (int *) malloc(size);
	
	cudaMemcpy(d_a,a,size,cudaMemcpyHostToDevice);
	cudaMemcpy(d_b,b,size,cudaMemcpyHostToDevice);
	vec_block_thread_add<<<(N+M-1)/M,M>>>(d_a,d_b,d_c,N);
	cudaMemcpy(c,d_c,size,cudaMemcpyDeviceToHost);
	
#if 1
	for(int i=0;i<N;i++) {
		printf("%-5d: a:%-5d b:%-5d c:%-5d\n",i,a[i],b[i],c[i]);
	}
#endif
	
	cudaFree(d_a);
	cudaFree(d_b);
	cudaFree(d_c);
	
	free(a);
	free(b);
	free(c);
	return 0;
}
可能有一个很显然的问题就是既然使用块能够达到并行的函数调用为什么还需要用线程机制呢?
引入线程机制的必要性有:
  • 线程间可以实现高效的通信
  • 线程间可以实现高效的同步

6 共享内存(Shared Memory)

现在我们考虑实现另外一个例子,称之为“模板转换”。
具体的问题描述如下:提供两个数组,输入数组in和输出数组out. 一共有n个元素,将in中的值映射到out,映射的规则是:
对于任意的i,out[i] = out[ i - radius ] + out[ i - radius + 1] + out[i-radius+2] + ... + out[i+radius]. 
这里我们会发现同一块内的线程比如,2号线程和3号线程访问的数据将会有大量的数据重复。如果能有cache机制,将有效的降低从GPU的DRAM中加载数据所消耗的时间。
共享内存(shared memory)就是用来实现这个功能的:
  • 在同一个块内线程通过共享内存实现数据共享或者说在同一个块内,线程通过共享内存实现线程间的通信
  • 硬件上看,共享内存是速度极高的片上内存。而设备内存,又称之为全局内存(global memory)的速度较慢
  • 可以将共享内存看做是用户管理的cache
  • 使用关键字__shared__定义的存储空间将在共享内存上为每个块开辟空间
  • 一个块内的共享内存数据对另外一个块的线程来说是不可见的
现在我们考虑使用Shared Memory来实现这个例子,核心就是我们在一个块内合并线程对共享数据的访问。
对每一个块来说:
  • 需要读入2 * radius + blockDim.x 个元素的数据。
  • 输出blockDim.x个元素数据到全局内存中.
#include<stdio.h>
#include<stdlib.h>
#include<cuda.h>
#include<cutil.h>
#include<time.h>

#define N 256
#define RADIUS 2
#define BLOCK_SIZE 32

__global__ void stencil_1d(int * in, int *out) 
{
	__shared__ int temp[BLOCK_SIZE + 2 * RADIUS];
	int g_index = threadIdx.x + blockIdx.x * blockDim.x + RADIUS;
	int s_index = threadIdx.x + RADIUS;
	temp[s_index] = in[g_index];
	if(threadIdx.x < RADIUS) {
		temp[s_index - RADIUS] = in[g_index - RADIUS];
		temp[s_index + BLOCK_SIZE] = in[g_index + BLOCK_SIZE];
	}
	int result = 0;
	for(int offset = -RADIUS; offset <= RADIUS; offset++) {
		result = result + temp[s_index+offset]; 
	}
	out[g_index-RADIUS] = result;
}

void rand_ints(int * arr, int count) {
	srand(time(NULL));
	for(int i=0;i<count;i++) {
		arr[i] = rand() % 100;
	}
}

int main(void) {
	int * in, * out;
	
	int size_in = sizeof(int) * ( N + 2 * RADIUS );
	in = (int *) malloc(size_in);
	rand_ints(in+RADIUS,N);
	
	int size_out = sizeof(int) * N;
	out = (int *) malloc(size_out);
	
	int * d_in, * d_out;
	cudaMalloc((void**)&d_in,size_in);
	cudaMalloc((void**)&d_out,size_out);
	cudaMemcpy(d_in,in,size_in,cudaMemcpyHostToDevice);
	stencil_1d<<<(N+BLOCK_SIZE-1)/BLOCK_SIZE,BLOCK_SIZE>>>(d_in,d_out);
	cudaMemcpy(out,d_out,size_out,cudaMemcpyDeviceToHost);
	
#if 1
	for(int i=0;i<N+2*RADIUS;i++) {
		printf("%-5d	",in[i]);
	}
	printf("\n");
	for(int i=0;i<N;i++) {
		printf("%-5d	",out[i]);
	}
	printf("\n");
#endif
	
	cudaFree(d_in);
	cudaFree(d_out);
	
	free(in);
	free(out);
	
	return 0;
}

7 线程同步函数(_syncthreads())

上述代码因为没有考虑数据竞争可能产生错误的结果。比如第2个块中的第三个线程进入for循环开始进行相加计算了,但是它需要读的数据还没有被4号线程写入到存储空间就会产生数据竞争问题。
CUDA提供了__syncthreads函数来进行数据同步,这样就可以保证所有的数据都就绪后再开始进行计算。
使用__syncthreads修改后的代码如下:
#include<stdio.h>
#include<stdlib.h>
#include<cuda.h>
#include<cutil.h>
#include<time.h>

#define N 256

#define RADIUS 2
#define BLOCK_SIZE 32

__global__ void stencil_1d(int * in, int *out) 
{
	__shared__ int temp[BLOCK_SIZE + 2 * RADIUS];
	int g_index = threadIdx.x + blockIdx.x * blockDim.x + RADIUS;
	int s_index = threadIdx.x + RADIUS;
	temp[s_index] = in[g_index];
	if(threadIdx.x < RADIUS) {
		temp[s_index - RADIUS] = in[g_index - RADIUS];
		temp[s_index + BLOCK_SIZE] = in[g_index + BLOCK_SIZE];
	}
	__syncthreads();
	int result = 0;
	for(int offset = -RADIUS; offset <= RADIUS; offset++) {
		result = result + temp[s_index+offset]; 
	}
	out[g_index-RADIUS] = result;
}

void rand_ints(int * arr, int count) {
	srand(time(NULL));
	for(int i=0;i<count;i++) {
		arr[i] = rand() % 100;
	}
}

int main(void) {
	int * in, * out;
	
	int size_in = sizeof(int) * ( N + 2 * RADIUS );
	in = (int *) malloc(size_in);
	rand_ints(in+RADIUS,N);
	
	int size_out = sizeof(int) * N;
	out = (int *) malloc(size_out);
	
	int * d_in, * d_out;
	cudaMalloc((void**)&d_in,size_in);
	cudaMalloc((void**)&d_out,size_out);
	cudaMemcpy(d_in,in,size_in,cudaMemcpyHostToDevice);
	stencil_1d<<<(N+BLOCK_SIZE-1)/BLOCK_SIZE,BLOCK_SIZE>>>(d_in,d_out);
	cudaMemcpy(out,d_out,size_out,cudaMemcpyDeviceToHost);
	
#if 1
	for(int i=0;i<N+2*RADIUS;i++) {
		printf("%-5d	",in[i]);
	}
	printf("\n");
	for(int i=0;i<N;i++) {
		printf("%-5d	",out[i]);
	}
	printf("\n");
#endif
	
	cudaFree(d_in);
	cudaFree(d_out);
	
	free(in);
	free(out);
	
	return 0;
}

8 异步操作(asynchronous operations)

CUDA kernel函数可以异步启动,普通的kernel函数需要等到执行完毕后才能将控制流返回到CPU端。异步启动方式指的是,kernel启动之后控制流立即返回到CPU端。
CUDA 的cudaMemcpy函数是等待数据拷贝完毕后才返回到CPU端。CUDA提供cudaMemcpyAsync来支持异步的数据拷贝。
CUDA提供的接口函数cudaDeviceSynchronize函数将阻塞CPU直到所有的CUDA函数调用都执行完毕。

9 故障处理(handle errors)

所有的CUDA函数都会返回一个错误码,其类型为cudaError_t。错误可能是当前函数调用产生的也可能是之前发起的异步函数执行后产生的。
得到最后一个错误的函数是:cudaError_t cudaGetLastError(void);
将错误码转换为字符串描述信息的函数是:char * cudaGetErrorString(cudaError_t);

10 设备管理(managing devices)

应用程序可以查询GPU的数量或者是选择GPU.
  • 查询设备数量的接口: cudaGetDeviceCount(int * count);
  • 选择设备的接口: cudaSetDevice(int device);
  • 获取设备的接口:cudaGetDevice(int * device);
  • 获取设备属性的接口: cudaGetDeviceProperties(cudaDeviceProp * prop, int device);
多个主机线程可以共享一个设备。
单个主机线程可以管理多个GPU设备。

11. 引用

1. CUDA C/C++ Basics Supercomputing 2011 Tutorial, http://www.nvidia.com/docs/IO/116711/sc11-cuda-c-basics.pdf

Makefile(1)编写简单的makefile批量编译CUDA程序 本文讲述如何编写简单的makefile批量编译CUDA程序,以及makefile的简单用法。 阅读详情

相关推荐

更简单的CUDA入门教程(个人翻译 未完成)

原文地址: https://devblogs.nvidia.com/even-easier-introduction-cuda/ An Even Easier Introduction to CUDA 一个更简单的CUDA入门教程 This post is a super simple introduction to CUDA, the popular parallel computing...

qq_29094161的博客 607

cuda.h, cuda_runtime.h, cuda_runtime_api.h区别

广义的理解: cuda.h: 为CUDA驱动程序API定义公共宿主函数和类型。链接动态库libcuda.so cuda_runtime.h: 为CUDA运行时API定义公共宿主函数和类型(包括一些内嵌类型). 。对于大多数场景下,使用cuda_runtime.h api 开发比cuda.haipi 更简单也更效率。链接动态库libcudart.so cuda_runtime_api.h:是cuda_runtime.h的子集。cuda_runtime_api.h是纯C接口和实现,而cuda_runtime.

Lekaor的博客 4478

CUDA 编程入门

CUDA编程入门 更好的阅读体验 CUDA 概述 CUDA 是 NVIDIA 推出的用于其发布的 GPU 的并行计算架构,使用 CUDA 可以利用 GPU 的并行计算引擎更加高效的完成复杂的计算难题。 在目前主流的使用冯·诺依曼体系结构的计算机中,GPU 属于一个外置的设备,因此即便在利用 GPU 进行并行计算的时候也无法脱离 CPU,需要与 CPU 协同工作。因此当我们在说 GPU 并行计算时,其实指的是基于 CPU+GPU 的异构计算架构。在异构计算架构中,CPU 和 GPU 通过 PCI-E 总线连接

A-Egoist的博客 5976

cuda

exp1 #include <cuda.h> #include <iostream> struct HostObject { __host__ int value() const { return 42; } }; struct DeviceObject { __device__ int value() const { return 3; } }; template <typename T> __host__ __device__ i

zz_hh_uu_的博客 556

CUDA入门

CUDA简介 CUDA是NVIDIA发布的GPU上的并行计算平台和模型, 2006年第一代CUDA发布,截至2018年最新的是9代CUDA. hello cuda! __global__ void helloWorld() //__globa__是关键字 { printf("Hello CUDA!"); } int ma...

大泽之国 2万+

CUDA:简单打印的实例 C/C++

在本示例中,我们将展示如何使用 CUDA 在 GPU 上执行简单的打印操作。要编译和运行这个示例,您需要安装适当的 CUDA 工具包,并使用支持 CUDA 的编译器(如 NVIDIA 的 nvcc)进行编译。,用于指定在 GPU 上执行的线程块数量和每个线程块中的线程数量。在本例中,我们只使用一个线程块和一个线程,因此每个线程将在 GPU 上执行一次打印函数。这个简单的示例展示了如何在 CUDA 中执行一个简单的打印操作。CUDA 提供了更复杂和强大的功能,使我们能够在 GPU 上进行高性能的并行计算。

PixelProX的博客 666

linux cuda 目录,致命错误:cuda.h:没有这样的文件或目录

我在我的PC上成功安装了CUDA 8.0,我可以通过在Ubuntu 16.10中运行以下命令来查看它的文件:$sudo find / -name nvcc/usr/local/cuda-8.0/bin/nvcc$sudo find / -name cuda/usr/local/cuda/usr/local/cuda-8.0/targets/x86_64-linux/include/thrust/s...

weixin_42303803的博客 2620

手把手教你用VM实现欧姆龙PLC数据采集(含Fins协议配置详解)

本文详细介绍了如何使用VM(VisionMaster)实现与欧姆龙PLC的数据采集,重点解析了Fins协议(包括Fins TCP/UDP)的配置步骤。从网络环境搭建、PLC的IP地址设置,到VM端Fins通讯通道的详细配置与数据点映射,提供了完整的实战指南。文章还涵盖了数据收发解析、常见问题排查清单,帮助工程师快速完成视觉系统与PLC的稳定集成。

weixin_29171087的博客 189

【CUDA编程】一:从简单的实例入门

前提 拥有NVIDIA的显卡- 配置好cuda 有C/C++基础 代码1 //helloworld.cu #include <iostream> #include <stdio.h> /* * __global__ 用于修饰在gpu上运行且由cpu调用的函数 */ __global__ void k_add (int a, int b, int* c ){ *c = a+b; } /* * __host__用于修饰在cpu上运行的函数,一般缺省 */ int main(v

qq_43257640的博客 7136

ST MotorControl Workbench 6.2.1 自定义硬件配置避坑指南

本文详细介绍了ST MotorControl Workbench 6.2.1在自定义硬件配置中的常见问题与解决方案,涵盖功率板配置、采样电路校准、电机参数设置及代码生成避坑指南,帮助开发者高效完成电机控制项目开发。

red88的博客 1023

游程理论提取灾害事件特征V2---基于MATLAB语言的编程实现

游程理论,灾害事件,MATLAB

EWBA_GIS_RS_ER的博客 3793

CUDA 入门教程:更简单的介绍 (更新版)

本文介绍了CUDA并行计算平台的基本概念和使用方法。作者展示了如何将简单的C++数组相加程序转换为CUDA并行计算程序,通过四个步骤实现:1) 添加__global__修饰符创建CUDA内核;2) 使用统一内存管理;3) 增加并行线程块;4) 优化内存预取。最终版本在NVIDIA T4 GPU上实现了265GB/s的带宽,性能提升1932倍。文章还建议读者尝试printf调试、多维线程索引等练习,并提供了CUDA学习资源。这篇入门教程适合C++程序员快速了解CUDA编程基础。

buknow的博客 231

DolphinScheduler租户配置踩坑实录:手把手教你修复‘tenant not exists‘报错

本文深度解析DolphinScheduler的租户模型,并提供一套从界面到数据库的系统性排查流程,手把手教你修复常见的'tenant not exists'报错。文章揭示了该错误的本质是用户与租户数据关联不一致,并给出了通过UI或SQL更新的具体修复方案,同时分享了根因分析与预防措施,帮助用户从根本上理解和避免此类问题。

2w3e4r5t6y的博客 541

cuda 函数传地址_CUDA程序设计入门(三)——实现简单的应用:向量加

上期内容:核函数写在前面因为疫情原因拖更了很久,本想回到学校后继续用学校的服务器做实验,结果碰上服务器维护,所以本期实验使用的GPU为PC端显卡。本期文章将讨论如何实现CUDA版的向量加程序——一种在数值应用中常见的计算场景,并与CPU版程序进行比较。本来还想在本期文章中加入矩阵乘的实现,但后来感觉矩阵乘和向量加有很多重复的内容,那就不增加文章的篇幅了。实验用完整代码可见我的GitHub:http...

weixin_36283702的博客 264

Python制作【大麦网】自动抢票程序

Python 的迅速崛起对整个行业来说都是极其有利的,但“人红是非多”,导致它平添了许许多多的批评,不过依旧挡不住它火爆的发展势头。如果你对Python感兴趣,想要学习pyhton,这里给大家分享一份Python全套学习资料,都是我自己学习时整理的,希望可以帮到你,一起加油!😝有需要的小伙伴,可以点击下方链接免费领取或者V扫描下方二维码免费领取🆓点击这里。

mate1357的博客 2万+

STM32G474的HRTIM实战:用CubeMX配置互补PWM驱动全桥电路(附移相算法代码)

本文详细介绍了如何使用STM32G474的HRTIM高分辨率定时器,通过CubeMX配置互补PWM驱动全桥电路,并附移相算法代码。内容涵盖HRTIM硬件架构解析、CubeMX配置全流程、移相控制算法实现及调试技巧,为数字电源和电机控制领域提供实用解决方案。

weixin_27017211的博客 482

CUDA Hello world

CUDA Hello World #include <iomanip> #include <iostream> #include <stdio.h> #include <cuda_runtime.h> #include <device_launch_parameters.h> using namespace std; //检测GPU bool CheckCUDA(void){ int count = 0; int i =

言午日月 199

强化学习优化多目标冲突推理的决策过程设计

1. 背景介绍 强化学习(Reinforcement Learning,RL)作为机器学习的一个重要分支,近年来在计算机科学和人工智能领域取得了显著的进展。其核心思想是通过试错来学习最优策略,从而在复杂的环境中实现目标。然而,在多目标冲突推理(Multi-Objective Conflict Reasoning,MOCR)问题上,强化学习面临着诸多挑

AI天才研究院 1512

最简单的 CUDA 程序,hello world from CUDA

【代码】最简单的 CUDA 程序,hello world from CUDA。

shimly123456的博客 270

预测股票价格趋势:使用MATLAB进行股票价格预测

通过收集股票数据、数据预处理、构建预测模型以及可视化结果,我们可以利用MATLAB强大的计算和分析功能来预测股票价格的走势。然而,需要注意的是,股票市场具有高度复杂性和不确定性,预测结果仅供参考,并不构成投资建议。然而,借助计算机编程和数据分析工具,我们可以利用历史股价数据来建立模型,并预测未来的股票价格走势。首先,我们需要获取股票的历史价格数据。完成模型的拟合后,我们可以使用该模型来进行未来股票价格的预测。最后,我们可以使用MATLAB的绘图功能将预测的股票价格与实际股票价格进行对比,并进行可视化展示。

2301_79326588的博客 1018

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

推荐几个不错的CUDA入门教程

推荐几个不错的CUDA入门教程(非广告)-阿里云开发者社区推荐几个不错的CUDA入门教程(非广告)推荐几个不错的CUDA入门教程(非广告)-阿里云开发者社区推荐几个不错的CUDA入门教程(非广告)

永远飞翔的鸟 8320

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

cuda编程简单入门

这是一份简单的CUDA编程入门,主要参考英伟达的官方文档进行学习,本人也是刚开始学习,如有表述错误,还请指出。官方文档链接如下:https://developer.nvidia.com/blog/even-easier-introduction-cuda/

u012374012的专栏 1333

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

【CUDA编程】CUDA编程入门第一课

本文总结了CUDA编程的学习资源与实践案例。推荐NVIDIA官方文档、中文社区教程和GitHub项目作为学习材料,重点介绍了从CPU到GPU的并行计算优化过程。通过一个百万级数据加法运算的实例,展示了不同实现方式的性能对比:CPU版本耗时1秒,而经过优化的GPU版本(多SM多线程+数据预取)仅需50微秒,加速效果显著。文章还提供了编译命令、性能分析工具的使用方法,以及完整的代码示例,帮助开发者理解CUDA编程的核心概念和优化技巧。

聿默的博客 2632

CUDA入门教程

推荐几个不错的CUDA入门教程(非广告)godweiyang​​字节跳动 AI Lab NLP算法工程师​关注他1,089 人赞同了该文章❝ 最近因为项目需要,入坑了CUDA,又要开始写很久没碰的C++了。对于CUDA编程以及它所需要的GPU、计算机组成、操作系统等基础知识,我基本上都忘光了,因此也翻了不少教程。这里简单整理一下,给同样有入门需求的同学们参考一下。 ❞官方文档及书籍英文好、时间充裕的同学可以精读官方文档或者著作。NVIDIA CUDA C++ Programming Guide「地址:」 h

luoganttcc的博客 6357

CUDA入门编程

记录个人学习cuda过程。

2303_77085211的博客 1445
上一篇: 云架构师=企业级架构师+SOA架构师+云技术专家
下一篇: UNIX的ar命令
下木
博客等级 码龄16年 88粉丝 132原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值