CUDA by example Chapter5 线程协作

国内免费部署GPT5.6与多AI工具整合方案实践指南 人工智能模型部署是当前技术领域的热点话题,其核心原理在于将预训练的大语言模型在本地环境中运行,实现离线推理能力。从技术价值角度看,本地部署不仅能有效解决API调用限制和网络访问问题,还能保障数据隐私安全,降低长期使用成本。在实际应用场景中,开发者可以通过整合GPT、Claude、Gemini等主流AI工具,构建统一的本地服务平台,支持文本生成、代码编写、图像处理等多模态任务。本文重点介绍的GPT5.6 sol部署方案,结合硬件优化和批量任务处理技术,为开发者提供了一套完整的实践指南,特别适合需要频繁使用多个 阅读详情

并行线程块的分解

使用线程实现GPU上的矢量求和

在上一章代码下需要修改两个地方:

1. add<<<N,1>>>(dev_a,dev_b,dev_c) 修改为 add<<<1,N>>>(dev_a,dev_b,dev_c)
2. int tid = blockIdx.x 修改为 int tid = threadIdx.x

完整代码:

//代码5.2.1.1使用thread实现GPU上的矢量求和
//时间:2019.07.24
#include "cuda_runtime.h"
#include "device_launch_parameters.h"
#include <iostream>


#define N 10

__global__ void add(int *a, int *b, int *c)
{
	int tid = threadIdx.x;//计算位于这个索引处的数据
	if (tid < N)
		c[tid] = a[tid] + b[tid];
}


int main()
{
	int a[N], b[N], c[N];
	int *dev_a, *dev_b, *dev_c;

	//在GPU上分配内存
	cudaMalloc((void **)&dev_a, N*sizeof(int));
	cudaMalloc((void **)&dev_b, N*sizeof(int));
	cudaMalloc((void **)&dev_c, N*sizeof(int));

	//在CPU上为数组'a'和‘b’赋值
	for (int i = 0; i < N; i++)
	{
		a[i] = -i;
		b[i] = i*i;
	}

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

	add << <1, N >> >(dev_a, dev_b, dev_c);

	//将数组'c'从GPU复制到CPU
	cudaMemcpy(c, dev_c, N*sizeof(int), cudaMemcpyDeviceToHost);

	//显示结果
	for (int i = 0; i < N; i++)
	{
		printf("%d + %d = %d\n", a[i], b[i], c[i]);
	}
	//释放在GPU上分配的内存
	cudaFree(dev_a);
	cudaFree(dev_b);
	cudaFree(dev_c);

	system("pause");
	return 0;
}

运行结果:
在这里插入图片描述

在GPU上对更长的矢量求和

原因:

  1. 在GPU中,每个Block可以拥有的thread数量存在上限,由设备属性结构中maxThreadsPerBlock指定
  2. 硬件将Block的数量限制为不超过65535个

因此,需要将Block与thread结合起来,以对更长的矢量进行求和。

在上一章的代码下需要修改以下3个地方:

1. int tid = blockIdx.x 修改为 int tid = threadIdx.x + blockIdx.x*blockDim.x
2. 假设将每个Block中线程数量固定为128,那么
	add<<<N,1>>>(dev_a,dev_b,dev_c) 修改为 add<<<(N+127)/128,128>>>(dev_a,dev_b,dev_c)
3. 由于(N+127)/128是将Block数量向上取整,所以申请的总线程数会大于等于需要的线程数,
	因此在访问输入数组和输出数组之前,必须检查线程的偏移是否位于0到N之间:
	if(tid < N)
		c[tid] = a[tid] + b[tid]

完整代码:

// 代码5.2.1.2使用block和thread实现GPU上的更大规模的矢量求和
//时间:2019.07.24
#include "cuda_runtime.h"
#include "device_launch_parameters.h"
#include <iostream>


#define N 10000

__global__ void add(int *a, int *b, int *c)
{
	int tid = threadIdx.x + blockIdx.x*blockDim.x;//计算位于这个索引处的数据
	if (tid < N)
		c[tid] = a[tid] + b[tid];
}


int main()
{
	int a[N], b[N], c[N];
	int *dev_a, *dev_b, *dev_c;

	//在GPU上分配内存
	cudaMalloc((void **)&dev_a, N*sizeof(int));
	cudaMalloc((void **)&dev_b, N*sizeof(int));
	cudaMalloc((void **)&dev_c, N*sizeof(int));

	//在CPU上为数组'a'和‘b’赋值
	for (int i = 0; i < N; i++)
	{
		a[i] = -i;
		b[i] = i*i;
	}

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

	add << <(N+127)/128, 128 >> >(dev_a, dev_b, dev_c);

	//将数组'c'从GPU复制到CPU
	cudaMemcpy(c, dev_c, N*sizeof(int), cudaMemcpyDeviceToHost);

	//显示结果
	for (int i = 0; i < N; i++)
	{
		printf("%d + %d = %d\n", a[i], b[i], c[i]);
	}
	//释放在GPU上分配的内存
	cudaFree(dev_a);
	cudaFree(dev_b);
	cudaFree(dev_c);

	system("pause");
	return 0;
}

运行结果:
在这里插入图片描述

在GPU上对任意长度的矢量求和

原因:
GPU的Block数量由硬件限制,最大为65535;每个Block中thread的数量由设备属性结构中maxThreadsPerBlock指定,也存在一个上限,因此每个GPU可以提供的线程总数量具有上限
对任意大小的矢量进行并行计算,常用的trick就是在核函数中用一个while()循环对数据进行迭代计算,每次递增的数量 = blockDim.x * gridDim.x

在上一章的代码下需要修改以下几个地方:

1. 核函数
__global__ void add(int *a, int *b, int *c)
{
	int tid = threadIdx.x + blockIdx.x*blockDim.x;	//计算位于这个索引处的数据
	while (tid < N)
	{
		c[tid] = a[tid] + b[tid];
		tid += blockDim.x*gridDim.x;				//每次递增
	}
}

2. 以合适的固定大小的grid_size和block_size启动核函数
	add<<<128,128>>>(dev_a,dev_b,dev_c) 

完整代码:

// 代码5.2.1.3使用block和thread实现GPU上的任意规模的矢量求和
//时间:2019.07.24
#include "cuda_runtime.h"
#include "device_launch_parameters.h"
#include <iostream>


