インテル® Xeon Phi™ プロセッサーへのオフロード・オーバー・ファブリック: チュートリアル

同カテゴリーの次の記事

ベクトル化によるパフォーマンスの向上

この記事は、インテル® デベロッパー・ゾーンに公開されている「Offload over Fabric to Intel® Xeon Phi™ Processor: Tutorial」の日本語参考訳です。


この記事の PDF 版はこちらからご利用になれます。

インテル® C++ コンパイラーでサポートされる OpenMP* 4.0 のデバイス構文により、PCI Express* (PCIe*) を介してインテル® Xeon® プロセッサー・ベースのホストマシンからインテル® Xeon Phi™ コプロセッサーへワークロードをオフロードできます。オフロード・オーバー・ファイブリック (OoF) は、第 2 世代インテル® Xeon Phi™ プロセッサーをサポートするように、このオフロード・プログラミング・モデルを拡張します。つまり、OoF を利用することで、インテル® Xeon® プロセッサー・ベースのホストマシンは、インテル® Omni-Path アーキテクチャー (インテル® OPA) や Mellanox* InfiniBand* などの高速ネットワークを介して第 2 世代インテル® Xeon Phi™ プロセッサーへワークロードをオフロードできます。

このチュートリアルでは、OoF ソフトウェアのインストール、ハードウェアの設定、基本設定のテスト、OoF の有効化について説明します。サンプル・ソースコードを使用して OoF の仕組みを解説します。

ハードウェアの準備

このチュートリアルでは、ホストマシンとしてインテル® Xeon® プロセッサー E5-2670 (2.60GHz) ベースのサーバーと、ターゲットとしてインテル® Xeon Phi™ プロセッサー・ベースのサーバーの 2 台のマシンを使用します。どちらのマシンにも Red Hat* Enterprise Linux* 7.2 とリモートログイン用にギガビット・イーサネットが搭載されています。ホストマシンとターゲットマシンのホスト名は、それぞれ host-deviceknl-sb2 です。

最初に、高速ネットワークのセットアップを行う必要があります。ここでは、たまたま手元にあった InfiniBand* を使用していますが、代わりにインテル® OPA を使用することもできます。

作業を開始する前に、ホストマシンとターゲットマシン間の高速ネットワークをセットアップするため、それぞれのマシンの電源をオフにします。それぞれのマシンの PCIe* スロットに Mellanox* ConnectX*-3 VPI InfiniBand* アダプターを装着し、ルーターを介さずに InfiniBand* ケーブルで直接接続します。マシンを再起動したら、まずホストに Mellanox* ネットワーク・アダプターが装着されていることを確認します。

[host-device]$ lspci | grep Mellanox
84:00.0 Network controller: Mellanox Technologies MT27500 Family [ConnectX-3]

ターゲットでも同様の確認を行います。

[knl-sb2 ~]$ lspci | grep Mellanox
01:00.0 Network controller: Mellanox Technologies MT27500 Family [ConnectX-3]

ソフトウェアのインストール

ホストマシンとターゲットマシンの両方に Red Hat* Enterprise Linux* 7.2 が搭載されています。ホストマシンで Linux* カーネルのバージョンを確認します。

[host-device]$ uname -a
Linux host-device 3.10.0-327.el7.x86_64 #1 SMP Thu Oct 29 17:29:29 EDT 2015 x86_64 x86_64 x86_64 GNU/Linux

ターゲットでも現在の OS のカーネルバージョンを確認します。

[knl-sb2 ~]$ uname -a
Linux knl-sb2 3.10.0-327.el7.x86_64 #1 SMP Thu Oct 29 17:29:29 EDT 2015 x86_64 x86_64 x86_64 GNU/Linux

