Boost.ComputeまたはGPU / CPU並列コンピューティング。パート1

前書き



こんにちは、Habr!



私の基準では、私はかなり長い間C ++コードを書いてきましたが、それまでは、並列コンピューティングに関連するタスクにまだ遭遇していませんでした。Boost.Computeライブラリに関する記事は1つも見たことがないので、この記事で説明します。

すべての部品





コンテンツ



  • boost.computeとは
  • boost.computeをプロジェクトに接続する際の問題
  • boost.computeの概要
  • 基本的な計算クラス
  • 入門
  • 結論


boost.computeとは



このc ++ライブラリは、マルチコアCPUおよびGPUコンピューティングデバイスと対話するためのシンプルな高レベルインターフェイスを提供します。このライブラリは、バージョン1.61.0でブーストするために最初に追加され、現在もサポートされています。



boost.computeをプロジェクトに接続する際の問題



そのため、このライブラリの使用中にいくつかの問題が発生しました。それらの1つは、ライブラリがOpenCLなしでは機能しないということでした。コンパイラは次のエラーを出します。



画像



接続後、すべてが正しくコンパイルされるはずです。



Boostライブラリを犠牲にして、NuGetパッケージマネージャを使用してダウンロードしてVisualStudioプロジェクトに接続できます。



boost.computeの概要



必要なすべてのコンポーネントをインストールしたら、簡単なコードを確認できます。正しく動作させるには、次の方法で計算モジュールを有効にするだけで十分です。



#include <boost/compute.hpp>
using namespace boost;


通常のstlコンテナは、名前の計算アルゴリズムでの使用には適していないことに注意してください。代わりに、標準のコンテナと競合しない特別に作成されたコンテナがあります。サンプルコード:



std::vector<float> std_vector(10);
compute::vector<float> compute_vector(std_vector.begin(), std_vector.end(), queue); 
//       ,     .


copy()関数を使用して、std :: vector:に戻すことができます。



compute::copy(compute_vector.begin(), compute_vector.end(), std_vector.begin(), queue);


基本的な計算クラス



ライブラリには3つの補助クラスが含まれており、ビデオカードやプロセッサでの計算から始めるのに十分です。



  • 計算::デバイス(使用するデバイスを決定します)
  • 計算::コンテキスト(このクラスのオブジェクトは、メモリバッファーやその他のオブジェクトを含むOpenCLリソースを格納します)
  • compute :: command_queue(コンピューティングデバイスと対話するためのインターフェイスを提供します)


この全体を次のように宣言できます。



auto device = compute::system::default_device(); //     
auto context = compute::context::context(device); //   
auto queue = compute::command_queue(context, device); //   


上記のコードの最初の行を使用するだけでも、次のコードを実行することで、すべてが正常に機能することを確認できます。



std::cout << device.name() << std::endl; 


このようにして、計算を実行するデバイスの名前を取得しました。結果(何か違うものがあるかもしれません):



画像



入門



例によって、trasform()関数とreduce()関数を見てみましょう。



std::vector<float> host_vec = {1, 4, 9};

compute::vector<float> com_vec(host_vec.begin(), host_vec.end(), queue);
//           
//  copy()

compute::vector<float> buff_result(host_vec.size(), context);
transform(com_vec.begin(), com_vec.end(), buff_result.begin(), compute::sqrt<float>(), queue);

std::vector<float> transform_result(host_vec.size());
compute::copy(buff_result.begin(), buff_result.end(), transform_result.begin(), queue);
	
cout << "Transforming result: ";
for (size_t i = 0; i < transform_result.size(); i++)
{
	cout << transform_result[i] << " ";
}
cout << endl;

float reduce_result;
compute::reduce(com_vec.begin(), com_vec.end(), &reduce_result, compute::plus<float>(),queue);

cout << "Reducing result: " << reduce_result << endl;


上記のコードを実行すると、次の結果が表示さ



画像



れます。これらの2つの方法は、すべてが不要になることなく、並列計算での基本的な作業をうまく示しているため、私はこれら2つの方法に決めました。



したがって、transform()関数は、すべての値に1つの関数を適用することにより、データの配列(または、渡す場合は2つの配列)を変更するために使用されます。



transform(com_vec.begin(), 
   com_vec.end(), 
   buff_result.begin(), 
   compute::sqrt<float>(), 
   queue);


引数の解析に移りましょう。最初の2つの引数で入力データのベクトルを渡し、3番目の引数で結果を書き込むベクトルの先頭へのポインターを渡し、次の引数で何をする必要があるかを示します。上記の例では、標準のベクトル処理関数の1つを使用しています。これは、平方根を抽出することです。もちろん、カスタム関数を作成することもできます。boostは2つの方法を提供しますが、これはすでに次の部分の資料です(ある場合)。さて、最後の引数として、上記で説明したcompute :: command_queueクラスのオブジェクトを渡します。



次の関数はreduce()です。ここでは、すべてがもう少し興味深いものです。このメソッドは、ベクトルのすべての要素に4番目の引数を適用した結果を返します。



compute::reduce(com_vec.begin(), 
   com_vec.end(), 
   &reduce_result, 
   compute::plus<float>(),
   queue);


ここで例を挙げて説明します。上記のコードは次の式と比較できます。

1+4+ナイン

この場合、配列内のすべての要素の合計を取得します。



結論



さて、それだけです。ビッグデータに対して簡単な操作を実行するには、これで十分だと思います。これで、boost.computeライブラリの基本的な機能を使用できます。また、このライブラリを使用する際のエラーを防ぐこともできます。



ポジティブなフィードバックをいただければ幸いです。お時間をいただきありがとうございます。



皆さんお元気で!



All Articles