CUDA by example Chapter11 多GPU系统上的CUDA C

CUDA编程第四章: 全局内存 前言: 本章内容: 学习CUDA内存模型 CUDA内存管理 全局内存编程 探索全局内存访问模式 研究全局内存数据布局 统一内存编程 最大限度地提高全局内存吞吐量 在上一章中,你已经了解了线程是如何在GPU中执行的,以及如何通过操作线程束来优化核函数性能。但是,核函数性能并不是只和线程束的执行有关。回忆一下第3章的内容,在3.3.2节中,把一个线程块最里面一层的维度设为线程束大小的一半,这导致内存负载效率的大幅下降。这种性能损失不能用线程束调度或并行性来解释,造成这种性能损失的 阅读详情

零拷贝主机内存

零拷贝主机内存是另一种固定内存,它也不能被CPU从物理内存交换出去或者重新定位。同时,这种内存除了可以用于主机与GPU之间的内存复制外,还可以在CUDA C核函数中直接访问这种类型的主机内存。由于这种内存不需要复制到GPU,因此也称为零拷贝内存。

通过零拷贝内存实现点积运算

1. 测试方法
我们用test性能测试的方式,编写两个函数,其中一个函数是对标准主机内存的测试,另一个函数将在GPU上执行规约运算,并使用零拷贝内存作为输入缓冲区和输出缓冲区,并比较它们的性能。
2. 分配零拷贝内存的方法
使用cudaHostAlloc()函数分配零拷贝内存,其中flags参数指定含义如下:
cudaHostAllocMapped这个标志告诉运行时将从GPU访问这块内存,即分配零拷贝内存。
cudaHostAllocWriteCombined这个标志表示,运行时应该将内存分配为"合并式写入"内存。这个标志并不会改变应用程序的功能,但却可以显著地提升GPU读取内存时的性能。然而,当CPU也要读取这块内存时,“合并式写入”会显得很低效,因此在决定是否使用这个标志之前,必须首先考虑应用程序的可能访问模式。

根据以上的说明,由于CPU不需要读取两个输入缓冲区的内容,所以它们应该使用cudaHostAllocWriteCombined标志来提升GPU读取内存时的性能;而CPU需要读取输出缓冲区的内容,因此它不应该使用cudaHostAllocWriteCombined标志,如下所示:

	//在CPU上分配内存
	cudaHostAlloc((void **)&a, size*sizeof(*a), cudaHostAllocWriteCombined|cudaHostAllocMapped);
	cudaHostAlloc((void **)&b, size*sizeof(*b), cudaHostAllocWriteCombined | cudaHostAllocMapped);
	cudaHostAlloc((void **)&partial_c, blocksPerGrid*sizeof(*partial_c), cudaHostAllocMapped);

3. 获得零拷贝内存在GPU上的有效地址
GPU的虚拟内存空间与CPU是独立的,因此在GPU上访问零拷贝内存与在CPU上访问它具有不同的地址。通过调用cudaHostAlloc()将返回这块内存在CPU上的指针,因此需要调用cudaHostGetDevicePointer()来获得这块内存在GPU上的有效指针。这些指针将被传递给核函数,并在随后由GPU对这块内存执行读取和写入操作。

	//获得主机上的零拷贝内存在GPU上的地址映射
	cudaHostGetDevicePointer(&dev_a, a, 0);
	cudaHostGetDevicePointer(&dev_b, b, 0);
	cudaHostGetDevicePointer(&dev_partial_c, partial_c, 0);
  1. 需要对CPU和GPU进行同步
    因为CPU和GPU都可以访问零拷贝内存,如果在核函数中会修改零拷贝内存的内容,那么在核函数执行期间CPU不应该访问或者修改零拷贝内存的内容,因此需要对CPU和GPU进行同步。同步完成后,此时可以确信GPU已经完成了相应的工作,CPU可以安全地对零拷贝内存进行操作。
//如果核函数中会修改零拷贝内存的内容,那么在核函数的执行期间
//零拷贝内存的内容是未定义的,因此需要对CPU和GPU进行同步
cudaThreadSynchronize();

5. 将运行时置入能分配零拷贝内存的状态
必须要先将运行时置入能分配零拷贝内存的状态之后才能将主机内存映射到设备上。
通过调用cudaSetDeviceFlags()实现这个操作,并且传递标志值cudaDeviceMapHost来表示我们希望设备映射主机内存。