#define N 10000

__global__ void add(int *a, int *b, int *c)
{
	int tid = threadIdx.x + blockIdx.x*blockDim.x;//计算位于这个索引处的数据
	while (tid < N)
	{
		c[tid] = a[tid] + b[tid];
		tid += blockDim.x*gridDim.x;
	}
		
}


int main()
{
	int a[N], b[N], c[N];
	int *dev_a, *dev_b, *dev_c;

	//在GPU上分配内存
	cudaMalloc((void **)&dev_a, N*sizeof(int));
	cudaMalloc((void **)&dev_b, N*sizeof(int));
	cudaMalloc((void **)&dev_c, N*sizeof(int));

	//在CPU上为数组'a'和‘b’赋值
	for (int i = 0; i < N; i++)
	{
		a[i] = -i;
		b[i] = i*i;
	}

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

	add << <128, 128 >> >(dev_a, dev_b, dev_c);

	//将数组'c'从GPU复制到CPU
	cudaMemcpy(c, dev_c, N*sizeof(int), cudaMemcpyDeviceToHost);

	//显示结果
	for (int i = 0; i < N; i++)
	{
		printf("%d + %d = %d\n", a[i], b[i], c[i]);
	}

	//验证GPU确实完成了我们要求的工作
	bool success = true;
	for (int i = 0; i < N; i++)
	{
		if ((a[i] + b[i]) != c[i])
		{
			printf("Error:%d + %d != %d\n", a[i], b[i], c[i]);
			success = false;
		}
	}
	if (success) printf("we did it!");

	//释放在GPU上分配的内存
	cudaFree(dev_a);
	cudaFree(dev_b);
	cudaFree(dev_c);

	system("pause");
	return 0;
}

运行结果:
在这里插入图片描述

在GPU上使用线程实现波纹效果

解释说明:

  1. 构建数据块结构体,用于组织CPU以及GPU端的数据
struct DataBlock
{
	unsigned char *dev_bitmap;
	CPUAnimBitmap *bitmap;//CPU中用于动画的一帧位图
};
  1. 像素映射
//将blockIdx/threadIdx映射到像素位置(二维)
int x = threadIdx.x + blockIdx.x*blockDim.x;
int y = threadIdx.y + blockIdx.y*blockDim.y;
//计算该像素(二维)的索引值
int offset = x + y*DIM;//blockDim.x*gridDim.x;

完整代码:

// 代码5.2.2使用block和thread实现波纹效果
//时间:2019.07.25
#include "cuda_runtime.h"
#include "device_launch_parameters.h"
#include <iostream>
#include "cpu_bitmap.h"
#include "cpu_anim.h"

#define DIM 1050

struct DataBlock
{
	unsigned char *dev_bitmap;
	CPUAnimBitmap *bitmap;//CPU中用于动画的一帧位图
};

//释放在GPU上分配的内存
void cleanup(DataBlock *d)
{
	cudaFree(d->dev_bitmap);
}


//生成图像的核函数是关键代码
//核函数需要知道当前的时间以便生成正确的帧
__global__ void kernal(unsigned char *ptr, int ticks)
{
	//将blockIdx/threadIdx映射到像素位置
	int x = threadIdx.x + blockIdx.x*blockDim.x;
	int y = threadIdx.y + blockIdx.y*blockDim.y;
	if (x < DIM && y < DIM)
	{
		int offset = x + y*DIM;//blockDim.x*gridDim.x;

		//现在计算这个位置上的值
		float fx = x - DIM / 2;
		float fy = y - DIM / 2;
		float d = sqrtf(fx*fx + fy*fy);
		unsigned char grey = (unsigned char)(128.0f + 127.0f*cos(d / 10.0f - ticks / 7.0f) / (d / 10.0f + 1.0f));

		ptr[offset * 4 + 0] = grey;//R
		ptr[offset * 4 + 1] = grey;//G
		ptr[offset * 4 + 2] = grey;//B
		ptr[offset * 4 + 3] = 255;//A
	}


}

//这里关心的是怎么生成一帧图像
void generate_frame(DataBlock *d, int ticks)
{
	dim3 blocks((DIM+15) / 16,(DIM+15) / 16);
	dim3 threads(16, 16);
	kernal << <blocks, threads >> >(d->dev_bitmap, ticks);
	cudaMemcpy(d->bitmap->get_ptr(), d->dev_bitmap, d->bitmap->image_size(), cudaMemcpyDeviceToHost);
}

int main()
{
	//data对于主机来说是全局的,如果要在kernal中使用,必须传递进去
	DataBlock data;
	CPUAnimBitmap bitmap(DIM, DIM, &data);
	data.bitmap = &bitmap;
	cudaMalloc((void **)&data.dev_bitmap, bitmap.image_size());
	bitmap.anim_and_exit((void(*)(void*, int))generate_frame, (void(*)(void*))cleanup);//动画怎么实现,这里并不关心
	//将generate_frame()传递进来,每当要生成一帧新的动画时,就要调用它一次
	//把cleanup()也传递进来,当动画程序外部中断结束时用于释放GPU内存

}

运行结果:
在这里插入图片描述

共享内存与同步

共享内存

  1. 用__shared__进行指定
__shared__ float cache[threadsPerBlock];
  1. 对于在GPU上启动的每一个线程块,CUDA C编译器都将创建共享内存中变量的一个副本,某个线程块中的每个线程都可以共享相应的共享内存变量副本,不同线程块之间的线程之间无法对共享内存变量进行共享。
    在这里插入图片描述
  2. 共享内存大小一般设置成threadsPerBlock,这样Block中的每个thread都可以不受竞争的并行运行。

线程同步

  1. 线程需要同步的原因
    由于引入共享内存,每个Block都拥有共享内存变量的一份copy,它们独立并行高效地运行。但是对所有Block中运行结果进行汇总的时候,必须要确定所有的Block都运行结束,这称之为线程同步,否则将会产生竞态条件,使运行结果具有不确定性。
  2. 用__syncthreads()进行线程同步
    这个函数的调用将确保线程块中的每个线程都执行完__syncthreads()前面的语句后,才会执行下一条语句。当一个线程开始执行__syncthreads()之后的第一条语句时,线程块中的其他线程肯定都已经执行完了__syncthreads()

点积运算

