全体的な並列ループ処理
先ほど提示したParallelReduceSampleは、限られた範囲内で計算をするものでした。データの全体を加味しないとならないないような処理、総和の計算・最小値の検出・最大値の検出などをするには、parallel_reduce
テンプレートを使用します。
ParallelReduceSampleプロジェクトでparallel_reduce
を使用しているので見て下さい。
#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; }
主な処理の流れは次の通りです。
- 売上分析の対象となる商品(Sales:売上オブジェクト)を設定します。
- 売上明細情報(SalesDetails:売上明細オブジェクト)を生成します。
- インテルTBBを使用するためにtask_scheduler_initオブジェクトを初期化します。
- 直列(並列化しない)で売上明細データを分析します。またその処理時間を計測します。
- 後でエラーチェックをするために直列で算出したデータを退避します。
- 今度は並列化して売上明細データを分析します。またその処理時間を計測します。
- 直列処理の結果と並列処理の結果を比較してエラーチェックをします。
- 処理効率と分析結果を表示して終了します。
次項で並列処理部分について詳しく解説します。