ホストマシンでは、OoF を有効にするため、こちらから最新の OoF ソフトウェアを入手してインストールします。このチュートリアルでは、Red Hat* Enterprise Linux* 7.2 用の Offload Over Fabric Software 1.4.0 (xppsl-1.4.0-offload-host-rhel7.2.tar) をインストールします。インストールの詳細は、『インテル® Xeon Phi™ プロセッサー x200 製品ファミリー向けオフロード・オーバー・ファブリック・ユーザーズ・ガイド』 (英語) を参照してください。また、OoF サポート (インテル® コンパイラーによって提供されるオフロード・プログラミング・モデルのサポート) を有効にするため、インテル® Parallel Studio XE 2017 もホストにインストールします。

ターゲットマシンでは、こちらから最新のインテル® Xeon Phi™ Processor Software を入手してインストールします。このチュートリアルでは、Red Hat* Enterprise Linux* 7.2 用のインテル® Xeon Phi™ Processor Software (xppsl-1.4.0-rhel7.2.tar) をインストールします。インストールの詳細は、『インテル® Xeon Phi™ Processor Software ユーザーズガイド』 (英語) を参照してください。

ホストマシンとターゲットマシン間の InfiniBand* ネットワークをセットアップするため、それぞれのマシンに Red Hat* Enterprise Linux* 7.2 用の Mellanox* OpenFabrics Enterprise Distribution (OFED) for Linux* ドライバー (MLNX_OFED_LINUX 3.2-2) をインストールします。このドライバーは、www.mellanox.com (英語) から入手できます ([Products] > [Software] > [InfiniBand/VPI Drivers] にアクセスして、Mellanox OFED Linux を選択します)。

ハードウェアの基本設定のテスト

ホストマシンとターゲットマシンに Mellanox* ドライバーをインストールしたら、Mellanox* InfiniBand* Host Channel Adapters (HCA) が適切に動作していることを確認するためネットワーク・カードをテストします。InfiniBand* ネットワークを起動し、ibping コマンドを実行してネットワーク・リンクをテストします。

最初に、ホストで InfiniBand* とサブネット・マネージャーを起動し、リンク情報を表示します。

[knl-sb2 ~]$ sudo service openibd start
Loading HCA driver and Access Layer:                       [  OK  ]

[knl-sb2 ~]$ sudo service opensm start
Redirecting to /bin/systemctl start  opensm.service

[knl-sb2 ~]$ iblinkinfo
CA: host-device HCA-1: 
      0x7cfe900300a13b41      1    1[  ] ==( 4X       14.0625 Gbps Active/  LinkUp)==>       2    1[  ] "knl-sb2 HCA-1" ( )
CA: knl-sb2 HCA-1: 
      0xf4521403007d2b91      2    1[  ] ==( 4X       14.0625 Gbps Active/  LinkUp)==>       1    1[  ] "host-device HCA-1" ( )

同様に、ターゲットで InfiniBand* とサブネット・マネージャーを起動し、InfiniBand* ネットワークの各ポートのリンク情報を表示します。

[knl-sb2 ~]$ sudo service openibd start
Loading HCA driver and Access Layer:                       [  OK  ]

[knl-sb2 ~]$ sudo service opensm start
Redirecting to /bin/systemctl start  opensm.service

[knl-sb2 ~]$ iblinkinfo
CA: host-device HCA-1: 
      0x7cfe900300a13b41      1    1[  ] ==( 4X       14.0625 Gbps Active/  LinkUp)==>       2    1[  ] "knl-sb2 HCA-1" ( )
CA: knl-sb2 HCA-1: 
      0xf4521403007d2b91      2    1[  ] ==( 4X       14.0625 Gbps Active/  LinkUp)==>       1    1[  ] "host-device HCA-1" ( )

iblinkinfo は、ファブリックのすべてのポート (ターゲットマシンとホストマシンにそれぞれ 1 つずつ) のリンク情報を表示します。次に、ibping コマンドを実行してリンクをテストします (このコマンドは、イーサネットの ping コマンドに相当します)。次のコマンドを実行して、ホストマシンで ibping サーバーを起動します。

[host-device ~]$ ibping -S

ターゲットマシンからホストのポート ID を ping します。