cudaSetDeviceFlags(cudaDeviceMapHost);

实际上,在运行完整代码时,一开始我忘记添加上述语句,发现实现的功能和性能与添加上述语句时相似的。个人猜想这可能是NVIDIA新CUDA版本做的优化。cuda by example这本书放在现在(2019年)来看确实有些古老了,但是其中的基本思想还是有借鉴之处的。
6.完整代码

// 代码11.2零拷贝主机内存
//时间:2019.08.03
#include "cuda_runtime.h"
#include "device_launch_parameters.h"
#include <iostream>

#define imin(a,b) (a<b?a:b)


const int N = 33 * 1024 * 1024;
const int threadsPerBlock = 256;
const int blocksPerGrid = imin(32,(N+threadsPerBlock-1)/threadsPerBlock);




__global__ void dot(int size, float *a, float *b, float *partial_c)
{
	__shared__ float cache[threadsPerBlock];
	int tid = threadIdx.x + blockIdx.x*blockDim.x;
	int cacheIndex = threadIdx.x;

	float temp = 0;
	while (tid < size)
	{
		temp += a[tid] * b[tid];
		tid += gridDim.x*blockDim.x;
	}

	//设置cache中的值
	cache[cacheIndex] = temp;

	//同步这个线程块中的线程
	__syncthreads();

	//规约运算
	int i = blockDim.x / 2;
	while (i != 0)
	{
		if (cacheIndex < i)
		{
			cache[cacheIndex] += cache[cacheIndex + i];
		}
		__syncthreads();
		i /= 2;
	}

	if (cacheIndex == 0)
	{
		partial_c[blockIdx.x] = cache[0];
	}

}


float cuda_malloc_test(int size)
{
	cudaEvent_t start, stop;
	float *a, *b, c, *partial_c;
	float *dev_a, *dev_b, *dev_partial_c;
	float elapsedTime;

	cudaEventCreate(&start);
	cudaEventCreate(&stop);

	//在CPU上分配主机内存
	a = (float *)malloc(size*sizeof(*a));
	b = (float *)malloc(size*sizeof(*b));
	partial_c = (float *)malloc(blocksPerGrid*sizeof(*partial_c));


	//在GPU上分配内存
	cudaMalloc((void **)&dev_a, size*sizeof(*dev_a));
	cudaMalloc((void **)&dev_b, size*sizeof(*dev_b));
	cudaMalloc((void **)&dev_partial_c, blocksPerGrid*sizeof(*dev_partial_c));

	//用数据填充主机内存
	for (int i = 0; i < size; i++)
	{
		a[i] = i;
		b[i] = i * 2;
	}

	//开始记录事件
	cudaEventRecord(start, 0);

	//将数组a和b复制到GPU
	cudaMemcpy(dev_a, a, size*sizeof(*a), cudaMemcpyHostToDevice);
	cudaMemcpy(dev_b, b, size*sizeof(*b), cudaMemcpyHostToDevice);

	//执行核函数
	dot << <blocksPerGrid, threadsPerBlock >> >(size, dev_a, dev_b, dev_partial_c);

	//将GPU运算得到的结果复制到CPU上
	cudaMemcpy(partial_c, dev_partial_c, blocksPerGrid*sizeof(*dev_partial_c), cudaMemcpyDeviceToHost);

	cudaEventRecord(stop, 0);
	cudaEventSynchronize(stop);
	cudaEventElapsedTime(&elapsedTime, start, stop);

	//在CPU上把partial_c上的内容加和,结果放到c上
	c = 0;
	for (int i = 0; i < blocksPerGrid; i++)
	{
		c += partial_c[i];
	}


	//释放GPU上的内存
	cudaFree(dev_a);
	cudaFree(dev_b);
	cudaFree(dev_partial_c);

	//释放CPU上的内存
	free(a);
	free(b);
	free(partial_c);

	//释放事件
	cudaEventDestroy(start);
	cudaEventDestroy(stop);

	printf("Valude calculated: %f\n", c);
	return elapsedTime;

}



