7

Boost.Compute を使い始めたばかりですが、どれだけの速度が得られるかを確認するために、簡単なプログラムを作成しました。

#include <iostream>
#include <vector>
#include <algorithm>
#include <boost/foreach.hpp>
#include <boost/compute/core.hpp>
#include <boost/compute/platform.hpp>
#include <boost/compute/algorithm.hpp>
#include <boost/compute/container/vector.hpp>
#include <boost/compute/functional/math.hpp>
#include <boost/compute/types/builtin.hpp>
#include <boost/compute/function.hpp>
#include <boost/chrono/include.hpp>

namespace compute = boost::compute;

int main()
{
    // generate random data on the host
    std::vector<float> host_vector(16000);
    std::generate(host_vector.begin(), host_vector.end(), rand);

    BOOST_FOREACH (auto const& platform, compute::system::platforms())
    {
        std::cout << "====================" << platform.name() << "====================\n";
        BOOST_FOREACH (auto const& device, platform.devices())
        {
            std::cout << "device: " << device.name() << std::endl;
            compute::context context(device);
            compute::command_queue queue(context, device);
            compute::vector<float> device_vector(host_vector.size(), context);

            // copy data from the host to the device
            compute::copy(
                host_vector.begin(), host_vector.end(), device_vector.begin(), queue
            );

            auto start = boost::chrono::high_resolution_clock::now();
            compute::transform(device_vector.begin(),
                       device_vector.end(),
                       device_vector.begin(),
                       compute::sqrt<float>(), queue);

            auto ans = compute::accumulate(device_vector.begin(), device_vector.end(), 0, queue);
            auto duration = boost::chrono::duration_cast<boost::chrono::milliseconds>(boost::chrono::high_resolution_clock::now() - start);
            std::cout << "ans: " << ans << std::endl;
            std::cout << "time: " << duration.count() << " ms" << std::endl;
            std::cout << "-------------------\n";
        }
    }
    std::cout << "====================plain====================\n";
    auto start = boost::chrono::high_resolution_clock::now();
    std::transform(host_vector.begin(),
                host_vector.end(),
                host_vector.begin(),
                [](float v){ return std::sqrt(v); });

    auto ans = std::accumulate(host_vector.begin(), host_vector.end(), 0);
    auto duration = boost::chrono::duration_cast<boost::chrono::milliseconds>(boost::chrono::high_resolution_clock::now() - start);
    std::cout << "ans: " << ans << std::endl;
    std::cout << "time: " << duration.count() << " ms" << std::endl;

    return 0;
}

そして、これが私のマシン(win7 64ビット)でのサンプル出力です:

====================Intel(R) OpenCL====================
device: Intel(R) Core(TM) i7-4770 CPU @ 3.40GHz
ans: 1931421
time: 64 ms
-------------------
device: Intel(R) HD Graphics 4600
ans: 1931421
time: 64 ms
-------------------
====================NVIDIA CUDA====================
device: Quadro K600
ans: 1931421
time: 4 ms
-------------------
====================plain====================
ans: 1931421
time: 0 ms

私の質問は: なぜプレーン (非 opencl) バージョンが速いのですか?

4

3 に答える 3

10

他の人が言っているように、単一のデータ セットに対して GPU で実行する価値があるほど、カーネルには十分な計算がない可能性が高いです (カーネルのコンパイル時間と GPU への転送時間によって制限されています)。

より良いパフォーマンス数値を得るには、アルゴリズムを複数回実行する必要があります (カーネルをコンパイルして保存する時間が含まれるため、最初のアルゴリズムははるかに長くなるため、最初のアルゴリズムを破棄する可能性が高くなります)。

transform()また、独立した操作として実行する代わりに、単一のカーネルで変換と縮小の両方を実行accumulate()する融合アルゴリズムを使用する必要があります。transform_reduce()コードは次のようになります。

float ans = 0;
compute::transform_reduce(
    device_vector.begin(),
    device_vector.end(),
    &ans,
    compute::sqrt<float>(),
    compute::plus<float>(),
    queue
);
std::cout << "ans: " << ans << std::endl;

-DBOOST_COMPUTE_USE_OFFLINE_CACHEオフライン カーネル キャッシュを有効にするBoost.Compute を使用してコードをコンパイルすることもできます(これには とのリンクが必要boost_filesystemです)。次に、使用するカーネルがファイル システムに保存され、アプリケーションを初めて実行するときにのみコンパイルされます (Linux の NVIDIA は、既定でこれを既に実行しています)。

于 2014-06-19T00:43:53.097 に答える
2

大きな違いの理由の 1 つが考えられます。CPU と GPU のデータ フローを比較します。

CPU              GPU

                 copy data to GPU

                 set up compute code

calculate sqrt   calculate sqrt

sum              sum

                 copy data from GPU

これを考えると、Intel チップは一般的な計算では少しゴミのように見えます.NVidia はおそらく余分なデータのコピーと計算を行うための GPU のセットアップに苦しんでいます.

同じプログラムを試してみる必要がありますが、はるかに複雑な操作を使用してください。sqrt と sum は単純すぎて、GPU を使用することによる余分なオーバーヘッドを克服できません。たとえば、マンドルブロ点を計算してみることができます。

あなたの例では、ラムダを蓄積に移動する方が高速になります(メモリ上の1パスと2パス)

于 2014-06-18T08:47:51.727 に答える
1

時間を間違って測定しているため、悪い結果が得られています。

OpenCL デバイスには、ホスト カウンターとは関係のない独自のタイム カウンターがあります。すべての OpenCL タスクには 4 つの状態、クエリ可能なタイマーがあります: ( Khronos の Web サイトから)

  1. CL_PROFILING_COMMAND_QUEUED、イベントによって識別されるコマンドがホストによってコマンドキューにエンキューされたとき
  2. CL_PROFILING_COMMAND_SUBMIT、キューに入れられたイベントによって識別されるコマンドが、コマンドキューに関連付けられたデバイスにホストによって送信されたとき。
  3. CL_PROFILING_COMMAND_START、イベントによって識別されるコマンドがデバイスで実行を開始したとき。
  4. CL_PROFILING_COMMAND_END、イベントによって識別されるコマンドがデバイスでの実行を終了したとき。

タイマーはDevice-sideであることを考慮してください。したがって、カーネルとコマンド キューのパフォーマンスを測定するために、これらのタイマーをクエリできます。あなたの場合、2 つの最後のタイマーが必要です。

サンプル コードでは、データ転送時間 ( Skizzが言ったように) に加えて、コマンド キューのメンテナンスに費やされたすべての時間を含むホスト時間を測定しています。

したがって、実際のカーネルのパフォーマンスを知るには、cl_event をカーネルに渡し (boost::compute でそれを行う方法がわからない)、そのイベントにパフォーマンス カウンターを問い合わせるか、すべてのオーバーヘッドを隠すためにカーネルを非常に巨大で複雑にする必要があります。

于 2014-06-18T15:21:52.150 に答える