C++ 配列コピーの速度
パディング処理をして新しい配列を作るときに、どのようにしてコピーするのが速いのか気になったので、比較してみた。
下図のような処理を、①逐次コピー ②std::copy ③memmove ④memcpyで実装して速度を比較してみた。
元の配列の大きさをcol × row、パディングをpadとした。
①逐次コピー
std::array<int, (row+2*pad)*(col+2*pad)> arr1 = {}; for (int i = 0; i < col; ++i) { for (int j = 0; j < row; ++j) { arr1[(i+pad)*(row+2*pad) + (j+pad)] = arr[i*row + j]; } }
②std::copy
std::array<int, (row+2*pad)*(col+2*pad)> arr2 = {}; for (int i = 0; i < col; ++i) { std::copy(arr.begin() + i*row, arr.begin() + (i+1)*row, arr2.begin() + (i+pad)*(row+2*pad) + pad); }
③memmove
std::array<int, (row+2*pad)*(col+2*pad)> arr3 = {}; for (int i = 0; i < col; ++i) { std::memmove(&(arr3[(i+pad)*(row+2*pad) + pad]), &(arr[i*row]), row*sizeof(int)); }
④memcpy
std::array<int, (row+2*pad)*(col+2*pad)> arr4 = {}; for (int i = 0; i < col; ++i) { std::memcpy(&(arr4[(i+pad)*(row+2*pad) + pad]), &(arr[i*row]), row*sizeof(int)); }
結果
col * row = 300 * 300, pad = 1として実験を行った。 chronoクラスのsteady_clockで実行時間を測定した。
それぞれ10回づつ実行した時の平均値はこのようになった。
time[μsec] | |
---|---|
①逐次コピー | 924.4 |
②std::copy | 40.1 |
③memmove | 21.9 |
④memcpy | 19.9 |
memcpy vs memmove
memcpyはコピー元のbufferとコピー先にbufferが重なった時の動作が未定義という問題がある。 今回の処理では別のバッファに書き込んでいるので大丈夫だが、バッファが重なっていないことをcheckする処理を入れたほうが確実だろう。
ということで、バッファが重なっていないことをcheckする処理を入れてもmemcpyのほうが速いのか調べてみた。
if (!(arr4.begin() > arr.end() || arr.begin() > arr4.end()))
abort();
arrayで確保される領域は連続しているので、これでcheckできているはずである。
col * row = 300 * 300, pad = 1として実験を行った。 chronoクラスのsteady_clockで実行時間を測定した。
それぞれ10回づつ実行した時の平均値はこのようになった。
time[μsec] | |
---|---|
memmove | 25.5 |
memcpy | 25.0 |
300*300程度の配列では差はほとんどないことがわかった。