1. 矢量点积运算举例
(x1,x2,x3,x4)·(y1,y2,y3,y4) = x1y1+x2y2+x3y3+x4y4
2. 规约运算
规约运算:对一个输入数组执行某种计算,然后产生一个更小的结果数组
并行规约运算:每个线程将cache[]中的两个值相加,然后将结果保存回cahce[].执行这种操作log2(threadsPerBlock)个步骤后,就能得到cache[]中所有值的总和。
3. 将block中所有线程计算的加和值保存到全局内存
因为一个block最终只产生一个结果,保存在cache[0]中.因此,只需要一个线程执行保存工作。这里选用了0号线程来执行它,其实每个线程都可以执行这个写入操作,书中讲到选用其他的线程来执行写入操作将会带来不必要的内存通信量。
解释如下:
我没有去查英文原文,但我武断并且自信地认为,书中的意思是指如果不加if(cacheIndex==0),那么一个block中的每个线程都会将结果写入到全局内存中,显然,这么做在写入单个值时效率非常差。在本书示例中,选用任意线程来将一个block的最终结果(cache[0])写入到全局内存中所花费的代价是相同的,并不会产生额外的内存通信量。

4. 完整代码

// 代码5.3.1基于共享内存和同步的点积运算
//时间:2019.07.25
#include "cuda_runtime.h"
#include "device_launch_parameters.h"
#include <iostream>
//#include <cuda.h>
//#include <device_functions.h>

#define imin(a,b) (a<b?a:b)
#define sum_squares(x) (x*(x+1)*(2*x+1)/6)

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

__global__ void dot(float *a, float *b, float *c)
{
	__shared__ float cache[threadsPerBlock];
	int tid = threadIdx.x + blockIdx.x*blockDim.x;
	int cacheIndex = threadIdx.x;
	float temp = 0;
	while (tid < N)
	{
		temp += a[tid] * b[tid];
		tid += blockDim.x * gridDim.x;
	}

	//设置cache中相应位置上的值
	cache[cacheIndex] = temp;

	//对线程块中的线程进行同步
	__syncthreads();

	//规约
	//对于规约运算来说,以下代码要求threadPerBlock必须是2的整数
	int i = blockDim.x / 2;
	while (i != 0)
	{
		if (cacheIndex < i)
			cache[cacheIndex] += cache[cacheIndex + i];

		__syncthreads();//等待所有线程执行完上面的代码
		i /= 2;
	}
	//由第一个线程负责将这个block中计算的加和值保存到c中
	if (cacheIndex == 0)
	{
		c[blockIdx.x] = cache[0];
	}

}

int main()
{
	float *a, *b, c, *partial_c;
	float *dev_a, *dev_b, *dev_partial_c;
	//CPU上分配内存
	a = (float *)malloc(N*sizeof(float));
	b = (float *)malloc(N*sizeof(float));
	partial_c = (float *)malloc(blocksPerGrid*sizeof(float));

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

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

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

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

	//将数组'c'从GPU复制回CPU
	cudaMemcpy(partial_c, dev_partial_c, blocksPerGrid*sizeof(float), cudaMemcpyDeviceToHost);

	//在CPU上完成最终的求和运算
	c = 0;
	for (int i = 0; i < blocksPerGrid; i++)
	{
		c += partial_c[i];
	}

	//验证结果的正确性
	printf("Does GPU value %.6g = %.6g?\n", c, 2 * sum_squares((float)(N - 1)));

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

	//释放CPU上的内存
	delete[] a;
	delete[] b;
	delete[] partial_c;

	system("pause");
}

5. 运行结果
在这里插入图片描述
显然,计算结果通过了验证

不正确的点积运算优化

__syncthreads()函数将会等待一个block中所有的线程结束之后,才会运行它之后的内容!!!
对于规约运算:

	//规约
	//对于规约运算来说,以下代码要求threadPerBlock必须是2的整数
	int i = blockDim.x / 2;
	while (i != 0)
	{
		if (cacheIndex < i)
			cache[cacheIndex] += cache[cacheIndex + i];

		__syncthreads();//等待所有线程执行完上面的代码
		i /= 2;
	}

不能改为:

	//规约
	//对于规约运算来说,以下代码要求threadPerBlock必须是2的整数
	int i = blockDim.x / 2;
	while (i != 0)
	{
		if (cacheIndex < i)
		{
			cache[cacheIndex] += cache[cacheIndex + i];
			__syncthreads();//等待所有线程执行完上面的代码
		}
		i /= 2;
	}

这将导致线程发散,即某些线程需要执行__syncthreads()函数,某些线程不需要。显然这种线程发散,对于GPU来说是致命的,此时GPU会挂起,等待那些永远也不能有机会执行__syncthreads()函数的线程执行__syncthreads(),这是十分糟糕的。

基于共享内存的位图

// 代码5.3.3基于共享内存的位图
//时间:2019.07.25
#include "cuda_runtime.h"
#include "device_launch_parameters.h"
#include <iostream>
#include "cpu_bitmap.h"

#define DIM 1024
#define PI 3.1415926535897932f

__global__ void kernal(unsigned char *ptr)
{
	//将threadIdx/blockIdx映射到像素位置
	int x = threadIdx.x + blockIdx.x*blockDim.x;
	int y = threadIdx.y + blockIdx.y*blockDim.y;
	int offset = x + y*DIM;
	__shared__ float shared[16][16];

	//计算这个像素位置上的值
	const float period = 128.0f;
	shared[threadIdx.x][threadIdx.y] = 255 * (sinf(x*2.0f*PI / period) + 1.0f)*(sinf(y*2.0f*PI / period) + 1.0f) / 4.0f;
	
	__syncthreads();//这里的同步十分重要,如果没有同步,下面的写入操作 可能会在 上面的计算值操作之前完成,而这是不正确的
	
	//把值保存回像素,保留x和y的次序
	ptr[offset * 4 + 0] = 0;
	ptr[offset * 4 + 1] = shared[15 - threadIdx.x][15 - threadIdx.y];
	ptr[offset * 4 + 2] = 0;
	ptr[offset * 4 + 3] = 255;

}

