移行されたコードのデバッグ: ランタイムの動作#

CodePin 機能は実験的なものであり、将来のリリースで完成する予定です。

状況によって、移行された SYCL* プログラムの実行時の動作が元の CUDA* プログラムと異なることがあります。これには、以下のような原因が考えられます。

  • ハードウェア間の演算精度の違い

  • CUDA* API と SYCL* API のセマンティクスの違い

  • 自動移行中に発生したエラー

CodePin は、実行時の動作のこのような不一致のデバッグ労力を軽減するインテル® DPC++ 互換性ツールの機能です。CodePin が有効である場合、インテル® DPC++ 互換性ツールは CUDA* プログラムを SYCL* に移行しますが、CUDA* プログラムをインストルメントしたバージョンも生成します。

このインストルメント化されたコードは、選択された API またはカーネル呼び出しの前後の関連する変数のデータをレポートにダンプします。CUDA* プログラムと SYCL* プログラムから生成されたレポートを比較して、実行時の動作の相違となる原因を特定します。

CodePin を有効にする#

CodePin を有効にするには、–enable-codepin オプションを使用します。インストルメントされたプログラムは、dpct_output_codepin_cuda および dpct_output_codepin_sycl フォルダーに配置されます。

#

次の CUDA* コードの例には、vectorAdd カーネル呼び出しの前の cudaMemcpy() に問題があります。コピーされるサイズが vectorSize * sizeof(int3) ではなく vectorSize * 12 としてハードコードされているため、移行された SYCL* プログラムは正しく機能しません。これは、int3sycl::int3 に移行されますが、sycl::int3 のサイズが 12 バイトではなく 16 バイトであるためです。

//example.cu 
#include <iostream> 
__global__ void vectorAdd(int3 *a, int3 *result) { 
    int tid = blockIdx.x * blockDim.x + threadIdx.x; 
    result[tid].x = a[tid].x + 1; 
    result[tid].y = a[tid].y + 1; 
    result[tid].z = a[tid].z + 1; 
} 

int main() { 
    const int vectorSize = 4; 
    int3 h_a[vectorSize], h_result[vectorSize]; 
    int3 *d_a, *d_result; 
    for (int i = 0; i < vectorSize; ++i) 
        h_a[i] = make_int3(1, 2, 3); 

    cudaMalloc((void **)&d_a, vectorSize * sizeof(int3)); 
    cudaMalloc((void **)&d_result, vectorSize * sizeof(int3)); 

    // Copy host vectors to device 
    // !! Using 12 instead of "sizeof(int3)」 
    cudaMemcpy(d_a, h_a, vectorSize * 12, cudaMemcpyHostToDevice); 

    // Launch the CUDA kernel 
    vectorAdd<<<1, 4>>>(d_a, d_result); 

    // Copy result from device to host 
    cudaMemcpy(h_result, d_result, vectorSize * sizeof(int3), 
        cudaMemcpyDeviceToHost); 

    // Print the result 
    for (int i = 0; i < vectorSize; ++i) { 
        std::cout << "Result[" << i << "]: (「
             << h_result[i].x << ", " << h_result[i].y << ", " << h_result[i].z << ")\n"; 
    }
 }

 
/* 
Execution Result: 
    Result[0]: (2, 3, 4) 
    Result[1]: (2, 3, 4) 
    Result[2]: (2, 3, 4) 
    Result[3]: (2, 3, 4) 
*/

この問題をデバッグするには、CodePin を有効にして CUDA* プログラムを移行します。

dpct example.cu --enable-codepin

移行後、次の 2 つのファイルが作成されます。dpct_output_codepin_sycl/example.dp.cppdpct_output_codepin_cuda/example.cu

workspace
 ├── example.cu
 ├── dpct_output_codepin_sycl
 │     ├── example.dp.cpp
 │     ├── generated_schema.hpp
 │     └── MainSourceFiles.yaml
 ├── dpct_output_codepin_cuda
 │     ├── example.cu
 │     └── generated_schema.hpp

dpct_output_codepin_sycl/example.dp.cpp は移行され、インストルメントされた SYCL* プログラムです。

//dpct_output_codepin_sycl/example.dp.cpp 
#include <dpct/dpct.hpp> 
#include <sycl/sycl.hpp>

#include "generated_schema.hpp" 
#include <dpct/codepin/codepin.hpp> 
#include <iostream> 

void vectorAdd(sycl::int3 *a, sycl::int3 *result, 
            const sycl::nd_item<3> &item_ct1) { 
    int tid = item_ct1.get_group(2) * item_ct1.get_local_range(2) + 
                item_ct1.get_local_id(2); 
    result[tid].x() = a[tid].x() + 1; 
    result[tid].y() = a[tid].y() + 1; 
    result[tid].z() = a[tid].z() + 1; 
} 

