TBB之pipeline

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()));
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章