int main()
{
	CPUBitmap bitmap(DIM, DIM);
	unsigned char *dev_bitmap;

	cudaMalloc((void **)&dev_bitmap, bitmap.image_size());

	dim3 grids(DIM / 16, DIM / 16);
	dim3 blocks(16, 16);
	kernal << <grids, blocks >> >(dev_bitmap);
	cudaMemcpy(bitmap.get_ptr(), dev_bitmap, bitmap.image_size(), cudaMemcpyDeviceToHost);

	bitmap.display_and_exit();
	cudaFree(dev_bitmap);
}

(添加了同步的)运行结果:
在这里插入图片描述
(未添加同步的)运行结果:
在这里插入图片描述
可以看到,那些随机出现的亮线和暗线就是写入操作在计算操作之前完成引起的,但是,共享内存的初始值不应该全部为0吗?有暗线是正常的,为什么会有亮线出现呢?出于好奇,我检查了共享内存shared[0][0]的值,没错啊,确实是0!百思不得其解,望着图书馆外校长的雕像发呆好久,然后将shared的所有初始值都打印出来:
在这里插入图片描述
看来,在GPU上申请共享内存时,它的初始值是不确定的!

基于共享内存位图的继续

为了更好的直观演示那些因为没有同步而出现的现象,我先将共享内存的初始值设置为255,然后再进行计算和赋值操作得到的结果如下:
在这里插入图片描述
这符合我们的预期。
完整代码:

/*
//代码5.2.1.1使用thread实现GPU上的矢量求和
//时间:2019.07.24
#include "cuda_runtime.h"
#include "device_launch_parameters.h"
#include <iostream>


#define N 10

__global__ void add(int *a, int *b, int *c)
{
   int tid = threadIdx.x;//计算位于这个索引处的数据
   if (tid < N)
   	c[tid] = a[tid] + b[tid];
}


int main()
{
   int a[N], b[N], c[N];
   int *dev_a, *dev_b, *dev_c;

   //在GPU上分配内存
   cudaMalloc((void **)&dev_a, N*sizeof(int));
   cudaMalloc((void **)&dev_b, N*sizeof(int));
   cudaMalloc((void **)&dev_c, N*sizeof(int));

   //在CPU上为数组'a'和‘b’赋值
   for (int i = 0; i < N; i++)
   {
   	a[i] = -i;
   	b[i] = i*i;
   }

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

   add << <1, N >> >(dev_a, dev_b, dev_c);

   //将数组'c'从GPU复制到CPU
   cudaMemcpy(c, dev_c, N*sizeof(int), cudaMemcpyDeviceToHost);

   //显示结果
   for (int i = 0; i < N; i++)
   {
   	printf("%d + %d = %d\n", a[i], b[i], c[i]);
   }
   //释放在GPU上分配的内存
   cudaFree(dev_a);
   cudaFree(dev_b);
   cudaFree(dev_c);

   system("pause");
   return 0;
}
*/


/*
// 代码5.2.1.2使用block和thread实现GPU上的更大规模的矢量求和
//时间:2019.07.24
#include "cuda_runtime.h"
#include "device_launch_parameters.h"
#include <iostream>


#define N 10000

__global__ void add(int *a, int *b, int *c)
{
   int tid = threadIdx.x + blockIdx.x*blockDim.x;//计算位于这个索引处的数据
   if (tid < N)
   	c[tid] = a[tid] + b[tid];
}


int main()
{
   int a[N], b[N], c[N];
   int *dev_a, *dev_b, *dev_c;

   //在GPU上分配内存
   cudaMalloc((void **)&dev_a, N*sizeof(int));
   cudaMalloc((void **)&dev_b, N*sizeof(int));
   cudaMalloc((void **)&dev_c, N*sizeof(int));

   //在CPU上为数组'a'和‘b’赋值
   for (int i = 0; i < N; i++)
   {
   	a[i] = -i;
   	b[i] = i*i;
   }

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

   add << <(N+127)/128, 128 >> >(dev_a, dev_b, dev_c);

   //将数组'c'从GPU复制到CPU
   cudaMemcpy(c, dev_c, N*sizeof(int), cudaMemcpyDeviceToHost);

   //显示结果
   for (int i = 0; i < N; i++)
   {
   	printf("%d + %d = %d\n", a[i], b[i], c[i]);
   }
   //释放在GPU上分配的内存
   cudaFree(dev_a);
   cudaFree(dev_b);
   cudaFree(dev_c);

   system("pause");
   return 0;
}
*/



/*
// 代码5.2.1.3使用block和thread实现GPU上的任意规模的矢量求和
//时间:2019.07.24
#include "cuda_runtime.h"
#include "device_launch_parameters.h"
#include <iostream>


#define N 10000

__global__ void add(int *a, int *b, int *c)
{
   int tid = threadIdx.x + blockIdx.x*blockDim.x;//计算位于这个索引处的数据
   while (tid < N)
   {
   	c[tid] = a[tid] + b[tid];
   	tid += blockDim.x*gridDim.x;
   }
   	
}


int main()
{
   int a[N], b[N], c[N];
   int *dev_a, *dev_b, *dev_c;

   //在GPU上分配内存
   cudaMalloc((void **)&dev_a, N*sizeof(int));
   cudaMalloc((void **)&dev_b, N*sizeof(int));
   cudaMalloc((void **)&dev_c, N*sizeof(int));

   //在CPU上为数组'a'和‘b’赋值
   for (int i = 0; i < N; i++)
   {
   	a[i] = -i;
   	b[i] = i*i;
   }

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

   add << <128, 128 >> >(dev_a, dev_b, dev_c);

   //将数组'c'从GPU复制到CPU
   cudaMemcpy(c, dev_c, N*sizeof(int), cudaMemcpyDeviceToHost);

   //显示结果
   for (int i = 0; i < N; i++)
   {
   	printf("%d + %d = %d\n", a[i], b[i], c[i]);
   }

   //验证GPU确实完成了我们要求的工作
   bool success = true;
   for (int i = 0; i < N; i++)
   {
   	if ((a[i] + b[i]) != c[i])
   	{
   		printf("Error:%d + %d != %d\n", a[i], b[i], c[i]);
   		success = false;
   	}
   }
   if (success) printf("we did it!");

   //释放在GPU上分配的内存
   cudaFree(dev_a);
   cudaFree(dev_b);
   cudaFree(dev_c);

   system("pause");
   return 0;
}
*/