int main() { 
    sycl::device dev_ct1; 
    sycl::queue q_ct1(dev_ct1, 
                        sycl::property_list{sycl::property::queue::in_order()}); 
    const int vectorSize = 4; 
    sycl::int3 h_a[vectorSize], h_result[vectorSize]; 
    sycl::int3 *d_a, *d_result; 
    for (int i = 0; i < vectorSize; ++i) 
        h_a[i] = sycl::int3(1, 2, 3); 

    d_a = sycl::malloc_device<sycl::int3>(vectorSize, q_ct1); 
    dpct::experimental::get_ptr_size_map()[*((void **)&d_a)] = 
        vectorSize * sizeof(sycl::int3); 

    d_result = sycl::malloc_device<sycl::int3>(vectorSize, q_ct1); dpct::experimental::get_ptr_size_map()[*((void **)&d_result)] = 
        vectorSize * sizeof(sycl::int3); 

    // Copy host vectors to device 
    q_ct1.memcpy(d_a, h_a, vectorSize * 12); 

    // Launch the CUDA kernel 
    dpct::experimental::gen_prolog_API_CP( 
        "example.cu:38:3(SYCL)", &q_ct1, VAR_SCHEMA_0, (long *)&d_a, VAR_SCHEMA_1, (long *)&d_result); 
    q_ct1.parallel_for( sycl::nd_range<3>(sycl::range<3>(1, 1, 4), 
        sycl::range<3>(1, 1, 4)), 
        [=](sycl::nd_item<3> item_ct1) { vectorAdd(d_a, d_result, item_ct1); }); 

    // Copy result from device to host 
    dpct::experimental::gen_epilog_API_CP( 
        "example.cu:38:3(SYCL)", &q_ct1, 
        VAR_SCHEMA_0, (long *)&d_a, VAR_SCHEMA_1, (long *)&d_result); 

    q_ct1.memcpy(h_result, d_result, vectorSize * sizeof(sycl::int3)).wait(); 

    // Print the result 
    for (int i = 0; i < vectorSize; ++i) { 
        std::cout << "Result[" << i << "]: (" << h_result[i].x() << ", " << 
                h_result[i].y() << ", " << h_result[i].z() << ")\n"; 
    } 
} 

/* Execution Result: 
    Result[0]: (2, 3, 4) 
    Result[1]: (2, 3, 4) 
    Result[2]: (2, 3, 4) 
    Result[3]: (1, 1, 1) <--- incorrect result 
*/

dpct_output_codepin_cuda/example.cu は移行され、インストルメントされた CUDA* プログラムです。

//dpct_output_codepin_cuda/example.cu 
#include "generated_schema.hpp" 
#include <dpct/codepin/codepin.hpp> 
#include <iostream>

__global__ void vectorAdd(int3 *a, int3 *result) {
   int tid = blockIdx.x * blockDim.x + threadIdx.x;
    result[tid].x = a[tid].x + 1;
    result[tid].y = a[tid].y + 1;
    result[tid].z = a[tid].z + 1; }
 
int main() { 
    const int vectorSize = 4;
     int3 h_a[vectorSize], h_result[vectorSize]; 
    int3 *d_a, *d_result; 
    for (int i = 0; i < vectorSize; ++i) 
        h_a[i] = make_int3(1, 2, 3); 

    cudaMalloc((void **)&d_a, vectorSize * sizeof(int3)); 
    dpct::experimental::get_ptr_size_map()[*((void **)&d_a)] = 
        vectorSize * sizeof(int3); 
    cudaMalloc((void **)&d_result, vectorSize * sizeof(int3)); 
    dpct::experimental::get_ptr_size_map()[*((void **)&d_result)] = 
        vectorSize * sizeof(int3); 

// Copy host vectors to device 
cudaMemcpy(d_a, h_a, vectorSize * 12, cudaMemcpyHostToDevice); 

    // Launch the CUDA kernel 
    dpct::experimental::gen_prolog_API_CP( 
        "example.cu:38:3", 0, VAR_SCHEMA_0, 
        (long *)&d_a, VAR_SCHEMA_1, (long *)&d_result); 
    vectorAdd<<<1, 4>>>(d_a, d_result); 

    // Copy result from device to host 
    dpct::experimental::gen_epilog_API_CP( 
        "example.cu:38:3", 0, VAR_SCHEMA_0, 
        (long *)&d_a, VAR_SCHEMA_1, (long *)&d_result); 
    cudaMemcpy(h_result, d_result, vectorSize * sizeof(int3), 
            cudaMemcpyDeviceToHost); 

    // Print the result 
    for (int i = 0; i < vectorSize; ++i) { 
        std::cout << "Result[" << i << "]: (「
                 << h_result[i].x << ", " << h_result[i].y << ", " << h_result[i].z << ")\n"; 
    }
 }

 /* 
Execution Result: 
    Result[0]: (2, 3, 4) 
    Result[1]: (2, 3, 4) 
    Result[2]: (2, 3, 4) 
    Result[3]: (2, 3, 4) 
*/

