异步CPU读取和GPU+CPU计算
Asynchronous CPU reading and GPU+CPU calculations
我的程序首先从 HDD 读取 2 个图像(使用 c++ file.read
函数),然后在 GPU 和 CPU 上执行计算(一堆 CUDA 内核与简单的 CPU 计算)与这些图像。我有大约 2000 对图像要处理。阅读和计算时间几乎相等。读取和处理并行化有没有比较简单的实现?
我知道,通过 CUDA 流,我可以相对于主机 (CPU) 异步计算内核,但我的计算是复杂的。那么,是否可以使用某种 CPU 多线程?
我要的是:
readfromHDD(im-1);
readfromHDD(im-2);
for(int i=3;i<1998;i=i+2){
readfromHDD(im-i); | functions inside the
readfromHDD(im-(i+1)); | for loop are evaluated
ProcessGPU&CPU(im-(i-2),im-(i-1)); | concurrently
Synchronize_Reading_and_processing;
end
我认为,没有必要 post 我的实际代码。我以前从未做过多线程,所以我不知道它如何与 CUDA 内核一起工作。
任何提示表示赞赏。
谢谢
可能有数千种不同的可能解决方案。这是我要开始的,看看它是如何工作的:
成分
- 一个读线程,
- 某种消息队列
- 一个处理线程
方法:
启动读取线程和处理线程。
读取线程一次读取两个图像并将它们作为一个包发送到消息队列中。重复直到读取所有图像。
处理线程读取消息队列并处理两个图像。重复直到处理完所有图像。
停止线程并报告结果(如适用)
为消息队列提供一些 "backpressure" 可能会有所帮助,这样当您已经加载了 4、6 或 10 张图像时,reader 线程会停止读取图像,直到有 space 再次在队列中。
以这种方式使用消息队列的好处是线程之间有合理的自由度,消息队列安排了线程之间的所有同步。
我非常偏爱 pthreads 并在 reader 之上实现异步包装器,当您请求下一组数据时它会同步。
这是我能想到的最容易实现的方法。我已经包含了一些应该易于编译和完整演示实现的东西。
祝你好运。
main.cpp演示使用
#include "Reader.h"
#include "Reader_Async_Wrapper.h"
using namespace std;
int main() {
Reader *reader = new Reader("test");
Reader_Async_Wrapper async_reader(reader);
int img_index=0;
char* data;
data = async_reader.get_data();
while(((int*)data)[0]!=-1){
cout<<"processing image "<<img_index<<endl;
sleep(2);
cout<<"processed image "<<img_index++<<endl;
delete[] data;
data = async_reader.get_data();
}
return 0;
}
Reader.h 是一个简单的串行实现文件 i/o class
#include <iostream>
#include <fstream>
#include <unistd.h>
using namespace std;
class Reader{
public:
bool isFinished(){return finished;}
Reader(string file_name){
open_file(file_name);
finished=false;
img_index=0;
}
char* read_data(){
cout<<"Reading img: "<<img_index<<endl;
sleep(1);
cout<<"Read img: "<<img_index++<<endl;
if(img_index==10)finished=true;
return new char[1000];
}
private:
bool finished;
int img_index;
void open_file(string name){
// TODO
}
};
Reader_Async_Wrapper.h 是 Reader.h 的简单包装器,可以使其 运行 异步
#include "Reader.h"
#include <pthread.h>
using namespace std;
class Reader_Async_Wrapper{
public:
pthread_t thread;
pthread_attr_t attr;
Reader* reader;
pthread_barrier_t barrier;
Reader_Async_Wrapper(Reader* reader):reader(reader){
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
pthread_barrier_init (&barrier, NULL, 2);
pthread_create(&thread, &attr, &Reader_Async_Wrapper::threadHelper, this);
finished=false;
image_data=NULL;
}
void finish(){
pthread_attr_destroy(&attr);
void *status;
pthread_join(thread, &status);
}
char* get_data(){
pthread_barrier_wait (&barrier);
return image_data;
}
void clear_buffer(char* old_image){
delete[] old_image;
}
private:
char* image_data;
static void *threadHelper(void * contx){
return ((Reader_Async_Wrapper *)contx)->async_loop();
}
bool finished;
void *async_loop(){
while(!finished){
if(reader->isFinished()){
finished=true;
image_data=new char[sizeof(int)];
((int*)image_data)[0]=-1;
}else
image_data=reader->read_data();
pthread_barrier_wait(&barrier);
}
pthread_exit(NULL);
return NULL;
}
};
我建议改进与检测文件结尾相关的处理(假设您正在读取单个长文件)。否则我认为您可以轻松地将其扩展到您的应用程序。
只要您不打算同时处理许多情况,并且您主要将此方法用作隐藏与读取文件相关的延迟的方法,此方法就足够了。
如果你想同时处理很多情况,你可以使用包装器来包装文件的读取和处理。关于CUDA,我相信他们应该共享一个CUDA context。
如果您希望能够在 GPU 上并行处理,我会推荐以下几点:
创建包装器 class 的多个副本,每个副本对应一个您想要的并行实例。
在 class 构造函数中为每个异步实例分配一次足够的内存。
为每个线程指定一个 GPU 线程,以便内核可以 运行 并行。
在 GPU 线程上执行所有内存复制和内核执行。
我的程序首先从 HDD 读取 2 个图像(使用 c++ file.read
函数),然后在 GPU 和 CPU 上执行计算(一堆 CUDA 内核与简单的 CPU 计算)与这些图像。我有大约 2000 对图像要处理。阅读和计算时间几乎相等。读取和处理并行化有没有比较简单的实现?
我知道,通过 CUDA 流,我可以相对于主机 (CPU) 异步计算内核,但我的计算是复杂的。那么,是否可以使用某种 CPU 多线程?
我要的是:
readfromHDD(im-1);
readfromHDD(im-2);
for(int i=3;i<1998;i=i+2){
readfromHDD(im-i); | functions inside the
readfromHDD(im-(i+1)); | for loop are evaluated
ProcessGPU&CPU(im-(i-2),im-(i-1)); | concurrently
Synchronize_Reading_and_processing;
end
我认为,没有必要 post 我的实际代码。我以前从未做过多线程,所以我不知道它如何与 CUDA 内核一起工作。 任何提示表示赞赏。
谢谢
可能有数千种不同的可能解决方案。这是我要开始的,看看它是如何工作的:
成分
- 一个读线程,
- 某种消息队列
- 一个处理线程
方法:
启动读取线程和处理线程。
读取线程一次读取两个图像并将它们作为一个包发送到消息队列中。重复直到读取所有图像。
处理线程读取消息队列并处理两个图像。重复直到处理完所有图像。
停止线程并报告结果(如适用)
为消息队列提供一些 "backpressure" 可能会有所帮助,这样当您已经加载了 4、6 或 10 张图像时,reader 线程会停止读取图像,直到有 space 再次在队列中。
以这种方式使用消息队列的好处是线程之间有合理的自由度,消息队列安排了线程之间的所有同步。
我非常偏爱 pthreads 并在 reader 之上实现异步包装器,当您请求下一组数据时它会同步。
这是我能想到的最容易实现的方法。我已经包含了一些应该易于编译和完整演示实现的东西。 祝你好运。
main.cpp演示使用
#include "Reader.h"
#include "Reader_Async_Wrapper.h"
using namespace std;
int main() {
Reader *reader = new Reader("test");
Reader_Async_Wrapper async_reader(reader);
int img_index=0;
char* data;
data = async_reader.get_data();
while(((int*)data)[0]!=-1){
cout<<"processing image "<<img_index<<endl;
sleep(2);
cout<<"processed image "<<img_index++<<endl;
delete[] data;
data = async_reader.get_data();
}
return 0;
}
Reader.h 是一个简单的串行实现文件 i/o class
#include <iostream>
#include <fstream>
#include <unistd.h>
using namespace std;
class Reader{
public:
bool isFinished(){return finished;}
Reader(string file_name){
open_file(file_name);
finished=false;
img_index=0;
}
char* read_data(){
cout<<"Reading img: "<<img_index<<endl;
sleep(1);
cout<<"Read img: "<<img_index++<<endl;
if(img_index==10)finished=true;
return new char[1000];
}
private:
bool finished;
int img_index;
void open_file(string name){
// TODO
}
};
Reader_Async_Wrapper.h 是 Reader.h 的简单包装器,可以使其 运行 异步
#include "Reader.h"
#include <pthread.h>
using namespace std;
class Reader_Async_Wrapper{
public:
pthread_t thread;
pthread_attr_t attr;
Reader* reader;
pthread_barrier_t barrier;
Reader_Async_Wrapper(Reader* reader):reader(reader){
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
pthread_barrier_init (&barrier, NULL, 2);
pthread_create(&thread, &attr, &Reader_Async_Wrapper::threadHelper, this);
finished=false;
image_data=NULL;
}
void finish(){
pthread_attr_destroy(&attr);
void *status;
pthread_join(thread, &status);
}
char* get_data(){
pthread_barrier_wait (&barrier);
return image_data;
}
void clear_buffer(char* old_image){
delete[] old_image;
}
private:
char* image_data;
static void *threadHelper(void * contx){
return ((Reader_Async_Wrapper *)contx)->async_loop();
}
bool finished;
void *async_loop(){
while(!finished){
if(reader->isFinished()){
finished=true;
image_data=new char[sizeof(int)];
((int*)image_data)[0]=-1;
}else
image_data=reader->read_data();
pthread_barrier_wait(&barrier);
}
pthread_exit(NULL);
return NULL;
}
};
我建议改进与检测文件结尾相关的处理(假设您正在读取单个长文件)。否则我认为您可以轻松地将其扩展到您的应用程序。
只要您不打算同时处理许多情况,并且您主要将此方法用作隐藏与读取文件相关的延迟的方法,此方法就足够了。
如果你想同时处理很多情况,你可以使用包装器来包装文件的读取和处理。关于CUDA,我相信他们应该共享一个CUDA context。
如果您希望能够在 GPU 上并行处理,我会推荐以下几点: 创建包装器 class 的多个副本,每个副本对应一个您想要的并行实例。 在 class 构造函数中为每个异步实例分配一次足够的内存。 为每个线程指定一个 GPU 线程,以便内核可以 运行 并行。 在 GPU 线程上执行所有内存复制和内核执行。