oneMKL サンプルコード

SYCL* インターフェイスと oneMKL の一般的なワークフローを示すため、次のサンプルコードは、GPU デバイスで倍精度の行列 - 行列乗算を行います。

次のコード例では、行中のコメントで示されるように、コンパイルと実行に追加のコードが必要です。

// 標準 SYCL ヘッダー 
#include <CL/sycl.hpp> 
// STL クラス 
#include <exception> 
#include <iostream> 
// インテル® oneAPI マス・カーネル・ライブラリー SYCL/DPC++ API の宣言 
#include "oneapi/mkl.hpp" 
int main(int argc, char *argv[]) { 
    // 
    // m、n、k、ldA、ldB、ldC の設定とともに、A、B、C 行列のデータを取得     // 
    // この例では、A、B、C は最初に std::vector、または data() および size() メンバー関数 
    // を持つ同様のコンテナーに格納される必要があります。
     //
 
    // GPU デバイスを作成 
    sycl::device my_device; 
    try { 
        my_device = sycl::device(sycl::gpu_selector()); 
    } 
    catch (...) { 
        std::cout << "Warning: GPU device not found! Using default device instead."<< std::endl; } 
    // キューにアタッチされる非同期例外ハンドラーを作成     // 必須ではありません。システムが正しく構成されていない場合に役立つ情報を提供できます。 
    auto my_exception_handler = [](sycl::exception_list exceptions) { 
        for (std::exception_ptr const& e : exceptions) { 
            try { std::rethrow_exception(e); 
                } 
            catch (sycl::exception const& e) { 
                std::cout << "Caught asynchronous SYCL exception:\n 
                    << e.what() << std::endl; 
            } 
            catch (std::exception const& e) { 
                std::cout << "Caught asynchronous STL exception:\n 
                   << e.what() << std::endl; 
            } 
        } 
    }; 
    // 例外ハンドラーをアタッチした GPU デバイス上に実行キューを作成 
    sycl::queue my_queue(my_device, my_exception_handler); 
    // デバイスとホスト間のオフロード用の行列データの sycl バッファーを作成 
    sycl::buffer<double, 1> A_buffer(A.data(), A.size()); 
    sycl::buffer<double, 1> B_buffer(B.data(), B.size()); 
    sycl::buffer<double, 1> C_buffer(C.data(), C.size()); 
    // oneapi::mkl::blas::gemm を実行キューに追加し、同期例外をキャッチ 
    try { 
        using oneapi::mkl::blas::gemm; 
        using oneapi::mkl::transpose; 
        gemm(my_queue, transpose::nontrans, transpose::nontrans, m, n, k, alpha, A_buffer, ldA, B_buffer, 
            ldB, beta, C_buffer, ldC); 
    } 
    catch (sycl::exception const& e) { 
        std::cout << "\t\tCaught synchronous SYCL exception during GEMM:\n 
            << e.what() << std::endl; 
    } 
    catch (std::exception const& e) { 
        std::cout << "\t\tCaught synchronous STL exception during GEMM:\n 
            << e.what() << std::endl; 
    } 
    // キャッチされた非同期例外が処理されてから続行することを確認 
    my_queue.wait_and_throw(); 
    // 
    // 後処理結果 
    // 
    // C バッファーからデータにアクセスし、C 行列の一部をプリント 
    auto C_accessor = C_buffer.template get_access<sycl::access::mode::read>(); 
    std::cout << "\t" << C << " = [ 「 << C_accessor[0] << ", 「 
        << C_accessor[1] << ", ... ]\n"; 
    std::cout << "\t [ " << C_accessor[1 * ldC + 0] << ", 「 
        << C_accessor[1 * ldC + 1] << ", ... ]\n"; 
    std::cout << "\t [ " << "... ]\n"; 
    std::cout << std::endl; 

    return 0; 
}

(倍精度値) 行列 A (サイズ m x k)、B (サイズ k x n)、C (サイズ m x n) は、ホストマシンの配列 (次元 ldA、ldB、ldC) に格納されると想定します。スカラー (倍精度) alpha と beta を指定し、行列 - 行列乗算 (mkl::blas::gemm) を計算します:

C = alpha * A * B + beta * C

標準 SYCL* ヘッダーと対象の mkl::blas::gemm API 宣言を含む oneMKL SYCL/DPC++ 固有ヘッダーをインクルードします:

// 標準 SYCL ヘッダー 
#include <CL/sycl.hpp> 
// STL クラス 
#include <exception> #include <iostream> 
// インテル® oneAPI マス・カーネル・ライブラリー SYCL/DPC++ API の宣言  
#include "oneapi/mkl.hpp"

次に、通常のようにホストマシンで行列データをロードまたはインスタンス化し、GPU デバイスを作成して非同期例外ハンドラーを作成し、最後に例外ハンドラーでデバイスキューを作成します。ホストで発生する例外は、標準 C++ 例外メカニズムでキャッチできます。ただし、デバイスで発生する例外は非同期エラーとして見なされ、例外リストに保存されて、ユーザー定義の例外ハンドラーによって処理されます。

// GPU デバイスを作成 
sycl::device my_device; 
try { 
    my_device = sycl::device(sycl::gpu_selector()); 
} 
catch (...) { 
    std::cout << "Warning: GPU device not found! Using default device instead."<< std::endl; } 
// キューにアタッチされる非同期例外ハンドラーを作成// 必須ではありません。システムが正しく構成されていない場合に役立つ情報を提供できます 
auto my_exception_handler = [](sycl::exception_list exceptions) { 
    for (std::exception_ptr const& e : exceptions) { 
        try { 
            std::rethrow_exception(e); 
        } 
        catch (sycl::exception const& e) { 
            std::cout << "Caught asynchronous SYCL exception:\n 
                << e.what() << std::endl; 
        } 
        catch (std::exception const& e) { 
            std::cout << "Caught asynchronous STL exception:\n 
                << e.what() << std::endl; 
        } 
    } 
};

これで、行列データが SYCL バッファーにロードされ、ターゲットのデバイスにオフロードして、完了後にホストに戻すことができます。最後に、mkl::blas::gemm API がすべてのバッファー、サイズ、および転置操作で呼び出され、行列乗算カーネルとデータをターゲットにキューに追加します。

// 例外ハンドラーをアタッチした GPU デバイス上に実行キューを作成 
sycl::queue my_queue(my_device, my_exception_handler); 
// デバイスとホスト間のオフロード用の行列データの sycl バッファーを作成 
sycl::buffer<double, 1> A_buffer(A.data(), A.size()); 
sycl::buffer<double, 1> B_buffer(B.data(), B.size()); 
sycl::buffer<double, 1> C_buffer(C.data(), C.size()); 
// oneapi::mkl::blas::gemm を実行キューに追加し、同期例外をキャッチ 
try { 
    using oneapi::mkl::blas::gemm; 
    using oneapi::mkl::transpose; 
    gemm(my_queue, transpose::nontrans, transpose::nontrans, m, n, k, alpha, A_buffer, ldA, B_buffer, 
        ldB, beta, C_buffer, ldC); 
} 
catch (sycl::exception const& e) { 
    std::cout << "\t\tCaught synchronous SYCL exception during GEMM:\n" 
        << e.what() << std::endl; 
} 
catch (std::exception const& e) { 
    std::cout << "\t\tCaught synchronous STL exception during GEMM:\n" 
        << e.what() << std::endl; 
}

gemm カーネルがキューに登録された後に実行されます。キューは、すべてのカーネルの実行を待機し、キャッチされた非同期例外を例外ハンドラーに渡してスローするように要求します。ランタイムは、ホストと GPU デバイス間のバッファーのデータ転送を処理します。C_buffer のアクセサーが作成されるまでに、バッファーのデータはホストマシンに暗黙的に転送されます。この場合、アクセサーは 2 x 2 の C_buffer のサブ行列を出力するために使用されます。

// C バッファーからデータにアクセスし、C 行列の一部をプリント 
auto C_accessor = C_buffer.template get_access<sycl::access::mode::read>(); 
std::cout << "\t" << C << " = [ " << C_accessor[0] << ", 「 
    << C_accessor[1] << ", ... ]\n"; 
std::cout << "\t [ " << C_accessor[1 * ldC + 0] << ", " 
    << C_accessor[1 * ldC + 1] << ", ... ]\n"; 
std::cout << "\t [ " << "... ]\n"; 
std::cout << std::endl; 

return 0;

結果データは C_buffer オブジェクトにあり、明示的にどこかにコピーしない限り (元の C コンテナーに戻すなど)、C_buffer がスコープ外になるまでアクセサーを介してのみ利用できます。