/*
// 代码5.2.2使用block和thread实现波纹效果
//时间:2019.07.25
#include "cuda_runtime.h"
#include "device_launch_parameters.h"
#include <iostream>
#include "cpu_bitmap.h"
#include "cpu_anim.h"

#define DIM 1050

struct DataBlock
{
   unsigned char *dev_bitmap;
   CPUAnimBitmap *bitmap;//CPU中用于动画的一帧位图
};

//释放在GPU上分配的内存
void cleanup(DataBlock *d)
{
   cudaFree(d->dev_bitmap);
}


//生成图像的核函数是关键代码
//核函数需要知道当前的时间以便生成正确的帧
__global__ void kernal(unsigned char *ptr, int ticks)
{
   //将blockIdx/threadIdx映射到像素位置
   int x = threadIdx.x + blockIdx.x*blockDim.x;
   int y = threadIdx.y + blockIdx.y*blockDim.y;
   if (x < DIM && y < DIM)
   {
   	int offset = x + y*DIM;//blockDim.x*gridDim.x;

   	//现在计算这个位置上的值
   	float fx = x - DIM / 2;
   	float fy = y - DIM / 2;
   	float d = sqrtf(fx*fx + fy*fy);
   	unsigned char grey = (unsigned char)(128.0f + 127.0f*cos(d / 10.0f - ticks / 7.0f) / (d / 10.0f + 1.0f));

   	ptr[offset * 4 + 0] = grey;//R
   	ptr[offset * 4 + 1] = grey;//G
   	ptr[offset * 4 + 2] = grey;//B
   	ptr[offset * 4 + 3] = 255;//A
   }


}

//这里关心的是怎么生成一帧图像
void generate_frame(DataBlock *d, int ticks)
{
   dim3 blocks((DIM+15) / 16,(DIM+15) / 16);
   dim3 threads(16, 16);
   kernal << <blocks, threads >> >(d->dev_bitmap, ticks);
   cudaMemcpy(d->bitmap->get_ptr(), d->dev_bitmap, d->bitmap->image_size(), cudaMemcpyDeviceToHost);
}

int main()
{
   //data对于主机来说是全局的,如果要在kernal中使用,必须传递进去
   DataBlock data;
   CPUAnimBitmap bitmap(DIM, DIM, &data);
   data.bitmap = &bitmap;
   cudaMalloc((void **)&data.dev_bitmap, bitmap.image_size());
   bitmap.anim_and_exit((void(*)(void*, int))generate_frame, (void(*)(void*))cleanup);//动画怎么实现,这里并不关心
   //将generate_frame()传递进来,每当要生成一帧新的动画时,就要调用它一次
   //把cleanup()也传递进来,当动画程序外部中断结束时用于释放GPU内存

}

*/


/*
// 代码5.3.1基于共享内存和同步的点积运算
//时间:2019.07.25
#include "cuda_runtime.h"
#include "device_launch_parameters.h"
#include <iostream>
//#include <cuda.h>
//#include <device_functions.h>

#define imin(a,b) (a<b?a:b)
#define sum_squares(x) (x*(x+1)*(2*x+1)/6)

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

__global__ void dot(float *a, float *b, float *c)
{
   __shared__ float cache[threadsPerBlock];
   int tid = threadIdx.x + blockIdx.x*blockDim.x;
   int cacheIndex = threadIdx.x;
   float temp = 0;
   while (tid < N)
   {
   	temp += a[tid] * b[tid];
   	tid += blockDim.x * gridDim.x;
   }

   //设置cache中相应位置上的值
   cache[cacheIndex] = temp;

   //对线程块中的线程进行同步
   __syncthreads();

   //规约
   //对于规约运算来说,以下代码要求threadPerBlock必须是2的整数
   int i = blockDim.x / 2;
   while (i != 0)
   {
   	if (cacheIndex < i)
   		cache[cacheIndex] += cache[cacheIndex + i];

   	__syncthreads();//等待所有线程执行完上面的代码
   	i /= 2;
   }
   //由第一个线程负责将这个block中计算的加和值保存到c中
   if (cacheIndex == 0)
   {
   	c[blockIdx.x] = cache[0];
   }

}

int main()
{
   float *a, *b, c, *partial_c;
   float *dev_a, *dev_b, *dev_partial_c;
   //CPU上分配内存
   a = (float *)malloc(N*sizeof(float));
   b = (float *)malloc(N*sizeof(float));
   partial_c = (float *)malloc(blocksPerGrid*sizeof(float));

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

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

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

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

   //将数组'c'从GPU复制回CPU
   cudaMemcpy(partial_c, dev_partial_c, blocksPerGrid*sizeof(float), cudaMemcpyDeviceToHost);

   //在CPU上完成最终的求和运算
   c = 0;
   for (int i = 0; i < blocksPerGrid; i++)
   {
   	c += partial_c[i];
   }

   //验证结果的正确性
   printf("Does GPU value %.6g = %.6g?\n", c, 2 * sum_squares((float)(N - 1)));

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

   //释放CPU上的内存
   delete[] a;
   delete[] b;
   delete[] partial_c;

   system("pause");
}
*/



/*
// 代码5.3.3基于共享内存的位图
//时间:2019.07.25
#include "cuda_runtime.h"
#include "device_launch_parameters.h"
#include <iostream>
#include "cpu_bitmap.h"

#define DIM 1024
#define PI 3.1415926535897932f

__global__ void kernal(unsigned char *ptr)
{
//将threadIdx/blockIdx映射到像素位置
int x = threadIdx.x + blockIdx.x*blockDim.x;
int y = threadIdx.y + blockIdx.y*blockDim.y;
int offset = x + y*DIM;
__shared__ float shared[16][16];

//计算这个像素位置上的值
const float period = 128.0f;
shared[threadIdx.x][threadIdx.y] = 255 * (sinf(x*2.0f*PI / period) + 1.0f)*(sinf(y*2.0f*PI / period) + 1.0f) / 4.0f;

__syncthreads();//这里的同步十分重要,如果没有同步,下面的写入操作 可能会在 上面的计算值操作之前完成,而这是不正确的

//把值保存回像素,保留x和y的次序
ptr[offset * 4 + 0] = 0;
ptr[offset * 4 + 1] = shared[15 - threadIdx.x][15 - threadIdx.y];
ptr[offset * 4 + 2] = 0;
ptr[offset * 4 + 3] = 255;

}

int main()
{
CPUBitmap bitmap(DIM, DIM);
unsigned char *dev_bitmap;

cudaMalloc((void **)&dev_bitmap, bitmap.image_size());

dim3 grids(DIM / 16, DIM / 16);
dim3 blocks(16, 16);
kernal << <grids, blocks >> >(dev_bitmap);
cudaMemcpy(bitmap.get_ptr(), dev_bitmap, bitmap.image_size(), cudaMemcpyDeviceToHost);

bitmap.display_and_exit();
cudaFree(dev_bitmap);
}
*/






