コンパイル例
oneAPI アプリケーションは、ダイレクト・プログラミング、oneAPI ライブラリーを使用する API ベース、およびそれらを組み合わせて記述することができます。API ベースのプログラミングでは、ライブラリーの機能を使用してデバイスへのオフロードを行います。これにより、開発者はアプリケーションの開発時間を節約できます。一般に、API ベースのプログラミングから始め、ニーズに対応できない場合に SYCL* または OpenMP* オフロード機能を導入するのが最も容易であると考えられます。
次のセクションでは、API ベースのコードと SYCL* を使用したダイレクト・プログラミングの例を紹介します。
API ベースのコード
次のコードは、oneAPI マス・カーネル・ライブラリーの oneapi::mkl::blas::axpy 関数を使用して、浮動小数点数のベクトル間で a と x を乗算し、y を加算する API 呼び出し a * x + y) の使用法を示しています。このサンプルコードは、oneAPI プログラミング・モデルを利用して、アクセラレーターで加算を実行します。
#include <vector> // std::vector()
#include <cstdlib> // std::rand()
#include <CL/sycl.hpp>
#include "oneapi/mkl/blas.hpp"
int main(int argc, char* argv[]) {
double alpha = 2.0;
int n_elements = 1024;
int incx = 1;
std::vector<double> x;
x.resize(incx * n_elements);
for (int i=0; i<n_elements; i++)
x[i*incx] = 4.0 * double(std::rand()) / RAND_MAX - 2.0;
// -2.0 から 2.0 の間の rand 値
int incy = 3;
std::vector<double> y;
y.resize(incy * n_elements);
for (int i=0; i<n_elements; i++)
y[i*incy] = 4.0 * double(std::rand()) / RAND_MAX - 2.0;
// -2.0 から 2.0 の間の rand 値
cl::sycl::device my_dev;
try {
my_dev = cl::sycl::device(cl::sycl::gpu_selector());
} catch(...){
std::cout << "Warning, failed at selecting gpu device. Continuing on default(host) device.\n";
}
// 非同期例外をキャッチ
auto exception_handler = [] (cl::sycl::exception_list exceptions) {
for (std::exception_ptr const& e : exceptions) {
try {
std::rethrow_exception(e);
} catch(cl::sycl::exception const& e) {
std::cout << "Caught asynchronous SYCL exception:\n";
std::cout << e.what() << std::endl;
}
}
};
cl::sycl::queue my_queue(my_dev, exception_handler);
cl::sycl::buffer<double, 1> x_buffer(x.data(), x.size());
cl::sycl::buffer<double, 1> y_buffer(y.data(), y.size());
// y = alpha*x + y を実行
try {
oneapi::mkl::blas::axpy(my_queue, n_elements, alpha, x_buffer, incx, y_buffer, incy);
}
catch(cl::sycl::exception const& e) {
std::cout << "\t\tCaught synchronous SYCL exception:\n" << e.what() << std::endl;
}
std::cout << "The axpy (y = alpha * x + y) computation is complete!" << std::endl;
// y_buffer をプリント
auto y_accessor = y_buffer.template
get_access<cl::sycl::access::mode::read>();
std::cout << std::endl;
std::cout << "y" << " = [ " << y_accessor[0] << " ]\n";
std::cout << " [ " << y_accessor[1*incy] << " ]\n";
std::cout << " [ " << "... ]\n";
std::cout << std::endl;
return 0;
}アプリケーション (axpy.cpp として保存) をコンパイルするには、次の操作を行います:
MKLROOT 環境変数が適切に設定されていることを確認します (
echo ${MKLROOT})。正しく設定されていない場合、setvars.sh|oneapi-vars.sh(Linux*)、またはsetvars.bat|oneapi-vars.bat(Windows*) スクリプトを実行するか、libおよびincludeフォルダーを含むディレクトリー・パスを変数に設定します。setvarsとoneapi-varsスクリプトの詳細については、oneAPI 開発環境の設定を参照してください。次のコマンドでアプリケーションをビルドします:
Linux*:
icpx -fsycl -I${MKLROOT}/include -c axpy.cpp -o axpy.o
Windows*:
icpx -fsycl -I${MKLROOT}/include /EHsc -c axpy.cpp /Foaxpy.obj
次のコマンドでアプリケーションをリンクします:
Linux*:
icpx -fsycl axpy.o -fsycl-device-code-split=per_kernel \ "${MKLROOT}/lib/intel64"/libmkl_sycl.a -Wl,-export-dynamic -Wl,--start-group \ "${MKLROOT}/lib/intel64"/libmkl_intel_ilp64.a \ "${MKLROOT}/lib/intel64"/libmkl_sequential.a \ "${MKLROOT}/lib/intel64"/libmkl_core.a -Wl,--end-group -lsycl -lOpenCL \ -lpthread -lm -ldl -o axpy.out
Windows*:
icpx -fsycl axpy.obj -fsycl-device-code-split=per_kernel ^ "${MKLROOT}\lib\intel64"\mkl_sycl.lib ^ "${MKLROOT}\lib\intel64"\mkl_intel_ilp64.lib ^ "${MKLROOT}\lib\intel64"\mkl_sequential.lib ^ "${MKLROOT}\lib\intel64"\mkl_core.lib ^ sycl.lib OpenCL.lib -o axpy.exe
次のコマンドでアプリケーションを実行します:
Linux*:
./axpy.out
Windows*:
axpy.exe
ダイレクト・プログラミング
ここでは、ベクトル加算のサンプルコードを使用します。このサンプルコードは、oneAPI プログラミング・モデルを利用して、アクセラレーターで加算を実行します。
次のコマンドは、実行形式をコンパイルしてリンクします:
icpx -fsycl vector_add.cppコマンドとオプションのコンポーネントと関数は、「API ベースのコード」セクションで説明したものと類似しています。
このコマンドを実行すると、実行時にベクトルの加算を実行する実行ファイルが作成されます。