使用CUDA在GPU上进行图像处理的多线程
Multithreading for image processing at GPU using CUDA
问题声明:我必须连续处理从相机拍摄的800万像素图像。它必须有几种图像处理算法,如颜色插值、颜色变换等。这些操作在CPU上需要很长时间。所以,我决定使用CUDA内核在GPU上进行这些操作。我已经编写了一个用于颜色转换的CUDA内核。但我仍然需要在表现上得到更多的提升。
基本上有两个计算时间:
- 将
source image
从CPU复制到GPU,反之亦然 source image
在GPU上的处理
当图像从CPU复制到GPU时。。。。没有其他事情发生。同样,当GPU处理图像时。。。没有其他事情发生。
我的想法:我想进行多线程处理,这样可以节省一些时间。我想在GPU上处理上一张图像的同时捕捉下一张图像。而且,当GPU完成对上一个图像的处理时,下一个图像已经在那里,可以从CPU传输到GPU。
我需要什么:我对多线程世界完全陌生。我正在看一些教程和其他一些东西来了解更多关于它的信息。所以,我正在寻找一些关于正确步骤和正确逻辑的建议
我不确定您是否真的需要线程。CUDA能够允许主机和设备之间异步并发执行(无需使用多个CPU线程(。您所要求的是一个非常标准的"流水线"算法。它看起来像这样:
$ cat t832.cu
#include <stdio.h>
#define IMGSZ 8000000
// for this example, NUM_FRAMES must be less than 255
#define NUM_FRAMES 128
#define nTPB 256
#define nBLK 64
unsigned char cur_frame = 0;
unsigned char validated_frame = 0;
bool validate_image(unsigned char *img) {
validated_frame++;
for (int i = 0; i < IMGSZ; i++) if (img[i] != validated_frame) {printf("image validation failed at %d, was: %d, should be: %dn",i, img[i], validated_frame); return false;}
return true;
}
void CUDART_CB my_callback(cudaStream_t stream, cudaError_t status, void* data) {
validate_image((unsigned char *)data);
}
bool capture_image(unsigned char *img){
for (int i = 0; i < IMGSZ; i++) img[i] = cur_frame;
if (++cur_frame == NUM_FRAMES) {cur_frame--; return true;}
return false;
}
__global__ void img_proc_kernel(unsigned char *img){
int idx = threadIdx.x + blockDim.x*blockIdx.x;
while(idx < IMGSZ){
img[idx]++;
idx += gridDim.x*blockDim.x;}
}
int main(){
// setup
bool done = false;
unsigned char *h_imgA, *h_imgB, *d_imgA, *d_imgB;
size_t dsize = IMGSZ*sizeof(unsigned char);
cudaHostAlloc(&h_imgA, dsize, cudaHostAllocDefault);
cudaHostAlloc(&h_imgB, dsize, cudaHostAllocDefault);
cudaMalloc(&d_imgA, dsize);
cudaMalloc(&d_imgB, dsize);
cudaStream_t st1, st2;
cudaStreamCreate(&st1); cudaStreamCreate(&st2);
unsigned char *cur = h_imgA;
unsigned char *d_cur = d_imgA;
unsigned char *nxt = h_imgB;
unsigned char *d_nxt = d_imgB;
cudaStream_t *curst = &st1;
cudaStream_t *nxtst = &st2;
done = capture_image(cur); // grabs a frame and puts it in cur
// enter main loop
while (!done){
cudaMemcpyAsync(d_cur, cur, dsize, cudaMemcpyHostToDevice, *curst); // send frame to device
img_proc_kernel<<<nBLK, nTPB, 0, *curst>>>(d_cur); // process frame
cudaMemcpyAsync(cur, d_cur, dsize, cudaMemcpyDeviceToHost, *curst);
// insert a cuda stream callback here to copy the cur frame to output
cudaStreamAddCallback(*curst, &my_callback, (void *)cur, 0);
cudaStreamSynchronize(*nxtst); // prevent overrun
done = capture_image(nxt); // capture nxt image while GPU is processing cur
unsigned char *tmp = cur;
cur = nxt;
nxt = tmp; // ping - pong
tmp = d_cur;
d_cur = d_nxt;
d_nxt = tmp;
cudaStream_t *st_tmp = curst;
curst = nxtst;
nxtst = st_tmp;
}
}
$ nvcc -o t832 t832.cu
$ cuda-memcheck ./t832
========= CUDA-MEMCHECK
========= ERROR SUMMARY: 0 errors
$
有许多cuda示例代码可能也很有用,例如simpleStreams、asyncAPI和simpleCallbacks
由于你的问题很广泛,我只能想到以下建议:
1(使用CUDA流
当使用多个CUDA流时,CPU->GPU之间的内存传输、GPU处理和GPU->CPU之间的内存传递可能重叠。这样,下一个图像的图像处理就可以在结果被传回时开始。
还可以分解每个帧。每帧使用n
个流,并以偏移量启动图像处理内核n
次。
2(应用生产者-消费者方案
生产者线程从相机捕获帧,并将它们存储在线程安全容器中。使用者线程从该源容器获取帧,使用其自己的CUDA流将其上传到GPU,启动内核并将结果复制回主机。在尝试从源容器获取新映像之前,每个使用者线程都会与其流同步。
一个简单的实现可以是这样的:
#include <vector>
#include <thread>
#include <memory>
struct ThreadSafeContainer{ /*...*/ };
struct Producer
{
Producer(std::shared_ptr<ThreadSafeContainer> c) : container(c)
{
}
void run()
{
while(true)
{
// grab image from camera
// store image in container
}
}
std::shared_ptr<ThreadSafeContainer> container;
};
struct Consumer
{
Consumer(std::shared_ptr<ThreadSafeContainer> c) : container(c)
{
cudaStreamCreate(&stream);
}
~Consumer()
{
cudaStreamDestroy(stream);
}
void run()
{
while(true)
{
// read next image from container
// upload to GPU
cudaMemcpyAsync(...,...,...,stream);
// run kernel
kernel<<<..., ..., ..., stream>>>(...);
// copy results back
cudaMemcpyAsync(...,...,...,stream);
// wait for results
cudaStreamSynchronize(stream);
// do something with the results
}
}
std::shared_ptr<ThreadSafeContainer> container;
cudaStream_t stream; // or multiple streams per consumer
};
int main()
{
// create an instance of ThreadSafeContainer which whill be shared between Producer and Consumer instances
auto container = std::make_shared<ThreadSafeContainer>();
// create one instance of Producer, pass the shared container as an argument to the constructor
auto p = std::make_shared<Producer>(container);
// create a separate thread which executes Producer::run
std::thread producer_thread(&Producer::run, p);
const int consumer_count = 2;
std::vector<std::thread> consumer_threads;
std::vector<std::shared_ptr<Consumer>> consumers;
// create as many consumers as specified
for (int i=0; i<consumer_count;++i)
{
// create one instance of Consumer, pass the shared container as an argument to the constructor
auto c = std::make_shared<Consumer>(container);
// create a separate thread which executes Consumer::run
consumer_threads.push_back(std::thread(&Consumer::run, c));
}
// wait for the threads to finish, otherwise the program will just exit here and the threads will be killed
// in this example, the program will never exit since the infinite loop in the run() methods never end
producer_thread.join();
for (auto& t : consumer_threads)
{
t.join();
}
return 0;
}
- 通过安装信号处理程序关闭多线程应用程序
- 如何在c++多线程中匹配处理时间和接收时间
- 多线程处理中的静态成员变量
- Opencv cpp 使用多线程处理同一视频的不同部分
- 对象析构函数在多线程处理时不断被调用,但该对象并未超出范围
- 使用 wxWidgets 进行多线程处理时出现奇怪的行为
- 通过多线程处理确定每个字符在文件中出现的次数
- 多线程应用程序中的零MQ处理中断
- 使用多线程处理的异步请求
- 从单线程到多线程图像处理
- 多线程处理,同时保持部分序列
- 易失性信号处理程序和多线程
- 如何在类中进行 c++ 多线程处理(将线程引用保留为成员 var)
- SDL 带变量的多线程处理 -- 无法按预期工作
- 用Poco SocketReactor处理多线程的好方法是什么?
- New和delete处理多线程问题
- 多线程:如何在wxthread中使显示图像的GUI线程和处理图像同步的工作线程无阻塞
- 基于生产者-消费者的多线程图像处理
- 使用CUDA在GPU上进行图像处理的多线程
- 处理多线程清理的最佳方法