float cuda_host_alloc_test(int size)
{
	cudaEvent_t start, stop;
	float *a, *b, c, *partial_c;
	float *dev_a, *dev_b, *dev_partial_c;
	float elapsedTime;

	cudaEventCreate(&start);
	cudaEventCreate(&stop);

	//在CPU上分配内存
	cudaHostAlloc((void **)&a, size*sizeof(*a), cudaHostAllocWriteCombined|cudaHostAllocMapped);
	cudaHostAlloc((void **)&b, size*sizeof(*b), cudaHostAllocWriteCombined | cudaHostAllocMapped);
	cudaHostAlloc((void **)&partial_c, blocksPerGrid*sizeof(*partial_c), cudaHostAllocMapped);

	//用数据填充主机内存
	for (int i = 0; i < size; i++)
	{
		a[i] = i;
		b[i] = i * 2;
	}


	//获得主机上的零拷贝内存在GPU上的地址映射
	cudaHostGetDevicePointer(&dev_a, a, 0);
	cudaHostGetDevicePointer(&dev_b, b, 0);
	cudaHostGetDevicePointer(&dev_partial_c, partial_c, 0);


	//开始记录事件
	cudaEventRecord(start, 0);
	dot << <blocksPerGrid, threadsPerBlock >> >(size, dev_a, dev_b, dev_partial_c);
	cudaThreadSynchronize();//如果核函数中会修改零拷贝内存的内容,那么在核函数的执行期间,零拷贝内存的内容是未定义的
	//由于使用的是零拷贝内存,所以不需要将GPU内存拷贝到主机上


	//结束事件记录
	cudaEventRecord(stop, 0);
	cudaEventSynchronize(stop);
	cudaEventElapsedTime(&elapsedTime, start, stop);

	//在CPU下对计算结果进行加和
	c = 0;
	for (int i = 0; i < blocksPerGrid; i++)
	{
		c += partial_c[i];
	}


	//释放一些内存
	cudaFreeHost(dev_a);
	cudaFreeHost(dev_b);
	cudaFreeHost(dev_partial_c);

	//释放事件
	cudaEventDestroy(start);
	cudaEventDestroy(stop);

	printf("Value calculated:%f\n", c);
	return elapsedTime;
}


int main()
{
	cudaDeviceProp prop;
	int whichDevice;
	cudaGetDevice(&whichDevice);
	cudaGetDeviceProperties(&prop, whichDevice);
	if (prop.canMapHostMemory != 1)
	{
		printf("Device cannot map memory.\n");
		return 0;
	}

	cudaSetDeviceFlags(cudaDeviceMapHost);
	
	float elapsedTime = cuda_malloc_test(N);
	printf("Time using cudamalloc:%3.1f ms\n", elapsedTime);

	elapsedTime = cuda_host_alloc_test(N);
	printf("Time using cudaHostAlloc:%3.1f ms\n", elapsedTime);


	

	system("pause");
	return 0;

}

在这里插入图片描述

零拷贝内存的性能

1. 集成GPU和独立GPU的查看
使用cudaGetDeviceProperties()返回的结构来判断GPU的这个属性。该结构中有一个域integrated,如果设备是集成GPU,那么该域的值是true,否则,该域的值是false。
2. 集成GPU使用零拷贝内存带来的性能提升
集成GPU是系统芯片组中内置的图形处理器,通常与CPU共享系统内存。因此集成GPU使用零拷贝内存通常都会带来性能提升。
3. 独立GPU使用零拷贝内存带来的性能提升
当输入内存和输出内存都只能使用一次时,那么在独立GPU上使用零拷贝内存将带来性能提升。
由于GPU不会缓存零拷贝内存的内容,如果多次读取内存,性能可能会出现严重的恶化,还不如一开始就将数据复制到GPU。

使用多个GPU

1. 点积计算中所需的全部数据

struct DataStruct
{
	int deviceID;//设备标识
	int size;//缓冲区大小
	float *a;//输出缓冲区指针
	float *b;//输入缓冲区指针
	float returnValue;//点积运行结果
};

2. 获得系统中CUDA处理器的数量

	int deviceCount;
	cudaGetDeviceCount(&deviceCount);
	if (deviceCount < 2)
	{
		printf("we need at least two computr 1.0 or greater devices, but only found %d\n", deviceCount);
		system("pause");
		return 0;
	}

3. 用不同的CPU线程控制不同的GPU
每个GPU都需要由一个不同的CPU线程来控制。书中将多线程代码的大部分复杂性都移入到辅助代码文件book.h中。在精简了代码后,我们需要做的就是填充一个结构来执行计算。

	//设置一下线程执行的基本DataStruct信息
	DataStruct data[2];
	data[0].deviceID = 0;
	data[0].size = N / 2;
	data[0].a = a;
	data[0].b = b;
	
	data[1].deviceID = 1;
	data[1].size = N / 2;
	data[1].a = a + N / 2;
	data[1].b = b + N / 2;