// 代码5.3.3基于共享内存的位图(添加查看共享内存初始值的代码,发现共享内存初始值为0)
//时间:2019.09.01
#include "cuda_runtime.h"
#include "device_launch_parameters.h"
#include <iostream>
#include "cpu_bitmap.h"

#define DIM 1024
#define PI 3.1415926535897932f

__global__ void kernal(unsigned char *ptr,float *dev_test)
{
   //将threadIdx/blockIdx映射到像素位置
   int x = threadIdx.x + blockIdx.x*blockDim.x;
   int y = threadIdx.y + blockIdx.y*blockDim.y;
   int offset = x + y*DIM;
   __shared__ float shared[16][16];


   dev_test[threadIdx.x * 16 + threadIdx.y] = shared[threadIdx.x][threadIdx.y];
   shared[threadIdx.x][threadIdx.y] = 255;
   __syncthreads();//当所有线程将shared初始值完全赋值为255时,才能继续执行下面的任务


   //计算这个像素位置上的值
   const float period = 128.0f;
   shared[threadIdx.x][threadIdx.y] = 255 * (sinf(x*2.0f*PI / period) + 1.0f)*(sinf(y*2.0f*PI / period) + 1.0f) / 4.0f;
   
   //__syncthreads();//这里的同步十分重要,如果没有同步,下面的写入操作 可能会在 上面的计算值操作之前完成,而这是不正确的
   
   //把值保存回像素,保留x和y的次序
   ptr[offset * 4 + 0] = 0;
   ptr[offset * 4 + 1] = shared[15 - threadIdx.x][15 - threadIdx.y];
   ptr[offset * 4 + 2] = 0;
   ptr[offset * 4 + 3] = 255;

}

int main()
{
   CPUBitmap bitmap(DIM, DIM);
   unsigned char *dev_bitmap;
   float *dev_test;//GPU共享内存初始值变量
   float test[16*16];//CPU共享内存初始值变量

   cudaMalloc((void **)&dev_bitmap, bitmap.image_size());
   cudaMalloc((void **)&dev_test, sizeof(float)*16*16);//给GPU共享内存初始值变量分配内存

   dim3 grids(DIM / 16, DIM / 16);
   dim3 blocks(16, 16);
   kernal << <grids, blocks >> >(dev_bitmap,dev_test);//核函数也做了相应改变
   cudaMemcpy(bitmap.get_ptr(), dev_bitmap, bitmap.image_size(), cudaMemcpyDeviceToHost);
   cudaMemcpy(test, dev_test, sizeof(float)*16*16, cudaMemcpyDeviceToHost);//将GPU共享内存初始值变量的值复制到CPU
   std::cout << "共享内存初始值为:" << std::endl;//输出共享内存初始值
   for (int i = 0; i < 16; i++)
   {
   	for (int j = 0; j < 16; j++)
   	{
   		std::cout << "  " << test[j+i*16];//输出共享内存初始值
   	}
   	std::cout << " " << std::endl;
   }

   cudaFree(dev_bitmap);
   cudaFree(dev_test);

   bitmap.display_and_exit();

}

注意,上述代码中将共享内存初始值赋值给全局内存、将共享内存初始化为255都是多线程的。

	dev_test[threadIdx.x * 16 + threadIdx.y] = shared[threadIdx.x][threadIdx.y];
	shared[threadIdx.x][threadIdx.y] = 255;
	__syncthreads();//当所有线程将shared初始值完全赋值为255时,才能继续执行下面的任务

如果在一个线程中,使用for循环来进行上述操作,即:

	/*
	//线程0将共享内存的初始值赋值给*dev_test
	//线程0将共享内存的初始值设置为255
	if (offset == 0)
	{
		for (int i = 0; i < 16; i++)
		{
			for (int j = 0; j < 16; j++)
			{
				dev_test[j + i * 16] = shared[i][j];
			}
		}
		for (int i = 0; i < 16; i++)
		{
			for (int j = 0; j < 16; j++)
			{
				shared[i][j] = 255;
			}
		}
	}
	__syncthreads();//所有线程必须等待线程0完成上述工作才能继续执行下面的任务
	*/

似乎看起来应该得到同样的效果,但是,亲身测试,结果并不符合预期,因此在CUDA编程中,不推荐在单个线程中使用for循环,具体原因目前还不清楚,后续会在补充说明。

NLP工程师实战知识管理:从周报到可执行工程动作 自然语言处理(NLP)作为人工智能的核心分支,其工程落地高度依赖对数据管道、模型解释性、评估基准和图神经网络等关键技术的深度理解与快速验证。PyTorch DataLoader的num_workers与pin_memory参数协同、ExpBERT将语言解释嵌入模型结构、GLGE多层NLG评估框架、RELVM在生物医学关系抽取中的定制化改造——这些并非孤立知识点,而是构成一线NLP工程师日常调试、迭代与部署的最小可行知识单元(MVKU)。本文基于真实行业周报的逆向拆解,揭示如何将碎片化信息转化为可复现、可度量 阅读详情

相关推荐

CalculiX入门实战:从零配置到可信仿真工作流

有限元分析(FEA)开源工具中,CalculiX以高精度和强扩展性著称,其核心由求解器ccx与图形前端cgx构成,依赖严谨的编译链与环境协同。理解其非单体架构特性,是突破‘ccx怎么用’‘ccx desktop’等高频搜索困惑的关键——它并非开箱即用软件,而是需手动搭建的计算工作流。技术价值体现在对非线性接触、热力耦合等复杂问题的原生支持,以及通过codex接口实现Python参数化驱动的能力。典型应用场景涵盖机械结构强度校核、风电叶片疲劳预测、3D打印支架生物力学仿真等工程实践。本文聚焦真实可执行的安装路

