arrayfun
を使用した、要素単位の MATLAB 関数の GPU におけるパフォーマンス改善
この例では、GPU でarrayfun
を使用して MATLAB® 関数を実行することでコードのパフォーマンスを改善する方法を示します。
MATLAB 関数に要素単位の演算が多数含まれている場合、arrayfun
を使用すると、gpuArray
入力データを用いて MATLAB 関数を GPU で直接実行する場合に比べてパフォーマンスを改善できます。関数が arrayfun
と互換性をもつには、入力配列の単一の要素について演算を行い、スカラー演算と算術演算を使用して出力配列の単一の要素を計算できなければなりません。
この例では、関数を CPU で実行する場合、GPU で arrayfun
を使用せずに実行する場合、および GPU で arrayfun
を使用して実行する場合の実行時間を比較します。
テスト用の関数の定義
特殊相対性理論の原理に従い、物体が動くときの物体の物理特性の変化はローレンツ因子で決まります。観測者に対する物体の相対的な移動速度が であるとすると、ローレンツ因子 は次のように定義されます。
.
は と の比です。 は真空中の光の速度です。この例の最後で定義している関数 lorentz
は、ローレンツ因子を次のように計算します。
Y = 1./sqrt(1-B.*B);
GPU 実行用の関数の準備
ほとんどの MATLAB 関数は、既定では CPU で実行されます。lorentz
を GPU で実行するには、gpuArray
オブジェクトを入力として指定します。gpuArray
オブジェクトは GPU メモリに格納される配列を表します。多くの関数が gpuArray
の入力をサポートしているため、通常はコードに最小限の変更を加えるだけで GPU で実行できます。詳細については、GPU での MATLAB 関数の実行を参照してください。
lorentz
には要素単位の個別の演算が含まれているため、各演算を GPU で一度に 1 つずつ実行してもパフォーマンスはそれほど改善しません。arrayfun
を使用して関数 lorentz
のすべての演算を一度に実行することで、パフォーマンスを改善できます。
関数 lorentz
を GPU で arrayfun
を使用して実行するために、関数のハンドルを定義します。
lorentzFcn = @lorentz;
比較用のパラメーターの設定
この例では、 ~ の要素を含む配列で関数 lorentz
の実行時間を比較します。比較を行う回数、および関数に渡す配列のサイズの上限と下限を設定します。
numComp = 15; lowerLimit = 4; upperLimit = 8.5;
関数に渡す配列のサイズは、対数的に等間隔な配列として指定します。この例の実行には数分かかる場合があります。実行時間を短縮するには、配列のサイズの上限を小さくしてください。
arraySize = ceil(logspace(lowerLimit,upperLimit,numComp));
CPU と GPU での実行時間の測定
次のようにして、それぞれの実行モードの時間を測定します。
for i = 1:numComp fprintf('Timing function for input array of size %d \n', arraySize(i)); % Create random input data data = rand(arraySize(i),1,"single"); % CPU execution tcpu(i) = timeit(@() lorentzFcn(data)); % Send data to the GPU gdata = gpuArray(data); % GPU execution using only gpuArray objects tgpuObject(i) = gputimeit(@() lorentzFcn(gdata)); % GPU execution using gpuArray objects with arrayfun tgpuArrayfun(i) = gputimeit(@() arrayfun(lorentzFcn,gdata)); end
Timing function for input array of size 10000 Timing function for input array of size 20962 Timing function for input array of size 43940 Timing function for input array of size 92106 Timing function for input array of size 193070 Timing function for input array of size 404709 Timing function for input array of size 848343 Timing function for input array of size 1778280 Timing function for input array of size 3727594 Timing function for input array of size 7813708 Timing function for input array of size 16378938 Timing function for input array of size 34333201 Timing function for input array of size 71968568 Timing function for input array of size 150859071 Timing function for input array of size 316227767
結果の比較
結果を比較するために、実行時間をデータ要素数に対してプロットします。
loglog(arraySize,[tgpuObject; tgpuArrayfun; tcpu]) xlabel("Input Array Size") ylabel("Execution Time (s)") legend(["GPU Execution" "GPU Execution with \fontname{courier}arrayfun" "CPU Execution"], ... location="southeast")
小さい配列については、CPU の方が GPU よりも関数の実行が高速です。入力配列のサイズが大きくなるにつれ、GPU のパフォーマンスの改善が CPU のパフォーマンスに比べて大きくなります。しきい値の配列サイズを超えると、GPU の方が CPU よりも関数の実行が高速になります。GPU のパフォーマンスが CPU のパフォーマンスを上回るしきい値は、使用するハードウェアや実行する関数によって異なります。
GPU のみの実行時間と GPU で arrayfun
を使用した実行時間のそれぞれに対する CPU の実行時間の比率を計算します。
gpuObjectSpeedup = tcpu./tgpuObject; gpuArrayfunSpeedup = tcpu./tgpuArrayfun;
比率を入力配列のサイズに対してプロットします。
semilogx(arraySize,[gpuObjectSpeedup;gpuArrayfunSpeedup]) xlabel("Input Array Size") ylabel("Ratio of CPU to GPU Execution Times") legend(["GPU Execution" "GPU Execution with \fontname{courier}arrayfun"],location="southeast")
lorentz
の実行は、arrayfun
を使用した場合の方が GPU のみで実行した場合よりも一貫して高速になります。この例で説明した手法を独自のコードに適用した場合のパフォーマンスの改善は、ハードウェアや実行するコードに大きく依存します。
サポート関数
関数 lorentz
は、 を受け取り、次の方程式に従ってローレンツ因子を計算します。
.
function Y = lorentz(B) Y = 1./sqrt(1-B.*B); end
参考
arrayfun
| gpuArray
| gputimeit