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的整个软件架构发生了很大变化,一些链接库和头文件的位置都发生... 阅读详情

相关推荐

ADS 2013安装教程

1.2013和2014安装过程是一样的,运行ads2013_06_shp_windows文件进行安装 2.点击“NEXT”。 3.选择I accept,然后点击“NEXT”。 4.选择“complete”,然后点击“NEXT”。 5.选择安装路径,最好默认安装路径,安装路径不要有中文,然后点击“NEXT”。 6.点击“NEXT”. 7.如下路径默认安装,然后点击“NEXT”。 8.点击“NEXT”。 ...

谢谢关注“小鱼教你模数电”,各平台同号 6009

cudaMallocHost函数详解

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

Bruce_0712的博客 2万+

Vertx之Grpc消息发送

介绍 Vert.x gRPC 模块,它将 Google gRPC 的编程风格与 Vert.x 风格保持一致. gRPC 是一个高性能、开源、通用的RPC框架,是一种进程间通讯技术,由Google推出,基于HTTP2协议标准设计开发,默认采用Protocol Buffers数据序列化协议,支持多种开发语言。gRPC提供了一种简单的方法来精确的定义服务,并且为客户端和服务端自动生成可靠的功能库。 在gRPC客户端可以直接调用不同服务器上的远程程序,使用姿势看起来就像调用本地程序一样,轻松的实现远端服务的连接、

Lance 1188

cuda学习笔记(一)存储

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

moonboat0331的专栏 844

Ovito隐藏技巧:用Expression Selection快速提取分子链(附模糊背景配置参数)

本文详细介绍了Ovito软件中Expression Selection功能的隐藏技巧,用于从复杂的分子动力学模拟数据中快速、精准地提取目标分子链。通过结合Expand Selection和Invert Selection等修饰器,形成一套高效的工作流,并深入解析了实现SCI论文级渲染效果的景深模糊参数配置,帮助研究者大幅提升数据处理与图片生成效率。

pz890123的博客 868

华为昇腾 910B 实战:DeepSeek-R1 蒸馏模型高效部署与性能调优全解析

本文详细解析了在华为昇腾910B硬件平台上高效部署DeepSeek-R1蒸馏系列模型(14B/32B版本)的完整实战流程。内容涵盖从MindIE环境搭建、镜像获取、模型准备,到服务配置、内存参数调优、性能测试及常见问题排查,为开发者提供了基于昇腾910B进行大模型部署与优化的全链路指南。

weixin_28235513的博客 211

CUDA:cudaHostAlloc()

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

weixin_39244242的博客 2934

[PCIE5.0] 4.2.3.2 Encoding of Presets

• 发射器预设:发射器预设值用4位二进制编码,表示不同的预设设置(P0 到 P10),这些设置定义了信号的传输特性。• 接收器预设提示:接收器预设提示是通过3位二进制编码告诉接收器建议的增益值,用于调整接收器的灵敏度(适用于8.0 GT/s数据速率)。

系统固件工程师 575

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

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

qq_62662919的博客 2354

【系统架构设计师-2024下半年真题】案例分析-参考答案及部分详解(完整回忆版)

根据h中描述,为了保证高可靠性,有ping/echo,心跳机制两种策略,从资源利用率问为啥心跳更好。王工觉得使用ping/echo好,但李工觉得从系统资源利用率来说,使用心跳机制好 1、针对质量属性可以使用ping/echo和心跳模式实现,分别简述ping/echo和心跳的实现原理。 2、张工认为心跳策略比较适合,简述为什么?

数据知道的博客 1万+

别让 Claude Code 一直问你“能不能执行“:权限配置完全指南

Claude Code 提供了一套精细的权限配置机制,配置好权限设置,可以提速不少。这个小技巧需要掌握一下。

沐风 3844

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

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

wondervictor的博客 613

多出来的那一截:Java 堆外内存 OOM 的来源分账、分流排查与治理解析

Java进程的RES常比-Xmx多出一截,堆却平稳。本文把堆外内存拆成直接缓冲区、Unsafe、JNI本地库、线程栈、元空间与文件映射六类,用JDK 8与17的六组实验对照RSS、NMT、BufferPool三本账:clear()不释放内存;MaxDirectMemorySize默认约等于堆上限且只管直接缓冲区;禁用显式GC会让申请线程退避半秒后失败;未关闭的Deflater零GC下让RSS涨1GB;Netty池内泄漏GC救不回。最后给出定性、对账、定位的排查路线与预算、编码、监控三层治理。

曾经“等你生日那天”都遥远得像未来,如今却可欢愉的挥手说“下个十年见” 14万+

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

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

电子书下载: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

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

CUDA by example Chapter3 CUDA C简介

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

wolfcsharp的博客 394

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

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、付费专栏及课程。

余额充值