4. 创建一个新的CPU线程调用GPU0

	CUTThread thread = start_thread( routine, &(data[0]));

函数start_thread()将创建一个新线程,这个线程将调用routine(),并将DataStruct变量作为参数传递进去。
5. 在当前CPU线程中调用GPU1

	routine(&(data[1]));

6.结束新的CPU线程

	end_thread(thread);

通过调用 end_thread(thread), 主应用程序线程将等待另一个线程执行完成。
7.routine()函数
**其中最重要的是调用cudaSetDevice()来设定指定的GPU设备。**其他都是平凡的。

//DWORD WINAPI routine(PVOID *pvoidData)
void* routine(void *pvoidData)
{
	DataStruct *data = (DataStruct*)pvoidData;
	cudaSetDevice(data->deviceID);//设置指定的GPU设备
	int size = data->size;
	float *a, *b, c, *partial_c;
	float *dev_a, *dev_b, *dev_partial_c;

	//在CPU上分配内存
	a = data->a;
	b = data->b;
	partial_c = (float*)malloc(blocksPerGrid*sizeof(*partial_c));

	//在GPU上分配内存
	cudaMalloc((void **)&dev_a, size*sizeof(*dev_a));
	cudaMalloc((void **)&dev_b, size*sizeof(*dev_b));
	cudaMalloc((void **)&dev_partial_c, blocksPerGrid*sizeof(*dev_partial_c));

	//将数组a和b复制到GPU上
	cudaMemcpy(dev_a, a, size*sizeof(*a), cudaMemcpyHostToDevice);
	cudaMemcpy(dev_b, b, size*sizeof(*b), cudaMemcpyHostToDevice);

	dot << <blocksPerGrid, threadsPerBlock >> >(size, dev_a, dev_b, dev_partial_c);

	//将数组partial_c从GPU复制到CPU上
	cudaMemcpy(partial_c, dev_partial_c, blocksPerGrid*sizeof(*dev_partial_c), cudaMemcpyDeviceToHost);

	//在CPU上做最后的加和处理
	c = 0;
	for (int i = 0; i < blocksPerGrid; i++)
	{
		c += partial_c[i];
	}

	//释放GPU内存
	cudaFree(dev_a);
	cudaFree(dev_b);
	cudaFree(dev_partial_c);

	//释放CPU内存
	free(partial_c);

	data->returnValue = c;

	return 0;

}

8. 完整代码

// 代码11.3使用多个GPU
//时间:2019.08.03
#include "cuda_runtime.h"
#include "device_launch_parameters.h"
#include <iostream>
#include "book.h"

#define imin(a,b) (a<b?a:b)


const int N = 33 * 1024 * 1024;
const int threadsPerBlock = 256;
const int blocksPerGrid = imin(32, (N + threadsPerBlock - 1) / threadsPerBlock);


__global__ void dot(int size, float *a, float *b, float *partial_c)
{
	__shared__ float cache[threadsPerBlock];
	int tid = threadIdx.x + blockIdx.x*blockDim.x;
	int cacheIndex = threadIdx.x;

	float temp = 0;
	while (tid < size)
	{
		temp += a[tid] * b[tid];
		tid += gridDim.x*blockDim.x;
	}

	//设置cache中的值
	cache[cacheIndex] = temp;

	//同步这个线程块中的线程
	__syncthreads();

	//规约运算
	int i = blockDim.x / 2;
	while (i != 0)
	{
		if (cacheIndex < i)
		{
			cache[cacheIndex] += cache[cacheIndex + i];
		}
		__syncthreads();
		i /= 2;
	}

	if (cacheIndex == 0)
	{
		partial_c[blockIdx.x] = cache[0];
	}

}


struct DataStruct
{
	int deviceID;//设备标识
	int size;//缓冲区大小
	float *a;//输出缓冲区指针
	float *b;//输入缓冲区指针
	float returnValue;//点积运行结果
};

