C++ 配列コピーの速度

パディング処理をして新しい配列を作るときに、どのようにしてコピーするのが速いのか気になったので、比較してみた。

下図のような処理を、①逐次コピー ②std::copy ③memmove ④memcpyで実装して速度を比較してみた。 f:id:wakanapo:20180522162743p:plain

元の配列の大きさを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程度の配列では差はほとんどないことがわかった。