次の方法で共有


アクセラレータオブジェクトとaccelerator_viewオブジェクトの使用

アクセラレータクラスとaccelerator_viewクラスを使用して、C++ AMP コードを実行するデバイスまたはエミュレーターを指定できます。 システムには、メモリの量、共有メモリのサポート、デバッグのサポート、または倍精度サポートによって異なる複数のデバイスまたはエミュレーターが存在する場合があります。 C++ Accelerated Massive Parallelism (C++ AMP) には、使用可能なアクセラレータの確認、既定値の設定、parallel_for_eachへの複数の呼び出しに対する複数のaccelerator_viewsの指定、特別なデバッグ タスクの実行に使用できる API が用意されています。

C++ AMP ヘッダーは、Visual Studio 2022 バージョン 17.0 以降では非推奨です。 AMP ヘッダーを含めると、ビルド エラーが発生します。 警告をサイレント状態にするには、AMP ヘッダーを含める前に _SILENCE_AMP_DEPRECATION_WARNINGS を定義します。

既定のアクセラレータの使用

C++ AMP ランタイムは、特定のアクセラレータを選択するコードを記述しない限り、既定のアクセラレータを選択します。 ランタイムは、既定のアクセラレータを次のように選択します。

  1. アプリがデバッグ モードで実行されている場合は、デバッグをサポートするアクセラレータ。

  2. それ以外の場合は、 CPPAMP_DEFAULT_ACCELERATOR 環境変数で指定されたアクセラレータ (設定されている場合)。

  3. それ以外の場合は、エミュレートされていないデバイス。

  4. それ以外の場合は、使用可能なメモリの量が最も多いデバイス。

  5. それ以外の場合は、ディスプレイに接続されていないデバイス。

さらに、ランタイムは既定のアクセラレータに対してaccess_typeaccess_type_autoを指定します。 つまり、既定のアクセラレータで共有メモリがサポートされていて、そのパフォーマンス特性 (帯域幅と待機時間) が専用 (非共有) メモリと同じであることがわかっている場合は、共有メモリが使用されます。

既定のアクセラレータを構築し、そのプロパティを調べることで、既定のアクセラレータのプロパティを確認できます。 次のコード例では、既定のアクセラレータのパス、アクセラレータ メモリの量、共有メモリのサポート、倍精度サポート、および制限付き倍精度サポートを出力します。

void default_properties() {
    accelerator default_acc;
    std::wcout << default_acc.device_path << "\n";
    std::wcout << default_acc.dedicated_memory << "\n";
    std::wcout << (accs[i].supports_cpu_shared_memory ?
        "CPU shared memory: true" : "CPU shared memory: false") << "\n";
    std::wcout << (accs[i].supports_double_precision ?
        "double precision: true" : "double precision: false") << "\n";
    std::wcout << (accs[i].supports_limited_double_precision ?
        "limited double precision: true" : "limited double precision: false") << "\n";
}

CPPAMP_DEFAULT_ACCELERATOR環境変数

CPPAMP_DEFAULT_ACCELERATOR環境変数を設定して、既定のアクセラレータのaccelerator::device_pathを指定できます。 パスはハードウェアに依存します。 次のコードでは、 accelerator::get_all 関数を使用して使用可能なアクセラレータの一覧を取得し、各アクセラレータのパスと特性を表示します。

void list_all_accelerators()
{
    std::vector<accelerator> accs = accelerator::get_all();

    for (int i = 0; i <accs.size(); i++) {
        std::wcout << accs[i].device_path << "\n";
        std::wcout << accs[i].dedicated_memory << "\n";
        std::wcout << (accs[i].supports_cpu_shared_memory ?
            "CPU shared memory: true" : "CPU shared memory: false") << "\n";
        std::wcout << (accs[i].supports_double_precision ?
            "double precision: true" : "double precision: false") << "\n";
        std::wcout << (accs[i].supports_limited_double_precision ?
            "limited double precision: true" : "limited double precision: false") << "\n";
    }
}

アクセラレータの選択

アクセラレータを選択するには、 accelerator::get_all メソッドを使用して使用可能なアクセラレータの一覧を取得し、そのプロパティに基づいてアクセラレータを選択します。 この例では、メモリが最も多いアクセラレータを選択する方法を示します。

void pick_with_most_memory()
{
    std::vector<accelerator> accs = accelerator::get_all();
    accelerator acc_chosen = accs[0];

    for (int i = 0; i <accs.size(); i++) {
        if (accs[i].dedicated_memory> acc_chosen.dedicated_memory) {
            acc_chosen = accs[i];
        }
    }

    std::wcout << "The accelerator with the most memory is "
        << acc_chosen.device_path << "\n"
        << acc_chosen.dedicated_memory << ".\n";
}

accelerator::get_allによって返されるアクセラレータの 1 つは、CPU アクセラレータです。 CPU アクセラレータでコードを実行することはできません。 CPU アクセラレータを除外するには、によって返されるアクセラレータのaccelerator::get_all プロパティの値と、アクセラレータの値::cpu_acceleratorを比較します。 詳細については、この記事の「特別なアクセラレータ」セクションを参照してください。

共有メモリ