//DWORD WINAPI routine(PVOID *pvoidData)
void* routine(void *pvoidData)
{
	DataStruct *data = (DataStruct*)pvoidData;
	cudaSetDevice(data->deviceID);//设置指定的GPU设备
	int size = data->size;
	float *a, *b, c, *partial_c;
	float *dev_a, *dev_b, *dev_partial_c;

	//在CPU上分配内存
	a = data->a;
	b = data->b;
	partial_c = (float*)malloc(blocksPerGrid*sizeof(*partial_c));

	//在GPU上分配内存
	cudaMalloc((void **)&dev_a, size*sizeof(*dev_a));
	cudaMalloc((void **)&dev_b, size*sizeof(*dev_b));
	cudaMalloc((void **)&dev_partial_c, blocksPerGrid*sizeof(*dev_partial_c));

	//将数组a和b复制到GPU上
	cudaMemcpy(dev_a, a, size*sizeof(*a), cudaMemcpyHostToDevice);
	cudaMemcpy(dev_b, b, size*sizeof(*b), cudaMemcpyHostToDevice);

	dot << <blocksPerGrid, threadsPerBlock >> >(size, dev_a, dev_b, dev_partial_c);

	//将数组partial_c从GPU复制到CPU上
	cudaMemcpy(partial_c, dev_partial_c, blocksPerGrid*sizeof(*dev_partial_c), cudaMemcpyDeviceToHost);

	//在CPU上做最后的加和处理
	c = 0;
	for (int i = 0; i < blocksPerGrid; i++)
	{
		c += partial_c[i];
	}

	//释放GPU内存
	cudaFree(dev_a);
	cudaFree(dev_b);
	cudaFree(dev_partial_c);

	//释放CPU内存
	free(partial_c);

	data->returnValue = c;

	return 0;

}



int main()
{
	int deviceCount;
	cudaGetDeviceCount(&deviceCount);
	if (deviceCount < 2)
	{
		printf("we need at least two computr 1.0 or greater devices, but only found %d\n", deviceCount);
		system("pause");
		return 0;
	}

	float *a = (float*)malloc(sizeof(*a)*N);
	float *b = (float*)malloc(sizeof(*b)*N);

	//用数据填充主机内存
	for (int i = 0; i < N; i++)
	{
		a[i] = i;
		b[i] = 2 * N;
	}

	//设置一下线程执行的基本DataStruct信息
	DataStruct data[2];
	data[0].deviceID = 0;
	data[0].size = N / 2;
	data[0].a = a;
	data[0].b = b;
	
	data[1].deviceID = 1;
	data[1].size = N / 2;
	data[1].a = a + N / 2;
	data[1].b = b + N / 2;

	CUTThread thread = start_thread( routine, &(data[0]));
	routine(&(data[1]));
	end_thread(thread);
	free(a);
	free(b);
	printf("Value calculated: %f\n", data[0].returnValue + data[1].returnValue);


	system("pause");
	return 0;

}

详解 CUDA By Example 中的 Julia Set 绘制GPU优化 笔者测试环境VS2019。 基本介绍 原书作者引入Julia Sets意在使用GPU加速图形的绘制。Julia Set 是指满足下式迭代收敛的复数集合 Zn+1=Zn2+C Z_{n+1}=Z_{n}^2+CZn+1​=Zn2​+C 环境配置 跑这个例子的主要困难应该在于配置环境。这个程序依赖于openGL中的glut库。由于VS2019的整个软件架构发生了很大变化,一些链接库和头文件的位置都发生... 阅读详情

相关推荐

ONFI5.0命令字实战指南:从基础操作到高级应用

本文深入解析ONFI5.0协议的核心——命令字,提供从基础操作到高级应用的实战指南。详细拆解了NAND闪存的复位、读状态、数据读取(00-30)、页编程(80-10)和块擦除(60-D0)等关键命令周期,并探讨了多平面操作与缓存操作等高级性能优化技巧,帮助开发者高效、稳定地与NAND芯片进行底层交互。

vscode5coder的博客 463

cudaMallocHost函数详解

在CUDA2.2以下,仅提供cudaMallocHost函数用于分配页锁定内存,与C语言函数malloc分配分页内存相对应。 而从CUDA2.2开始,页锁定内存增加三种新的类型用于主机多线程的portable,用于高效写回write-combined以及零拷贝的mapping,用cudaHostAlloc进行分配,其中采用四个可选参数标志用以指定使用何种特性: cudaHostAllocDef

Bruce_0712的博客 2万+

