CodeZine(コードジン)

特集ページ一覧

インテルTBBから学ぶループの並列化

インテルTBBを通じて学ぶ並列処理(2)

  • LINEで送る
  • このエントリーをはてなブックマークに追加
2009/11/11 14:00
目次

全体的な並列ループ処理

 先ほど提示したParallelReduceSampleは、限られた範囲内で計算をするものでした。データの全体を加味しないとならないないような処理、総和の計算・最小値の検出・最大値の検出などをするには、parallel_reduceテンプレートを使用します。

 ParallelReduceSampleプロジェクトでparallel_reduceを使用しているので見て下さい。

ParallelReduceSample
#include <time.h>   
#include <limits.h>
#include <iostream>
#include<iomanip>
#include <vector>
#include <windows.h>
#include <winnt.h>
#include <tchar.h>
#include "tbb/parallel_reduce.h"
#include "tbb/blocked_range.h"
#include "tbb/task_scheduler_init.h"
#include "tbb/tick_count.h"

using namespace std;
using namespace tbb;

/*----------売上明細----------*/
struct SalesDetails {
private:
    int count; //数量
    double consumption_tax; //消費税額
    double amount; //合計金額
    double total; //売上+税金
public:
    //コンストラクタ
    SalesDetails() 
        : count( 0 ), consumption_tax( 0 ), amount( 0 ), total( 0 ) {};

    //セッター
    void set_count( int count ) { this->count = count; };
    void set_consumption_tax( double tax ) { this->consumption_tax = tax; };
    void set_amount( double amount ) { this->amount = amount; };
    void set_total( double total ) { this->total = total; };

    //ゲッター
    int get_count() const { return this->count; };
    double get_consumption_tax() const { return this->consumption_tax; };
    double get_amount() const { return this->amount; };
    double get_total() const { return this->total; };
};


/*----------売上----------*/
struct Sales {
private:
    string name; //名前
    int price; //単価
    SalesDetails*  datas; //売上明細データ
    int count; //明細データの数
    double sumAmount; //売上合計
    double sumTax; //税金合計
    int sumCount; //売上数合計
    int maxCount; //最大売上数
    int maxIndex; //最大売上数をもつ明細のインデックス
    int minCount; //最小売上数
    int minIndex; //最小売上数をもつ明細のインデックス
    double aveCount; //平均売上数
    double aveAmount; //平均売上金額
public:
    //コンストラクタ
    Sales( ) 
        : name(), price( 0 ), 
        datas(), count( 0 ), 
        sumAmount( 0 ), sumTax( 0 ), sumCount( 0 ),
        maxCount( INT_MIN ), maxIndex( -1 ), minCount( INT_MAX ), minIndex( -1 ),
        aveCount( 0 ), aveAmount( 0 ) {};
    Sales( const string name, int price ) 
        : name( name ), price( price ), 
        datas(), count( 0 ), 
        sumAmount( 0 ), sumTax( 0 ), sumCount( 0 ),
        maxCount( INT_MIN ), maxIndex( -1 ), minCount( INT_MAX ), minIndex( -1 ),
        aveCount( 0 ), aveAmount( 0 ) {};
    
    //セッター
    void set_price( int price ) { this->price = price; };
    void set_name ( string name ) { this->name = name; };
    void set_details( SalesDetails datas[] ) { this->datas = datas; };
    void set_detailCount( int count ) { this->count = count; };
    void set_sum( double value ) { this->sumAmount = value; };
    void set_sumTax( double value ) { this->sumTax = value; };
    void set_countSum( int value ) { this->sumCount = value; };
    void set_maxCount( int count ) { this->maxCount = count; };
    void set_maxIndex( int index ) { this->maxIndex = index; };
    void set_minCount( int count ) { this->minCount = count; };
    void set_minIndex( int index ) { this->minIndex = index; };
    void set_aveCount( double value ) { this->aveCount = value; };
    void set_aveAmount( double value ) { this->aveAmount = value; };
    
    //ゲッター
    int get_price() const { return this->price; };
    string get_name() const { return this->name; };
    SalesDetails* get_details() { return this->datas; };
    int get_detailCount() { return this->count; };
    double get_sum() const { return this->sumAmount; };
    double get_sumTax() const { return this->sumTax; };
    int get_countSum() const { return this->sumCount; };
    int get_maxCount() const { return this->maxCount; };
    int get_maxIndex() const { return this->maxIndex; };
    int get_minCount() const { return this->minCount; };
    int get_minIndex() const { return this->minIndex; };
    double get_aveCount() const { return this->aveCount; };
    double get_aveAmount() const { return this->aveAmount; };