weixin_34224941的博客 371

《GPU高性能编程 CUDA实战》(CUDA By Example)读书笔记

入门书籍,十分简单,看完就可以编写一些简单的CUDA程序了

每天get√新知识 1万+

离线AI实战指南:本地部署大模型处理PDF/Word/Excel

离线AI是指在无网络、不上传数据前提下,于个人设备运行大语言模型并完成文档理解、多轮对话与代码辅助等任务的技术方案。其核心依赖本地模型推理、文档解析向量化和轻量级对话调度三大能力。随着Phi-3、Qwen2、Llama3等轻量化开源模型普及,以及Ollama、LM Studio等易用工具成熟,普通用户已可在主流笔记本上实现PDF摘要、合同比对、代码注释等真实办公场景。本文聚焦离线AI的硬件适配、模型选型、文件处理链路与知识库构建,尤其强调中文OCR支持、结构化文档提取与CPU/GPU混合推理优化,助力非开发

weixin_31968639的博客 497

从头开始进行CUDA编程:原子指令和互斥锁

本文介绍了原子操作,这是协调线程的基本要素。还有互斥锁模式,它利用原子来创建自定义区域,一次只能有一个线程访问这些区域。本文代码在这里:在本系列的篇文章中,介绍了在各种常见情况下使用 Numba CUDA。这些教程并不详尽,但是目的是介绍CUDA 的一些基础的知识,让你对CUDA有一个大概的印象。最后,这里我们没有涉及的一些主题:动态并行性(让内核启动内核)、复杂的同步(例如,warp-level、协作组)、复杂的内存保护(我们在上面提到过)、多 GPU、纹理和许多其他主题。

deephub 4058

【工具】GitHub学生认证+PyCharm配置Copilot全流程指南

本文提供了一份详细的GitHub学生认证及在PyCharm中配置Copilot的完整指南。通过完成A2F认证、准备有效材料并使用干净网络环境,学生可免费获取GitHub Copilot Pro订阅,极大提升编程学习与开发效率。

w7x8y9z的博客 1459

保姆级教程:在RK3588开发板上用MPP库实现H.264硬编码(附完整代码)

本文详细介绍了在RK3588开发板上使用MPP库实现H.264硬编码的全流程,包括环境准备、MPP库编译、编码器初始化与配置、数据输入与编码流程以及高级性能优化技巧。通过实战案例和完整代码,帮助开发者充分利用RK3588的VPU硬件加速能力,提升视频编码效率和质量。

weixin_28716769的博客 414

揭秘CUDA线程同步机制:如何避免数据竞争与死锁?

掌握C语言CUDA的线程同步技巧,有效避免GPU编程中的数据竞争与死锁问题。适用于并行计算密集型场景,通过__syncthreads()实现块内同步,结合内存栅栏确保数据一致性。提升程序稳定性与执行效率,是高性能计算开发的关键基础。值得收藏

codeink的博客 655

基于Hadoop的NBA篮球球员大数据分析与可视化系统大数据分析系统python的项目(源码+lw+部署文档+讲解等)

本系统以 Hadoop 为基础,致力于对 NBA 篮球球员的海量数据进行深入分析与可视化呈现。它能够收集来自多个数据源的丰富数据,包括球员比赛统计数据、身体素质数据、技术特点数据等。利用 Hadoop 的分布式存储和计算能力,对这些大规模数据进行高效处理。通过数据分析算法,挖掘球员的潜在价值和特点,如球员的得分能力趋势、篮板球效率变化、不同位置球员的技术差异等。同时,借助先进的可视化技术,将分析结果以直观的图表、图形等形式展示出来。教练和球队管理层可以通过可视化界面清晰地了解球员的综合表现和发展态势,从而制

QQ3359892174的博客 1314

如何确定cuda程序的block数量和线程数量