GQA数据集介绍

GQA,这是一个用于真实世界视觉推理和组合问答的新数据集。2200万个不同的推理问题,所有这些问题都带有表示其语义的功能程序。答案分布受到严格控制。11.3万张图像、2200万个问题,推理能力有对象和属性识别、传递关系跟踪、空间推理、逻辑推理和比较。

qq_56551150的博客 2956

cuda学习笔记(一)存储

1. 一个GPU上有很多的sm(stream Multiprocessor),每个sm中包括了8个sp(stream Processor)标量流处理器,商业宣传中所说的数百个“核”,大多指的是sp的数量。隶属于同一个sm的sp共用同一套取指与发射单元。CUDA中的kernel是以block为单位执行的,一个block必须在一个sm上执行,一个sp执行一个线程,但是一个sm可以同时存在多个bloc

moonboat0331的专栏 844

PHP实现SM2算法(附完整源码)

PHP实现SM2算法(附完整源码)

希望我的博客,能帮上你解决学习中工作中所遇到的问题 4025

【使用DeepSeek辅助Python接口性能自动化测试教程-实战教程】

访问DeepSeek官方网站,注册账号并获取API Key。此Key是调用DeepSeek API的必要凭证,务必妥善保管。安装时,注意勾选“Add Python to PATH”选项,以便系统能识别Python命令。确保你已安装Python 3.8或更高版本。会自动发现并执行所有以。

Dreams°的博客 2467

CUDA:cudaHostAlloc()

cudaHostAlloc() 是 CUDA 中的一个函数,用于在主机(CPU)上分配页锁定(或称为“固定”)内存。页锁定内存与普通的可分页主机内存相比有一些优点,尤其是对于 CUDA 编程:更高的数据传输速度:当数据需要从主机内存传输到 GPU 设备内存时,页锁定内存通常能够提供更高的带宽,因为 GPU 可以直接访问物理内存页,而无需通过操作系统的页面交换机制。异步数据传输:页锁定内存允许主机和设备之间的数据传输与主机上的其他操作并行进行。

weixin_39244242的博客 2934

DeepSmote:一种深度SMOTE方法

DeepSmote 一种全新的将深度学习与Smote相结合的过采样方法! 目前在许多领域已经被使用,值得大家关注学习!

走在深度学习前沿的小宋 1447

【C++】CUDA期末复习指南下(详细)

上期CUDA期末复习指南我们主要讲了GPU的串行/并行以及一些背诵的知识点,这篇博客我们继续介绍cuda的函数以及cuda编程,常考的CUDA函数和编程题博主在这里为大家总结一下,希望对大家有所帮助。

qq_62662919的博客 2354

Python 爬虫实战:模拟登录网易云音乐(加密接口逆向与自动化登录实现)

网易云音乐作为国内主流的音乐流媒体平台,其丰富的音乐资源和个性化推荐算法吸引了大量用户。对于开发者而言,模拟登录网易云音乐并实现自动化操作,可以用于数据分析、歌单同步、歌词抓取等场景。然而,网易云音乐采用了多重加密机制和反爬策略,直接模拟登录存在较大挑战。

yansideyucsdn的博客 2590

【第07例】IPD体系进阶: 6 个技术评审点(TR)

TR技术评审点

产品线负责人 4421

《CUDA By Example》【Chapter 11】多GPU系统上的CUDA C?

11.1 概述 了解如何分配和使用零拷贝内存(zero-copy memory) 了解如何在同一个应用程序中使用多个GPU 了解如何分配和使用可移动的固定内存(Portable pinned Memory) 11.2 零拷贝主机内存 固定内存(页锁定内存)能确保不会交换出物理内存。我们通过调用cudaHostAlloc()来分配这种内存,并且传递参数cudaHostAllocDefa...

wondervictor的博客 613

最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版

Part-E1-SDIO-Specification-Ver4.00-Final-120220.pdf:最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版,包括SDIO 接口的外设,SDIO 接口官方协议等。

CUDA by Example 源代码项目推荐

CUDA by Example 源代码项目推荐 1. 项目基础介绍和主要编程语言 CUDA by Example 是一个开源项目,旨在帮助开发者理解和使用CUDA技术。该项目包含了书籍《CUDA by Example》的所有示例代码,由两位NVIDIA的高级软件工程师Jason Sanders和Edward Kandrot编写。项目主要使用C、C++和CUDA作为编程语言,适合有一定编程基础的开发...