    /*
        売上明細の各種計算項目を算出します
    */
    void Calculate( SalesDetails* datas, int count ) 
    {
        for ( int i = 0; i < count; i++ ) {
            datas[ i ].set_amount( this->price * datas[ i ].get_count() );
            datas[ i ].set_consumption_tax( static_cast<int>( datas[ i ].get_amount() * 0.05 ) );
            datas[ i ].set_total( datas[ i ].get_amount() + datas[ i ].get_consumption_tax() );
        }
        this->datas = datas;
        this->count = count;
    }

    /*
        各種数値の平均値を算出します
    */
    void Average() 
    {
        this->aveAmount = static_cast<float>(this->sumAmount) / this->count;
        this->aveCount = static_cast<float>(this->sumCount) / this->count;
    }

    /*
        分析値を算出します
    */
    void Analysis( SalesDetails* datas, int count ) 
    {
        for ( int i = 0; i < count; i++ ) {
            //合計を算出
            this->sumAmount += datas[ i ].get_amount();
            this->sumTax += datas[ i ].get_consumption_tax();
            this->sumCount += datas[ i ].get_count();
            //最大値もしくは最小値を決定
            if ( this->maxCount < datas[ i ].get_count() ) {
                this->maxCount = datas[ i ].get_count();
                this->maxIndex = i;
            } else if ( this->minCount > datas[ i ].get_count() ) {
                this->minCount = datas[ i ].get_count();
                this->minIndex = i;
            }
        }
        this->datas = datas;
        this->count = count;
        Average();
    }

    /*-----------------------------------------------------------------
        並列的に分析値を算出するための設定です
    ----------------------------------------------------------------*/
    //分割コンストラクター
    Sales( Sales& obj, split ) : 
        name( obj.get_name() ), price( obj.get_price() ), 
        datas( obj.get_details() ), count( obj.get_detailCount() ), 
        sumAmount( 0 ), sumTax( 0 ), sumCount( 0 ),
        maxCount( INT_MIN ), maxIndex( -1 ), minCount( INT_MAX ), minIndex( -1 ), 
        aveCount( 0 ), aveAmount( 0 ){};
    //計算結果を結合するためのメソッド
    void join( const Sales& obj ) { 
        //合計を算出
        this->sumAmount += obj.get_sum(); 
        this->sumTax += obj.get_sumTax();
        this->sumCount += obj.get_countSum();
        //最大値と最小値を決定
        if ( this->maxCount < obj.get_maxCount() ) {
            this->maxCount = obj.get_maxCount();
            this->maxIndex = obj.get_maxIndex();
        }
        if ( this->minCount > obj.get_minCount() ) {
            this->minCount = obj.get_minCount();
            this->minIndex = obj.get_minIndex();
        }
    };
    //範囲内で各種計算を行います
    void operator() ( const blocked_range<size_t>& range ) 
    {
        SalesDetails* tmp = this->datas;
        for ( size_t i = range.begin(); i != range.end(); i++ ) {
            //合計を算出
            this->sumAmount += tmp[ i ].get_amount();
            this->sumTax += tmp[ i ].get_consumption_tax();
            this->sumCount += tmp[ i ].get_count();
            //最大値もしくは最小値を決定
            if ( this->maxCount < tmp[ i ].get_count() ) {
                this->maxCount = tmp[ i ].get_count();
                this->maxIndex = i;
            } else if ( this->minCount > tmp[ i ].get_count() ) {
                this->minCount = tmp[ i ].get_count();
                this->minIndex = i;
            }
        }
    };
};


/*----------売上情報を解析するオブジェクト----------*/
class Analyzer {
public:

    //売上明細で算出するべき項目を直列で計算します。
    void Calculate( Sales* target, SalesDetails* datas, int count ) 
    {
        target->Calculate( datas, count );
    };

    /* 
        【直列で】分析値を算出します
    */
    void Analysis( Sales* target, SalesDetails* datas, int count ) 
    {
        target->Analysis( datas, count );
    }

    /* 
        【並列に】分析値を算出します
    */
    Sales ParallelSum( Sales* target, SalesDetails* datas, int count ) 
    {
        Sales obj( target->get_name(), target->get_price() );
        obj.set_details( datas );
        obj.set_detailCount( count );
        parallel_reduce( blocked_range<size_t>( 0, count, 100000 ), obj );
        obj.Average(); //並列計算では誤差が出るので直列に計算
        return obj;
    };
};