第一步获取GPU硬件有多少block,有多少线程,代码如下 int getThreadNum() { cudaDeviceProp prop; int count; HANDLE_ERROR(cudaGetDeviceCount(&count)); printf("gpu num %d\n", count); HANDLE_ERROR(cudaGetDeviceProperties(&prop, 0)); printf("max thre

我的笔记 1万+

Type-c超声波切割刀

接下来,我将提供Type-C超声波切割刀项目的C代码实现示例,代码将按照上述分层架构和模块化设计进行组织。为了达到3000行以上的代码量,我将尽可能详细地实现各个模块的功能,并添加必要的注释和错误处理。通过采用分层架构和模块化设计,我们可以建立一个可靠、高效、可扩展的系统平台,为产品的成功奠定坚实的基础。实际项目中,需要更详细的硬件驱动、更完善的功能模块、更严格的测试验证和更周全的维护升级计划。针对Type-C超声波切割刀这个项目,我将从以下几个方面详细阐述最适合的代码设计架构,并提供具体的C代码实现。

lianmengde的博客 1194

《Zephyr OS 入门与实战》第3章:第一个 Zephyr 程序 - Blinky 实战

解决方法:确认设备树overlay文件已正确加载。返回值,确认驱动初始化状态。

2301_79143213的博客 541

CUDA 关于 BLOCK数目与Thread数目设置

GPU的计算核心是以一定数量的StreamingProcessor(SP)组成的处理器阵列,NV称之为TextureProcessingClusters(TPC),每个TPC中又包含一定数量的StreamingMulti-Processor(SM),每个SM包含8个SP。SP的主要结构为一个ALU(逻辑运算单元),一个FPU(浮点运算单元)以及一个Regi...

weixin_30650859的博客 2064

ESP32学习笔记06-UART 的485 通讯模式

通信方式选择UART 控制器支持多种通信模式,使用函数 uart_set_mode() 可以选择模式。选择特定模式后,UART 驱动程序将处理已连接 UART 设备的相应行为。例如,主机开始向 Tx FIFO 缓冲区写入数据时,UART 驱动程序会自动置位 RTS 管脚(逻辑 1);最后一位数据传输完成后,驱动程序就会取消置位 RTS 管脚(逻辑 0)。要使用此模式,软件。

Car12 2476

《CUDA By Example》【Chapter 05】线程协作 ?

概述 本章介绍代码在各个并行副本之间的通信和协作。 1,了解不同线程之间的通信机制; 2,了解并行执行线程的同步机制; 5.2 并行线程块的分解 add&lt;&lt;&lt;N,1&gt;&gt;&gt;( dev_a, dev_b, dev_c); //第一个参数为想要启动的线程块数量; //第二个参数为CUDA运行时在每个线程块中创建的线程数量; //共创建了N个线程块,N*1...

wondervictor的博客 294

解决开着代理情况下pip或魔搭下载失败

最近由于经常配环境导致,老是要来回切clash关掉代理,非常的不方便如下面的,魔搭模型下载失败如下面的,pip换源后下载失败。

风吹落叶的博客 2950

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

Hands-On GPU加速计算机视觉全解析:OpenCV+CUDA从入门到实战的完整路线图

如果你正在寻找一条系统学习 **GPU加速计算机视觉** 的完整路径,那么 Hands-On-GPU-Accelerated-Computer-Vision-with-OpenCV-and-CUDA 这个开源项目就是为你准备的宝藏仓库。它源自 Packt 出版的同名经典书籍,由资深计算机视觉工程师 Bhaumik Vaidya 编写,将 **OpenCV + CUDA** 双技术栈的实战代码全部开

gitblog_01053的博客 759

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

Markdown转语音实战:从输入处理到批量合成的完整指南

文本转语音(TTS)技术通过深度学习模型将文字信息转换为自然语音输出,其核心原理涉及语音合成和声学建模。这项技术在提升信息可访问性方面具有重要价值,特别适用于多模态内容分发和无障碍服务等场景。在实际工程应用中,MD编辑器文字转语音需要重点关注输入格式的预处理,通过正则表达式过滤Markdown符号,确保合成内容的纯净度。同时,批量任务处理需要建立可靠的队列管理和失败重试机制,本地部署方案需合理配置显存和内存资源,而在线服务则要关注API配额和网络稳定性。这些工程实践要点对于构建稳定的语音合成流水线至关重要,

chutisun0039的博客 289

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

如何用MegaTTS3构建企业级语音合成API?从零到一的完整集成指南

在智能应用快速发展的今天,语音合成技术已成为提升用户体验的关键环节。然而,开发者在集成TTS功能时常常面临三大挑战:**响应延迟高**导致交互卡顿、**资源占用大**影响系统稳定性、**接口不灵活**难以适配多样化业务场景。MegaTTS3作为新一代端到端语音合成系统,通过创新的DiT(Diffusion Transformer)架构和模块化设计,为开发者提供了高性能、低资源消耗的API解决方案。

gitblog_00392的博客 614

基于Whisper与FFmpeg的AI双语字幕生成:从原理到实战部署

语音识别(ASR)是人工智能在音频处理领域的核心应用,其基本原理是通过深度学习模型将语音信号转换为文本。以Transformer架构为代表的技术显著提升了识别准确率与多语言支持能力,为自动化字幕生成奠定了技术基础。这项技术的工程价值在于能够大幅降低字幕制作的人力与时间成本,提升多媒体内容的可访问性与传播效率。其应用场景广泛覆盖在线教育、视频本地化、会议记录及媒体内容生产等领域。本文聚焦于利用开源AI工具链实现视频实时语音识别与双语字幕自动生成,详细拆解了基于Whisper模型与FFmpeg的完整解决方案,涵

weixin_34174422的博客 528

【机器学习】一文搞懂 混合专家架构(MoE, Mixture of Experts)

这一设计基于以下观察:在足够大的模型规模下,单个专家的选择已能提供足够的模型容量,而避免多专家聚合带来的计算与通信开销。其关键特性包括:自适应专家容量(根据实时负载动态调整)、二维专家并行(数据并行与专家并行的混合)、以及All-to-All与All-Gather的融合优化。代码涵盖了细粒度MoE的维度切分、共享专家隔离的动态路由、专家管理系统的剪枝合并,以及Soft MoE等超越稀疏性的创新架构,提供了从理论到实践的完整技术路径。专家并行将专家网络分片至不同设备,门控网络与注意力层保持数据并行。

u012133341的博客 494

CUDA学习笔记(四)线程协作

CUDA线程协作 教材《GPU高性能编程CUDA实战》第五章 线程协作 一、并行线程块的分解 1.矢量求和:使用线程实现GPU上的矢量求和 //重新回顾矢量求和 : 一个线程块N个线程 #include "book.h" #include "cuda_runtime.h" #include "device_launch_parameters.h"//包含blockIdx.x #define N 10 __global__ void add( int *a, int *b, int *c ).

视觉小浩 518

CUDA By Example(四)——线程协作

主要关于CUDA中线程协作的问题,通过点积运算示例,说明了如何通过 __share__ 共享内存,以及如何通过 __syncthreads() 解决竞态问题

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

cuda编程-block和thread数量的确定

<br />第一,thread 和block数量的受到gpu本身hard limit的限制<br />第二,取决于目前shared memory and rigisters大小的限制,不能发布太多,否则系统会自动调用local memory,这样存取速度会下降,影响性能<br />第三,取决具体的应用,比如:你的矩阵有1000* 1000,如果你设计的kenel 每个线程处理一行的话,你发布1000现在就够了,或者其他的方法来确定。

打不死的心态活到老 6427

CUDA 学习(二十一)、优化策略6: 资源竞争

一、识别瓶颈         分析瓶颈工具:         (1)、Visual Profiler          对已实现的代码进行分析,我们首先可以考虑的就是SDK 中提供的那些分析工具。比如,NVIDIA Visual Profile。这是一款多平台的工具。它能指出在我们内核中哪些是错误的,并告诉我们该如何改进。         (2)、Parallel Nsight     

1422

《GPU高性能编程CUDA实战》学习笔记(八)

第8章 图形互操作性 GPU既执行渲染计算,又执行通用计算。。。 CUDA C应用可以无缝地与OpenGL和DirectX这两种实时渲染API进行交互。 本章中包含大量OpenGL和GLUT(OpenGL Utility Toolkit)代码,要有些思想准备。 8.1 本章目标 了解图形互操作性是什么,以及为什么需要使用它。了解如何设置某个CUDA设备的图形互操作性。了解如何在C

博客 5930
上一篇: 视觉SLAM十四讲 读书编程笔记 Chapter8 视觉里程计2
下一篇: CUDA by example Chapter6 常量内存与事件
Leo-Ma
博客等级 码龄11年 1701粉丝 124原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值