[knl-sb2 ~]$ ibping -G 0x7cfe900300a13b41
Pong from host-device.(none) (Lid 1): time 0.259 ms
Pong from host-device.(none) (Lid 1): time 0.444 ms
Pong from host-device.(none) (Lid 1): time 0.494 ms

同様に、ターゲットマシンで ibping サーバーを起動します。

[knl-sb2 ~]$ ibping -S

そして、ホストマシンからターゲットのポート ID を ping します。

[host-device ~]$ ibping -G 0xf4521403007d2b91
Pong from knl-sb2.jf.intel.com.(none) (Lid 2): time 0.469 ms
Pong from knl-sb2.jf.intel.com.(none) (Lid 2): time 0.585 ms
Pong from knl-sb2.jf.intel.com.(none) (Lid 2): time 0.572 ms

IPoIB (IP over InfiniBand*) 設定

ここまでで、InfiniBand* ネットワークが動作していることを確認しました。次に、OoF を使用するため、IPoIB (IP over InfiniBand*) を設定する必要があります。IPoIB は、ファブリックを介して計算をオフロードする際に使用されるターゲット IP アドレスを提供します。

最初に、ib_ipoib ドライバーがインストールされていることを確認します。

[host-device ~]$ lsmod | grep ib_ipoib
ib_ipoib              136906  0
ib_cm                  47035  3 rdma_cm,ib_ucm,ib_ipoib
ib_sa                  33950  5 rdma_cm,ib_cm,mlx4_ib,rdma_ucm,ib_ipoib
ib_core               141088  12 rdma_cm,ib_cm,ib_sa,iw_cm,mlx4_ib,mlx5_ib,ib_mad,ib_ucm,ib_umad,ib_uverbs,rdma_ucm,ib_ipoib
mlx_compat             16639  17 rdma_cm,ib_cm,ib_sa,iw_cm,mlx4_en,mlx4_ib,mlx5_ib,ib_mad,ib_ucm,ib_addr,ib_core,ib_umad,ib_uverbs,mlx4_core,mlx5_core,rdma_ucm ib_ipoib

ib_ipoib ドライバーがリストされない場合は、次のコマンドを実行して Linux* カーネルにこのモジュールを追加する必要があります。

[host-device ~]$ modprobe ib_ipoib

次に、ホストで ifconfig コマンドを実行して InfiniBand* インターフェイス ib0 をリストします。

[host-device ~]$ ifconfig ib0
ib0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 2044
Infiniband hardware address can be incorrect! Please read BUGS section in ifconfig(8).
        infiniband A0:00:02:20:FE:80:00:00:00:00:00:00:00:00:00:00:00:00:00:00  txqueuelen 1024  (InfiniBand)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

このインターフェイスの IP アドレスを 10.0.0.1 に設定します。

[host-device ~]$ sudo ifconfig ib0 10.0.0.1/24
[host-device ~]$ ifconfig ib0
ib0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 2044
        inet 10.0.0.1  netmask 255.255.255.0  broadcast 10.0.0.255