int _tmain(void)
{
    //ロケールを設定してコンソールで日本語表示ができるようにする
    _tsetlocale( LC_ALL, _T("") ); 

    //ランダムに数を生成するための準備
    srand( static_cast<unsigned int> ( time( NULL ) ) );

    //売上分析の対象となる商品を設定
    string name = "yakitori";
    int price = 100;
    Sales* target = new Sales( name, price );
    cout << "これから";
    copy( name.begin(), name.end(), ostream_iterator<char>( cout ) );
    cout << "(" << target->get_price() << "円)の売上を分析します・・・" << endl;

    //売上明細情報を生成
    const int count = 1000000;
    SalesDetails* datas = new SalesDetails[ count ];
    for ( int i = 0; i < count; i++ ) {
        SalesDetails data;
        int number = static_cast<int>( ( rand() % 100 ) + 1 );
        data.set_count( number );
        datas[ i ] = data; 
    }

    //各種変数を初期化
    task_scheduler_init init;
    Analyzer* analyzer = new Analyzer();
    analyzer->Calculate( target, datas, count ); 

    //直列で解析
    tick_count start = tick_count::now();
    analyzer->Analysis( target, datas, count );
    tick_count end = tick_count::now();
    double second = ( end - start ).seconds();
    cout << "直列で計算した場合" <<  second << "秒かかりました。" << endl;

    //後にエラーチェックするために直列処理の計算結果を退避
    Sales* old = new Sales( name, price );
    old->set_details( datas );
    old->set_countSum( target->get_countSum() );
    old->set_sum( target->get_sum() );
    old->set_sumTax( target->get_sumTax() );
    old->set_maxCount( target->get_maxCount() );
    old->set_maxIndex( target->get_maxIndex() );
    old->set_minCount( target->get_minCount() );
    old->set_minIndex( target->get_minIndex() );
    old->set_aveCount( target->get_aveCount() );
    old->set_aveAmount( target->get_aveAmount() );

    //並列で解析
    start = tick_count::now();
    Sales result = analyzer->ParallelSum( target, datas, count );
    end = tick_count::now();
    double paralleSecond = (end - start ).seconds();
    cout << "並列で計算した場合" << paralleSecond << "秒かかりました。" << endl;
    
    //エラーチェック
    int error = 0;
    if ( result.get_countSum() != old->get_countSum() ) error = 1;
    if ( result.get_sum() != old->get_sum() ) error = 1;
    if ( result.get_sumTax() != old->get_sumTax() ) error = 1;
    if ( result.get_maxCount() != old->get_maxCount() ) error = 1;
    if ( result.get_maxIndex() != old->get_maxIndex() ) error = 1;
    if ( result.get_minCount() != old->get_minCount() ) error = 1;
    if ( result.get_minIndex() != old->get_minIndex() ) error = 1;
    if ( result.get_aveCount() != old->get_aveCount() ) error = 1;
    if ( result.get_aveAmount() != old->get_aveAmount() ) error = 1;
    if ( error == 1 ) {
        cerr << "直列処理と並列処理の値が一致しません。";
        cout << endl;
        return 1;
    }

    //終了処理
    cout << setprecision(12);
    cout << "処理効率は" << ( second / paralleSecond ) << "倍です。" << endl;
    cout << "総売上数:" << result.get_countSum() << endl;
    cout << "売上合計:" << result.get_sum() << endl;
    cout << "税金合計:" << result.get_sumTax() << endl;
    cout << "最大売上数:" << result.get_maxCount() << endl;
    cout << "最大売上数をもつ明細:" << result.get_maxIndex() << endl;
    cout << "最小売上数:" << result.get_minCount() << endl;
    cout << "最大売上数をもつ明細:" << result.get_minIndex() << endl;
    cout << "明細データの数:" << result.get_detailCount() << "件" << endl;
    cout << "平均売上数:" << result.get_aveCount() << endl;
    cout << "平均売上金額:" << result.get_aveAmount() <<endl;
    cout << endl << endl << endl;
    delete analyzer;
    delete target;
    return 0;
}

 主な処理の流れは次の通りです。

  1. 売上分析の対象となる商品(Sales:売上オブジェクト)を設定します。
  2. 売上明細情報(SalesDetails:売上明細オブジェクト)を生成します。
  3. インテルTBBを使用するためにtask_scheduler_initオブジェクトを初期化します。
  4. 直列(並列化しない)で売上明細データを分析します。またその処理時間を計測します。
  5. 後でエラーチェックをするために直列で算出したデータを退避します。
  6. 今度は並列化して売上明細データを分析します。またその処理時間を計測します。
  7. 直列処理の結果と並列処理の結果を比較してエラーチェックをします。
  8. 処理効率と分析結果を表示して終了します。

 次項で並列処理部分について詳しく解説します。


  • LINEで送る
  • このエントリーをはてなブックマークに追加

あなたにオススメ

著者プロフィール

  • インドリ(インドリ)

    分析・設計・実装なんでもありのフリーエンジニア。 ブログ「無差別に技術をついばむ鳥(http://indori.blog32.fc2.com/)」の作者です。 アドバイザーをしたり、システム開発したり、情報処理技術を研究したりと色々しています。 座右の銘は温故知新で、新旧関係なく必要だと考えた...

バックナンバー

連載:インテルTBBを通じて学ぶ並列処理
All contents copyright © 2005-2021 Shoeisha Co., Ltd. All rights reserved. ver.1.5