共有メモリは、CPU とアクセラレータの両方からアクセスできるメモリです。 共有メモリを使用すると、CPU とアクセラレータの間でデータをコピーするオーバーヘッドが排除または大幅に削減されます。 メモリは共有されますが、CPU とアクセラレータの両方で同時にアクセスすることはできません。これにより、未定義の動作が発生します。 アクセラレータ プロパティsupports_cpu_shared_memoryは、アクセラレータが共有メモリをサポートしている場合にtrueを返し、default_cpu_access_type プロパティは、に割り当てられたメモリの既定のaccelerator (に関連付けられたacceleratorarray_viewでアクセスされるオブジェクトacceleratorなど) を取得します。

C++ AMP ランタイムは、access_typeごとに最適な既定のacceleratorを自動的に選択しますが、CPU からの読み取り、CPU からの書き込み、またはその両方の場合、共有メモリのパフォーマンス特性 (帯域幅と待機時間) が専用 (非共有) アクセラレータ メモリよりも悪くなる可能性があります。 共有メモリが CPU からの読み取りと書き込みに専用メモリと同様に実行される場合、ランタイムは既定で access_type_read_write に設定されます。それ以外の場合、ランタイムは、より保守的な既定の access_typeを選択し、計算カーネルのメモリ アクセス パターンが別の access_typeの恩恵を受ける場合に、アプリがそれをオーバーライドできるようにします。

次のコード例は、既定のアクセラレータが共有メモリをサポートしているかどうかを判断し、その既定のアクセスの種類をオーバーライドし、そこから accelerator_view を作成する方法を示しています。

#include <amp.h>
#include <iostream>

using namespace Concurrency;

int main()
{
    accelerator acc = accelerator(accelerator::default_accelerator);

    // Early out if the default accelerator doesn't support shared memory.
    if (!acc.supports_cpu_shared_memory)
    {
        std::cout << "The default accelerator does not support shared memory" << std::endl;
        return 1;
    }

    // Override the default CPU access type.
    acc.set_default_cpu_access_type(access_type_read_write);

    // Create an accelerator_view from the default accelerator. The
    // accelerator_view reflects the default_cpu_access_type of the
    // accelerator it's associated with.
    accelerator_view acc_v = acc.default_view;
}

accelerator_viewは、関連付けられているdefault_cpu_access_typeacceleratorを常に反映し、そのaccess_typeをオーバーライドまたは変更するためのインターフェイスを提供しません。

既定のアクセラレータの変更

既定のアクセラレータを変更するには、 accelerator::set_default メソッドを呼び出します。 既定のアクセラレータは、アプリの実行ごとに 1 回だけ変更でき、GPU でコードを実行する前に変更する必要があります。 後続の関数呼び出しでアクセラレータの戻り値を変更しようとするとfalseを返します。 parallel_for_eachの呼び出しで別のアクセラレータを使用する場合は、この記事の「複数のアクセラレータの使用」セクションを参照してください。 次のコード例では、既定のアクセラレータを、エミュレートされていない、ディスプレイに接続されていない、倍精度をサポートするものに設定します。

bool pick_accelerator()
{
    std::vector<accelerator> accs = accelerator::get_all();
    accelerator chosen_one;

    auto result = std::find_if(accs.begin(), accs.end(),
        [] (const accelerator& acc) {
            return !acc.is_emulated &&
                acc.supports_double_precision &&
                !acc.has_display;
        });

    if (result != accs.end()) {
        chosen_one = *(result);
    }

    std::wcout <<chosen_one.description <<std::endl;
    bool success = accelerator::set_default(chosen_one.device_path);
    return success;
}

複数のアクセラレータの使用

アプリで複数のアクセラレータを使用するには、次の 2 つの方法があります。

  • accelerator_viewオブジェクトは、parallel_for_each メソッドの呼び出しに渡すことができます。

  • 特定の オブジェクトを使用してaccelerator_viewオブジェクトを構築できます。 C++ AMP ランタイムは、ラムダ式でキャプチャされたaccelerator_viewオブジェクトから オブジェクトを取得します。

特殊アクセラレータ

3 つの特殊なアクセラレータのデバイス パスは、 accelerator クラスのプロパティとして使用できます。

  • accelerator::direct3d_ref データメンバー: このシングルスレッド アクセラレータは、CPU 上のソフトウェアを使用して汎用グラフィックス カードをエミュレートします。 既定ではデバッグに使用されますが、ハードウェア アクセラレータよりも低速であるため、運用環境では役に立ちません。 さらに、DirectX SDK と Windows SDK でのみ使用でき、顧客のコンピューターにインストールされる可能性は低いです。 詳細については、「デバッグ GPU コード」を参照してください。

  • accelerator::direct3d_warp データ メンバー: このアクセラレータは、ストリーミング SIMD 拡張機能 (SSE) を使用するマルチコア CPU で C++ AMP コードを実行するためのフォールバックソリューションを提供します。

  • accelerator::cpu_accelerator データ メンバー: このアクセラレータを使用してステージング配列を設定できます。 C++ AMP コードを実行できません。 詳細については、ネイティブ コードでの並列プログラミングに 関するブログの C++ AMP でのステージング配列 の投稿を参照してください。

Interoperability

C++ AMP ランタイムは、 accelerator_view クラスと Direct3D ID3D11Device インターフェイス間の相互運用性をサポートします。 create_accelerator_view メソッドは、IUnknown インターフェイスを受け取り、accelerator_view オブジェクトを返します。 get_device メソッドは、accelerator_view オブジェクトを受け取り、IUnknown インターフェイスを返します。

こちらも参照ください

C++ AMP (C++ Accelerated Massive Parallelism)
GPU コードのデバッグ
accelerator_view クラス