gitblog_01041的博客 528

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

电子书下载:CUDA by Example: An Introduction to General-Purpose GPU Programming

  Book DescriptionCUDA is a computing architecture designed to facilitate the development of parallel programs. In conjunction with a comprehensive software platform, the CUDA Architecture enables ...

weixin_33882452的博客 1215

VB摄像头监控

该VB模块可对摄像头捕获的画面进行录像等,可在创建的窗口进行控制

CUDA by example Chapter3 CUDA C简介

CUDA C简介CPU上的Hello WorldGPU上的Hello World__global__修饰符:向kernal()中传递参数cudaMalloc()函数:cudaFree()函数:cudaMemcpy()函数:查询设备信息cudaGetDeviceCount()函数:结构体cudaDevicePropcudaGetDeviceProperties()函数:设备属性的使用:选择一个符合条...

wolfcsharp的博客 394

CUDA_BY_EXAMPLE头文件资源:为CUDA开发加速

CUDA_BY_EXAMPLE头文件资源:为CUDA开发加速 去发现同类优质开源项目:https://gitcode.com/ 项目介绍 在现代计算机科学领域,并行计算和图形渲染技术已成为不可或缺的技术手段。CUDA_BY_EXAMPLE 头文件资源正是为了满足这一需求而诞生的开源项目。它包含了一系列头文件,这些头文件是CUDA相关程序运行和开发的关键组成部分。通过提供与CUDA、OpenGL以及...

gitblog_06724的博客 672

CUDA By Example(三)——CUDA C并行编程

CUDA C并行编程,主要通过矢量求和与Julia集的绘制进行说明。以及对编译运行代码过程中遇到的问题及解决方案进行说明

hhhcbw的博客,欢迎各位来访 1542

Cuda By Example - 1

初学cuda

dairyman的专栏 1160

CUDA by example Chapter4 CUDA C并行编程

CUDA C 并行编程CPU矢量求和运算GPU矢量求和运算尖括号语法<<CPU矢量求和运算 /* //代码4.2.1.1基于CPU的矢量求和运算 //时间:2019.07.20 */ #include "cuda_runtime.h" #include "device_launch_parameters.h" #include <iostream> #define N 1...

wolfcsharp的博客 369

CUDA By Example(一)——为什么需要CUDA

主要介绍了并行计算,为什么需要CUDA,以及CUDA架构是什么

hhhcbw的博客,欢迎各位来访 1650

NVIDIA CUDA 学习 (1) CUDA By Example

CUDA By ExampleHost和DeviceCUDA C编程分配空间函数定义和执行使用规范 Host和Device host就是你的CPU,Device就是你的GPU。 CUDA C编程 #include <iostream> #include "book.h" __global__ void add( int a, int b, int *c ) { *c = a + b; } int main( void ) { int c; int *dev_c; HANDLE_ERROR

weixin_47658743的博客 595

CUDA By Example(八)——流

CUDA 流操作,使用Stream加速程序执行

hhhcbw的博客,欢迎各位来访 2823

《CUDA By Example》【Chapter 04】CUDA C 并行编程

GPU计算的应用前景在很大程度上取决于能否从很多问题中发掘出大规模并行性。 add_loop_cpu.cu #include &amp;amp;amp;amp;quot;../common/book.h&amp;amp;amp;amp;quot; #define N 10 void add( int *a, int *b, int *c ) { int tid = 0; // this is CPU zero, so we start at ze...

wondervictor的博客 299

CUDA By Example(二)——CUDA C简介

CUDA C的简单介绍,包括 __global__关键字以及 kernel 关键字,以及如何查询设备的数量与属性

hhhcbw的博客,欢迎各位来访 1041

Cuda by Example 配置

Cuda by Example 真是一本好书,学习必备。 从网站下载源码:https://developer.nvidia.com/cuda-example 下载完解压,里边有一些动态库和作者自己写的头文件,注意一定要包含,否则运行必出错啊   之后解压文件到专用目录 F:\cuda_example   接着配置链接库   1:需要把.lib文件放入VS2013安装路径lib文件

runner668的博客 736
上一篇: 树莓派小车识别场地内特定颜色的直线并据此修正方向的方法
下一篇: sklearn中的make_blobs
Leo-Ma
博客等级 码龄11年 1701粉丝 124原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值