インテル® oneMKL BLAS および LAPACK と DPC++ の紹介#

このガイドでは、SYCL* のデータ並列 C++ (DPC++) 実装用のインテル® oneMKL BLAS (基本線形代数サブプログラム) および LAPACK (線形代数パッケージ) アプリケーション・プログラミング・インターフェイスの概要を説明します。これは、標準の BLAS および LAPACK API の使用経験があるユーザーを対象としています。

一般に、BLAS および LAPACK の DPC++ API は標準の BLAS および LAPACK API と似ており、同じルーチン名と引数の順序を持っています。ただし、標準ルーチンとは異なり、DPC++ ルーチンは計算デバイス (CPU または GPU) 上で非同期的に実行されるように設計されており、通常は入力と出力にデバイスメモリーを使用します。この機能をサポートするため、多くの引数のデータタイプが変更され、各ルーチンはルーチンを実行する場所を指定する追加の引数 (DPC++ キュー) を受け取ります。以下に API の変更を示します。

oneMKL では、すべての DPC++ ルーチンと関連するデータタイプは oneapi::mkl 名前空間に属します。CPU ベースの oneMKL ルーチンは、C インターフェイス (グローバル名前空間を使用) で引き続き利用できます。さらに、各 BLAS 固有のルーチンは、oneapi::mkl::blasoneapi::mkl::blas::column_major、および oneapi::mkl::blas::row_major 名前空間にあります。各 LAPACK 固有のルーチンは、oneapi::mkl::lapack 名前空間にあります。現在、LAPACK DPC++ API は、行優先レイアウトで保存された行列をサポートしていません。

デフォルトでは、oneapi::mkl::blas 名前空間内のすべての BLAS 関数に対して列優先レイアウトが想定されています。oneapi::mkl::blas::column_major 名前空間の BLAS 関数は、行列が列優先レイアウトで保存されている場合にも使用できます。行優先レイアウトを使用するには、oneapi::mkl::blas::row_major 名前空間の BLAS 関数を使用する必要があります。たとえば、oneapi::mkl::blas::gemm は、列優先レイアウトを使用する行列乗算の DPC++ ルーチンですが、::{cblas_}{s, d, c, z}gemm は従来の CPU ベースのバージョンです。

標準 BLAS/LAPACK と DPC++ oneMKL API の違い#

名前#

DPC++ BLAS および LAPACK API は精度に関してオーバーロードされます。たとえば、標準の BLAS API には、精度に基づいて名前が付けられた GEMM 計算用の 4 つの異なるルーチン (sgemmdgemmcgemmzgemm) がありますが、DPC++ BLAS には、floatdoublehalfbfloat16std::complex<float>、および std::complex<double> データタイプを受け入れる gemm という名前の GEMM 計算用のエントリーポイントが 1 つだけあります。

関連情報#

すべての DPC++ オブジェクト (バッファとキュー) は、ポインターではなく参照によって渡されます。その他のパラメーターは通常、値で渡されます。

キュー#

すべての DPC++ BLAS および LAPACK ルーチンには、先頭に特別なパラメーターがあります: 計算タスクが送信される DPC++ キュー (タイプ queue&)。キューは、CPU デバイスまたは GPU デバイスに関連付けることができます。すべての BLAS 関数で CPU および GPU デバイスがサポートされています。サポートされているデバイスを確認するには、個々の LAPACK 関数のドキュメントを参照してください。

ベクトルと行列タイプ#

DPC++ には、デバイスにデータを保存し、デバイスとホスト間でデータを共有する 2 つの API (バッファー API と統合共有メモリー (USM) API) があります。DPC++ BLAS および LAPACK ルーチンは両方の API をサポートします。

バッファー API を使用すると、DPC++ BLAS および LAPACK ルーチンへのベクトルおよび行列入力は DPC++ バッファータイプになります。現在、すべてのバッファーは 1 次元である必要がありますが、DPC++ の buffer::reinterpret() メンバー関数を使用して、高次元のバッファーを 1 次元のバッファーに変換できます。

