Pipelining是一個通用並行模型,模仿一個傳統的製造集成生成線,數據流向一系列的管道濾波,每個濾波以某種方式處理數據,給定一個輸入流數據,一些濾波能並行操作,另外一些不能。例如,視頻處理,對幀的處理不依賴其他幀,那麼能同時處理多個幀,反之,對幀的操作需要首先處理先前的幀。
TBB類pipeline和filter實現pipeline模式。一個簡單的文本處理例子會被使用證明pipeline和filter的用法。這個例子讀取一個文本文件,平方這個文本里的每個數,寫出改變的文本到一個新的文件裏,流程如下:
這個濾波作用是提取文本中一行的數據:
class InputStream: public filter {
public:
explicit InputStream(ifstream* file):m_file(file), filter(serial_in_order) {} // filter初始化serial_in_order(串行)
~InputStream() {}
void* operator()(void*) {
ostringstream out;
vector<string> vec_str;
string str;
if(!getline(*m_file, str)) {
return NULL;
}
_split(str, ' ', vec_str);
// 把開闢內存,準備存放固定格式的數據
int length = vec_str.size();
char* result = new char[length*sizeof(int) + sizeof(int)];
vector<string>::iterator _it = vec_str.begin();
// 把數據轉成整形,存成固定格式
for (int i = 0; _it != vec_str.end(); ++_it, i++) {
int temp = atoi((*_it).c_str());
memcpy(result + sizeof(int) + i*sizeof(int), &temp, sizeof(int));
}
memcpy(result, &length, sizeof(int));
return result;
}
private:
ifstream* m_file;
};
這個濾波爲對獲取的數據進行平方操作:
class Transform: public filter {
public:
Transform():filter(parallel) {} // filter初始化parallel(並行)
~Transform() {}
void* operator()(void* in_str) {
if (!in_str) {
return NULL;
}
// 獲取數據長度,得到數據的首地址result
char* char_str = reinterpret_cast<char*>(in_str);
ostringstream out;
int* length = reinterpret_cast<int*>(char_str);
char* result = new char[(*length)*sizeof(int) + sizeof(int)];
// 遍歷獲取數據,並對數據進行平方
for (int i = 0; i < *length; i++) {
char* temp = (char_str + sizeof(int) + i*sizeof(int));
int temp_int = (*(reinterpret_cast<int*>(temp)))*2;
// 把數據存成固定格式,準備傳出去
memcpy(result + sizeof(int) + i*sizeof(int), &temp_int, sizeof(int));
}
memcpy(result, length, sizeof(int));
return result;
}
};
這個濾波的作用是輸出數據到文件中:
class OutputStream: public filter {
public:
explicit OutputStream(ofstream* file):m_file(file), filter(serial_in_order) {}
~OutputStream() {}
void* operator()(void* in_str) {
if (!in_str) {
return NULL;
}
char delim = ' ';
char cvtline = '\n';
// 獲取數據長度length
char* char_str = reinterpret_cast<char*>(in_str);
int* length = reinterpret_cast<int*>(char_str);
// 把數據存到文件中
for (int i = 0; i < *length; i++) {
int* int_str = reinterpret_cast<int*>(char_str + sizeof(int) + i*sizeof(int));
(*m_file) << *int_str;
(*m_file) << " ";
}
(*m_file) << "\n";
if (char_str) {
delete []char_str;
char_str = NULL;
}
return NULL;
}
private:
ofstream* m_file;
};
運行管道:
int run_pipeline(int nthreads) {
string input = "/home/shaomingliang/beh/core/tbb/pipline/input.txt";
string output = "/home/shaomingliang/beh/core/tbb/pipline/output.txt";
ifstream in_stream(input.c_str());
ofstream out_stream(output.c_str());
if (!in_stream.is_open()) {
throw invalid_argument(("invalid input file name: " + input).c_str());
}
if (!out_stream.is_open()) {
throw invalid_argument(("invalid output file name: " + output).c_str());
}
// 建立管道,加入InputStream濾波
pipeline pipeline;
InputStream input_filter(&in_stream);
pipeline.add_filter(input_filter);
// 加入Transform濾波
Transform trans_filter;
pipeline.add_filter(trans_filter);
// 加入OutputStream濾波
OutputStream out_filter(&out_stream);
pipeline.add_filter(out_filter);
// 開始計時並運行pipeline
tick_count start = tick_count::now();
pipeline.run(nthreads * 4);
ostringstream out;
out << "thread" << nthreads <<" consume time: " << (tick_count::now() - start).seconds() << endl;
in_stream.close();
out_stream.close();
return 1;
}
main函數:
int main(int argc, char** args) {
try {
// 獲取可利用的線程數
int p = task_scheduler_init::default_num_threads();
tick_count global_start = tick_count::now();
for (int i = 1; i <= p; i++) {
// 初始化task scheduler並初始化線程數爲i
task_scheduler_init init(i);
if (!run_pipeline(i)) {
return 0;
}
}
ostringstream out;
out << "global consume time: " << (tick_count::now() - global_start).seconds() << endl;
} catch(exception& e) {
cerr << "error ocurred. error text is:\"" << e.what() << "\"" << endl;
}
return 0;
}
字符串分割函數:
void _split(const string& s, char delim,vector<string>& v) {
int i = 0;
size_t pos = s.find(delim);
while (pos != string::npos) {
v.push_back(s.substr(i, pos-i));
i = ++pos;
pos = s.find(delim, pos);
if (pos == string::npos)
v.push_back(s.substr(i, s.length()));
}
}