実行

並列実行

NLCPyを使用するPythonスクリプトは、Vector Engine(VE)でマルチスレッド化することにより、性能を発揮します。並列スレッドのデフォルト数は、実行時に環境変数 VE_OMP_NUM_THREADS で指定できます。 VE_OMP_NUM_THREADS が未定義であるか、値が無効な場合、システムで使用可能なCPUコアの最大数が設定されます。

例を以下に示します。

  • 対話モード:

    $ VE_OMP_NUM_THREADS=4 python
    >>> import nlcpy
    

    後続の計算は、VE上の4つの並列スレッドによって実行されます。

  • 非対話モード:

    $ VE_OMP_NUM_THREADS=4 python example.py
    

    example.pyの計算は、VE上の4つの並列スレッドによって実行されます。

VEのノード番号を指定する場合は、次のように環境変数 VE_NODE_NUMBER を使用する必要があります。

  • 対話モード:

    $ VE_NODE_NUMBER=1 VE_OMP_NUM_THREADS=4 python
    >>> import nlcpy
    

    後続の計算は、VEノード#1の4つの並列スレッドによって実行されます。

  • 非対話モード:

    $ VE_NODE_NUMBER=1 VE_OMP_NUM_THREADS=4 python example.py
    

    example.pyの計算は、VEノード#1の4つの並列スレッドによって実行されます。

注釈

1つのVEに対して2つ以上のNLCPyプログラムを実行すると、コンテキストスイッチが発生します。 このような場合、パフォーマンスは大幅に低下します。

複数VEの利用

NLCPyは複数VEを利用するための方法をを2種類提供しています。

  1. mpi4py-ve

    mpi4py-veは、SX-Aurora TSUBASA用に、Message Passing Interface (MPI)を利用して、プロセス間の通信を実装したPythonライブラリです。MPI通信に利用可能なオブジェクトは numpy.ndarraynlcpy.ndarray です。mpi4py-veの利用には、NEC MPIのランタイムパッケージが必要な点にご注意ください。

    詳細は、 mpi4py-ve project をご参照ください。

  2. VE デバイス管理

    Pythonスクリプトから "with" コンテキストマネージャーを利用することで、実行するVEデバイスを選択することが可能です。

    import nlcpy
    with nlcpy.venode.VE(0):
        # do something on VE#0
        x_ve0 = nlcpy.arange(10)
    with nlcpy.venode.VE(1):
        # do something on VE#1
        y_ve1 = nlcpy.arange(10)
    # transfer x_ve0 to VE#1
    x_ve1 = nlcpy.venode.transfer_array(x_ve0, nlcpy.venode.VE(1))
    with nlcpy.venode.VE(1):
        # do something on VE#1
        z_ve1 = x_ve1 + y_ve1
    

    nlcpy.venode.transfer_array() は現時点ではVE間の直接転送に対応していない点にご注意ください。言い換えると、VE間のデータ転送はVHを経由する必要があります。もし、大規模なデータを転送する必要がある場合は、mpi4py-veのご利用を推奨します。

    デフォルトでは、NLCPyは import nlcpy が実行されたときに物理VE#0にのみVEプロセスを生成します。他のVEプロセスに関しては、 nlcpy.venode.VENode.__enter__(), nlcpy.venode.VENode.use(), nlcpy.venode.VENode.apply() のいずれかが初めて呼び出されたときに生成されます。ここで、VEプロセスの生成には数秒かかる点にご注意ください。もし、 import nlcpy が実行されるときに複数のVEプロセスを生成したい場合は、環境変数 VE_NLCPY_NODELIST=0,1,... を指定することができます。NLCPyは import nlcpy 実行時に環境変数 VE_NLCPY_NODELIST に指定されたIDに対応する物理VEに対してプロセスを生成します。

    環境変数 VE_NLCPY_NODELIST が設定されているとき、 nlcpy.venode.VE() の引数に指定するIDと物理VEのIDは異なる場合があります。

    環境変数 VE_NLCPY_NODELIST=1,2 を設定した場合のVEデバイスマッピングの例は以下の通りです:

    $ VE_NLCPY_NODELIST=1,2 python
    >>> import nlcpy
    >>> nlcpy.venode.VE(0)
    <VE node logical_id=0, physical_id=1>
    >>> nlcpy.venode.VE(1)
    <VE node logical_id=1, physical_id=2>
    

数学関数の最適化

環境変数 VE_NLCPY_FAST_MATHyes または YES に設定すると、NLCPyはVE用に最適化した共有オブジェクト libnlcpy_ve_kernel_fast_math.so を使用します。デフォルトでは、VE_NLCPY_FAST_MATH は未設定です。共有オブジェクト libnlcpy_ve_kernel_fast_math.so は、NEC C/C++コンパイラの以下の最適化オプションを指定してコンパイルされています。

  • -ffast-math

    ベクトル化ループ外でスカラ高速バージョンの数学関数を使用します。

  • -mno-vector-intrinsic-check

    ベクトル化された部分から呼び出された数学関数の引数の値の範囲を実行時に検査しません。
    このオプションの対象となる数学関数は次のとおりです。
    acos, acosh, asin, atan, atan2, atanh, cos, cosh, cotan, exp, exp10, exp2, expm1, log10, log2, log, pow, sin, sinh, sqrt, tan, tanh
  • -freciprocal-math

    x/yx * (1/y) に変更します。

  • -mvector-power-to-explog

    ベクトル化されたループ中の pow(R1,R2) の呼び出しを exp(R2*log(R1)) の呼び出しに置き換えることを許可します。 powf() に対しても同様の最適化が適用されます。powpowf で計算した場合に比べて実行時間は短縮されますが、計算結果が誤差レベルで変わることがあります。

  • -mvector-low-precise-divide-function

    低精度のベクトル浮動小数点数除算を使用します。通常精度版と比較して高速に処理されますが、除算結果の仮数部に最大1ビットの誤差が含まれることがあります。

これらの最適化は副作用を引き起こします。 たとえば、 nan または inf が正しく取得されない場合があります。

VE_NLCPY_FAST_MATH は次のように設定できます。

  • 対話モード:

    $ VE_NLCPY_FAST_MATH=yes python
    >>> import nlcpy
    
  • 非対話モード:

    $ VE_NLCPY_FAST_MATH=yes python example.py
    

メモリプールの管理

NLCPyは、事前に確保されたメモリ (メモリプール) を再利用してmallocとfreeの呼び出しを省略することで、VEメモリ確保のオーバーヘッドを削減しています。環境変数 VE_NLCPY_MEMPOOL_SIZE を設定することで、メモリプールの容量を管理することが可能です。デフォルトの値は1 GB に設定されています。この環境変数の利用方法は、 環境変数 をご参照ください。

この環境変数に1 GB以上の値を設定することで性能改善するケースがあります。ただし、メモリフラグメンテーションの影響でメモリ不足が発生することがあります。