dpct_output_codepin_sycl/example.dp.cppdpct_output_codepin_cuda/example.cu をビルドし、ビルドされたバイナリーを実行すると、次の実行ログファイルが生成されます。

インストルメントされた CUDA* プログラムのレポート

インストルメントされた移行後の SYCL* プログラムのレポート

1[ 
2    { 
3       "ID": "example.cu:26:3:prolog", 
4       "Free Device Memory": "16374562816", 
5       "Total Device Memory": "16882663424", 
6       "Elapse Time(ms)": "0", 
7       "CheckPoint": { 
8          "d_a": { 
9             "Type": "Pointer", 
10           "Data": [ 
11               { 
12                   "Type": "int3", 
13                   "Data": [ 
14                       { 
15                          "x": { 
16                              "Type": "int", 
17                              "Data": [ 
18                                  1 
19                              ] 
20                          } 
21                       }, 
22                       { 
23                              "y": { 
24                                   "Type": "int", 
25                                   "Data": [ 
26                                   2 
27                                   ] 
28                              } 
29                       }, 
30 ...
1[ 
2    { 
3    "ID": "example.cu:26:3:prolog", 
4    "Free Device Memory": "0", 
5    "Total Device Memory": "31023112192", 
6    "Elapse Time(ms)": "0", 
7    "CheckPoint": { 
8       "d_a": { 
9          "Type": "Pointer", 
10        "Data": [ 
11           { 
12               "Type": "sycl::int3", 
13               "Data": [ 
14                  { 
15                     "x": { 
16                        "Type": "int", 
17                        "Data": [ 
18                           1 
19                        ] 
20                     } 
21                  }, 
22                  { 
23                      "y": { 
24                         "Type": "int", 
25                         "Data": [ 
26                         2 
27                      ] 
28                  } 
29              }, 
30 ...

このレポートは、CUDA* プログラムと SYCL* プログラムの実行時の動作がどこで相違するか特定するのに役立ちます。

CodePin の結果を解析する#

codepin-report.py (dpct/c2s –codepin-report でもトリガー可能) は、CUDA* と SYCL* コードの両方から実行ログファイルを入力し、自動解析を行う互換性ツールの機能です。codepin-report.py は、データ値の不一致を識別し、実行の統計データを報告できます。

ユーザーは、次の形式で浮動小数点の比較許容値を指定できます。

{ 
     "bf16_abs_tol": 9.77E-04,, 
     "fp16_abs_tol":      9.77E-04,, 
     "float_abs_tol": 1.19E-04,, 
     "double_abs_tol": 2.22E-04,, 
     "rel_tol": 1e-3 
}

最初の 4 つの項目 “bf16_abs_tol”、“bf16_abs_tol”、“bf16_abs_tol” および “bf16_abs_tol” は、対応するタイプの絶対許容範囲です。最後の “rel_tol” は、比率値で表される相対許容範囲です。

codepin-report.py は、次のコマンドラインで CUDA* および SYCL* コードの両方から生成された実行ログファイルを使用します。 codepin-report.py [-h] --instrumented-cuda-log <file path> --instrumented-sycl-log <file path> [--floating-point-comparison-epsilon <file path>]

以下は解析レポートの例です。

CodePin Summary 
Totally APIs count, 2 
Consistently APIs count, 2 
Most Time-consuming Kernel(CUDA), example.cu:26:3:epilog, time:8.2316 
Most Time-consuming Kernel(SYCL), example.cu:26:3:epilog, time:10.2575 
Peak Device Memory Used(CUDA), 508100608 
Peak Device Memory Used(SYCL), 31023112192 
CUDA Meta Data ID, SYCL Meta Data ID, Type, Detail 
example.cu:26:3:prolog,example.cu:26:3:prolog,Data value,[WARNING: METADATA MISMATCH] The pair of prolog data example.cu:26:3:prolog are mismatched, 
and the corresponding pair of epilog data matches. This mismatch may be caused by the initialized memory or argument used in the API example.cu.