Infiniband hardware address can be incorrect! Please read BUGS section in ifconfig(8).
        infiniband A0:00:02:20:FE:80:00:00:00:00:00:00:00:00:00:00:00:00:00:00  txqueuelen 1024  (InfiniBand)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 10  bytes 2238 (2.1 KiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

同様に、ターゲットで InfiniBand* インターフェイスの IP アドレスを 10.0.0.2 に設定します。

[knl-sb2 ~]$ ifconfig ib0
ib0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 2044
Infiniband hardware address can be incorrect! Please read BUGS section in ifconfig(8).
        infiniband A0:00:02:20:FE:80:00:00:00:00:00:00:00:00:00:00:00:00:00:00  txqueuelen 1024  (InfiniBand)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

[knl-sb2 ~]$ sudo ifconfig ib0 10.0.0.2/24
[knl-sb2 ~]$ ifconfig ib0
ib0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 2044
        inet 10.0.0.2  netmask 255.255.255.0  broadcast 10.0.0.255
Infiniband hardware address can be incorrect! Please read BUGS section in ifconfig(8).
        infiniband A0:00:02:20:FE:80:00:00:00:00:00:00:00:00:00:00:00:00:00:00  txqueuelen 1024  (InfiniBand)
        RX packets 3  bytes 168 (168.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 10  bytes 1985 (1.9 KiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

最後に、ホストからターゲットの新しい IP アドレス 10.0.0.2ping して、接続できることを確認します。

[host-device ~]$ ping 10.0.0.2
PING 10.0.0.2 (10.0.0.2) 56(84) bytes of data.
64 bytes from 10.0.0.2: icmp_seq=1 ttl=64 time=0.443 ms
64 bytes from 10.0.0.2: icmp_seq=2 ttl=64 time=0.410 ms
<CTRL-C>

同様に、ターゲットからホストの新しい IP アドレス 10.0.0.1 を ping して、接続できることを確認します。

[knl-sb2 ~]$ ping 10.0.0.1
PING 10.0.0.1 (10.0.0.1) 56(84) bytes of data.
64 bytes from 10.0.0.1: icmp_seq=1 ttl=64 time=0.313 ms
64 bytes from 10.0.0.1: icmp_seq=2 ttl=64 time=0.359 ms
64 bytes from 10.0.0.1: icmp_seq=3 ttl=64 time=0.375 ms
<CTRL-C>

パスワードなしの SSH 設定 (オプション)

ターゲットマシンにワークロードをオフロードする場合、セキュアシェル (SSH) ではターゲットにログオンしてワークロードを実行するのにターゲットのパスワードが必要です。この処理を自動化するには、パスワードなしの ssh ログインを有効にする必要があります。最初に、ホストでパスワードを入力せずに認証キーのペアを生成します。

[host-device ~]$ ssh-keygen -t rsa

次に、ssh-copy-id コマンドでホストの新しい公開キーをターゲットの公開キーに追加します。

[host-device ~]$ ssh-copy-id @10.0.0.2

オフロード・オーバー・ファブリック (OoF)

ここまでで、高速ネットワークのセットアップが完了し、動作していることを確認しました。OoF 機能を有効にするには、ホストにインテル® Parallel Studio XE 2017 for Linux* をインストールする必要があります。そして、次のコマンドを実行してシェル環境を設定します。

[host-device]$ source /opt/intel/parallel_studio_xe_2017.0.035/psxevars.sh intel64
Intel(R) Parallel Studio XE 2017 for Linux*
Copyright (C) 2009-2016 Intel Corporation. All rights reserved.

以下のサンプルプログラムを使用して OoF 機能をテストします。このサンプルプログラムは、ホストで定数 A とバッファー x、y、z の割り当てと初期化を行い、OpenMP* デバイス構文ディレクティブ (#pragma omp target map…) を使用して計算をターゲットにオフロードします。target ディレクティブは、ターゲット上にデバイスデータ環境を作成します。実行時に計算を開始する前に変数 x、yA の値がターゲットにコピーされ、ターゲットが計算を完了すると変数 y の値がホストにコピーバックされます。この例では、ターゲットは lscpu コマンドで取得した CPU 情報を解析し、OpenMP* スレッドのチームをスポーンして、ベクトルスカラー積を計算し結果をベクトルに加算しています。

#include <stdio.h>

int main(int argc, char* argv[])
{
    int i, num = 1024;; 
    float A = 2.0f; 

    float *x = (float*) malloc(num*sizeof(float)); 
    float *y = (float*) malloc(num*sizeof(float)); 
    float *z = (float*) malloc(num*sizeof(float)); 

    for (i=0; i<num; i++)
    {
       x[i] = i; 
       y[i] = 1.5f; 
       z[i] = A*x[i] + y[i]; 
    }

    printf("Workload is executed in a system with CPU information:\n"); 
    #pragma omp target map(to: x[0:num], A) \
                       map(tofrom: y[0:num])
    {
        char command[64];  
        strcpy(command, "lscpu | grep Model"); 
        system(command); 
        int done = 0; 
        
        #pragma omp parallel for
        for (i=0; i<num; i++)
        {
            y[i] = A*x[i] + y[i]; 
       
            if ((omp_get_thread_num() == 0) && (done == 0))
            {
               int numthread = omp_get_num_threads(); 
               printf("Total number of threads: %d\n", numthread); 
               done = 1; 
            }
        }
    }

    int passed = 0; 

    for (i=0; i<num; i++)
        if (z[i] == y[i]) passed = 1;  
    
    if (passed == 1)
        printf("PASSED!\n"); 
    else
        printf("FAILED!\n"); 

    free(x); 
    free(y); 
    free(z); 

    return 0; 
}

この OpenMP* プログラムを、オフロード領域を第 2 世代インテル® Xeon Phi™ プロセッサー向けにビルドするように指示する -qoffload-arch=mic-avx512 オプションを指定してインテル® コンパイラーでコンパイルします。プログラムを実行する前に、高速ネットワークを使用するように、OFFLOAD_NODES 環境変数にターゲットマシンの IP アドレス (この例では 10.0.0.2) を設定します。

[host-device]$ icc -qopenmp -qoffload-arch=mic-avx512 -o OoF-OpenMP-Affinity OoF-OpenMP-Affinity.c

[host-device]$ export OFFLOAD_NODES=10.0.0.2

[host-device]$ ./OoF-OpenMP-Affinity
Workload is executed in a system with CPU information: 
Model:                 87
Model name:            Intel(R) Xeon Phi(TM) CPU 7250 @000000 1.40GHz
PASSED! 
Total number of threads: 268

オフロード処理は、インテル® Coprocessor Offload Infrastructure (インテル® COI) によって内部処理されます。デフォルトでは、オフロードコードはターゲットで利用可能なすべての OpenMP* スレッドを使用して実行されます。ターゲットには 68 のコアがあり、そのうち 1 つのコアでインテル® COI デーモンが実行しているため、 67 のコアが利用可能で、合計スレッド数は 268 (コアあたり 4 スレッド) になります。coitrace コマンドを使用して、インテル® COI のすべての API 呼び出しを追跡できます。

[host-device]$ coitrace ./OoF-OpenMP-Affinity
COIEngineGetCount [ThID:0x7f02fdd04780]
        in_DeviceType = COI_DEVICE_MIC
        out_pNumEngines = 0x7fffc8833e00 0x00000001 (hex) : 1 (dec)

COIEngineGetHandle [ThID:0x7f02fdd04780]
        in_DeviceType = COI_DEVICE_MIC
        in_EngineIndex = 0x00000000 (hex) : 0 (dec)
        out_pEngineHandle = 0x7fffc8833de8 0x7f02f9bc4320

Workload is executed in a system with CPU information: 
COIEngineGetHandle [ThID:0x7f02fdd04780]
        in_DeviceType = COI_DEVICE_MIC
        in_EngineIndex = 0x00000000 (hex) : 0 (dec)
        out_pEngineHandle = 0x7fffc88328e8 0x7f02f9bc4320

COIEngineGetInfo [ThID:0x7f02fdd04780]
        in_EngineHandle = 0x7f02f9bc4320
        in_EngineInfoSize = 0x00001440 (hex) : 5184 (dec)
        out_pEngineInfo = 0x7fffc8831410
                DriverVersion: 
                DeviceType: COI_DEVICE_KNL
                NumCores: 68
                NumThreads: 272 

<以下略>

OpenMP* スレッド・アフィニティー

上記のサンプルプログラムの結果から、ターゲットで実行されるデフォルトのスレッド数 (272) が分かります。ターゲットで実行するスレッド数は、明示的に指定することもできます。1 つの方法は、ホストで環境変数を使用してターゲットの実行環境を変更します。最初に、ターゲット固有の環境変数のプリフィクスを定義し、このプリフィクスを OpenMP* スレッド・アフィニティー環境変数に追加します。例えば、次の環境変数の設定は、オフロードランタイムがターゲット上で 8 つのスレッドを使用するように指定します。

[host-device]$ $ export MIC_ENV_PREFIX=PHI
[host-device]$ $ export PHI_OMP_NUM_THREADS=8

インテルの OpenMP* ランタイム拡張の KMP_PLACE_THREAD 環境変数と KMP_AFFINITY 環境変数を使用すると、スレッドを物理プロセシング・ユニット (コア) にバインドすることができます (詳細は、『インテル® C++ コンパイラー・デベロッパー・ガイドおよびリファレンス』の「スレッド・アフィニティー・インターフェイス」 を参照してください)。例えば、次の環境変数の設定は、オフロードランタイムが隣接する 8 つのスレッドを使用するように指定します。

[host-device]$ $ export PHI_KMP_AFFINITY=verbose,granularity=thread,compact
[host-device]$ $ ./OoF-OpenMP-Affinity

OMP_PROC_BIND 環境変数を使用して OpenMP* アフィニティーを利用することもできます。例えば、OMP_PROC_BIND で上記と同じ隣接する 8 つのスレッドを使用するように指定するには、次のように設定します。

[host-device]$ $ export MIC_ENV_PREFIX=PHI
[host-device]$ $ export PHI_KMP_AFFINITY=verbose
[host-device]$ $ export PHI_OMP_PROC_BIND=close
[host-device]$ $ export PHI_OMP_NUM_THREADS=8
[host-device]$ $ ./OoF-OpenMP-Affinity

または、分散する 8 つのスレッドで実行する場合は、次のように設定します。

[host-device]$ $ export PHI_OMP_PROC_BIND=spread
[host-device]$ $ ./OoF-OpenMP-Affinity

結果を次の表に示します。

OpenMP* スレッド数 コア数
0 0
1 10
2 19
3 31
4 39
5 48
6 56
7 65

8 つのスレッドをコアあたり 2 スレッド (合計 4 コア) で実行するには、次の設定を使用します。

[host-device]$ export PHI_OMP_PROC_BIND=close; 
[host-device]$ export PHI_OMP_PLACES="cores(4)"
[host-device]$ export PHI_OMP_NUM_THREADS=8
[host-device]$ $ ./OoF-OpenMP-Affinity

結果を次の表に示します。

OpenMP* スレッド数 コア数
0 0
1 0
2 1
3 1
4 2
5 2
6 3
7 3

まとめ

このチュートリアルでは、OoF アプリケーションのセットアップと実行について、ハードウェアの取り付けとソフトウェアのインストールも含め詳しく説明しました。ここで紹介した例では、Mellanox* InfiniBand* HCA を使用しましたが、代わりにインテル® OPA を使用することもできます。オフロード・プログラミング・モデルを使用するサンプル OpenMP* アプリケーションを使って、インテル® Xeon® プロセッサー・ベースのホストで実行し、高速ネットワークを介してインテル® Xeon Phi™ プロセッサー・ベースのターゲットに計算をオフロードする方法を示しました。また、インテル® Xeon Phi™ プロセッサー向けにコンパイルおよび実行する方法と、インテル® Xeon Phi™ プロセッサー上で OpenMP* スレッド・アフィニティーを制御する方法も紹介しました。

参考資料

著者紹介

Loc Q Nguyen。ダラス大学で MBA を、マギル大学で電気工学の修士号を、モントリオール理工科大学で電気工学の学士号を取得しています。現在は、インテル コーポレーションのソフトウェア & サービスグループのソフトウェア・エンジニアで、コンピューター・ネットワーク、並列コンピューティング、コンピューター・グラフィックスを研究しています。

コンパイラーの最適化に関する詳細は、最適化に関する注意事項を参照してください。

関連記事