USM API の場合、DPC++ BLAS および LAPACK ルーチンへのベクトルおよび行列入力は適切なタイプのポインターですが、ポインターは DPC++ USM 割り当てルーチン (malloc_hostmalloc_sharedmalloc_device など) のいずれかで割り当てられたメモリーを指す必要があります。通常の malloc または new ルーチンで割り当てられたメモリーは、インテル® oneMKL DPC++ インターフェイスでは使用できません。

たとえば、gemv ルーチンは行列 A とベクトル xy を受け取ります。実数倍精度の場合、これらの各パラメーターには次のタイプがあります。

  • 標準 BLAS では double* です。

  • バッファー API を使用した DPC++ BLAS では、buffer<double,1>&

  • USM API を使用した DPC++ BLAS の double* では、ポインターが参照するメモリーは、DPC++ USM 割り当てルーチンを使用してデバイスがアクセス可能な方法で割り当てられる必要があるという制限があります。

スカラー#

すべての BLAS 関数では、スカラー入力は値によって渡されます。

複素数#

DPC++ では、複素数は C++ std::complex タイプで表されます。たとえば、MKL_Complex8std::complex<float> に置き換えることができます。

これは、スカラー、ベクトル、および行列の引数に当てはまります。たとえば、倍精度複素数ベクトルのタイプは buffer<std::complex<double>,1> になります。

戻り値#

一部の BLAS および LAPACK ルーチン (dotnrm2asumiamax) は、戻り値としてスカラーの結果を返します。DPC++ では、非同期計算をサポートするため、これらのルーチンは引数リストの最後にある追加の引数を受け取ります。計算が完了すると、結果の値は、このバッファー (バッファー API の場合) またはポインター (USM API の場合) に格納されます。これらのルーチンは、他の DPC++ ルーチンと同様に、バッファー API の場合は void、USM API の場合は sycl::event の戻り値のタイプを持ちます。

計算オプション (文字パラメーター)#

標準の BLAS および LAPACK は、行列の転置、対称行列と三角行列の格納などの操作を制御するため特殊なアルファベット文字を使用します。DPC++ では、タイプの安全性を高めるため、これらの特殊文字はスコープ付き列挙タイプに置き換えられます。

たとえば、BLAS 行列ベクトル乗算 dgemv は文字引数 trans を受け取ります。これは N または T のいずれかで、乗算前に入力行列 A を転置するかどうかを指定します。

DPC++ では、trans はスコープ付き enum タイプ oneapi::mkl::transpose のメンバーです。従来の文字ベースの名前 oneapi::mkl::transpose::N および oneapi::mkl::transpose::T、または同等の説明的な名前 oneapi::mkl::transpose::nontrans および oneapi::mkl::transpose::trans を使用できます。

新しいタイプの詳細については、データタイプを参照してください。

行列レイアウト (行優先と列優先)#

標準の BLAS および LAPACK API では、行列に Fortran レイアウト (列優先) が必要です。行列はメモリー内に列ごとに格納され、各列のエントリーは連続したメモリー位置に格納されます。デフォルトでは、DPC++ 用の oneMKL も同様にこの行列レイアウトを想定します。BLAS の行優先レイアウトには、oneapi::mkl::blas::row_major 名前空間を使用する必要があります。行優先レイアウトは LAPACK では直接サポートされていませんが、行優先入力行列を転置し、必要な DPC++ LAPACK ルーチンを呼び出して、出力行列を行優先レイアウトに戻すことができます。

BLAS の例#

以下は、標準 BLAS dgemm を呼び出すプログラムの抜粋です:

double *A = …, *B = …, *C = …; 
double alpha = 2.0, beta = 3.0; 

int m = 16, n = 20, k = 24; 
int lda = m, ldb = n, ldc = m; 

dgemm("N", "T", &m, &n, &k, &alpha, A, &lda, B, &ldb, &beta, C, &ldc);

DPC++ 相当のコードは次のようになります。

using namespace sycl; 
using namespace oneapi::mkl; 
queue Q(…); 
buffer A = …, B = …, C = …; 
int m = 16, n = 20, k = 24; 
int lda = m, ldb = n, ldc = m; 
blas::gemm(Q, transpose::N, transpose::T, m, n, k, 2.0, A, lda, B, ldb, 3.0, C, ldc);