GCS → S3 へのデータコピーは、gsutil 単体でよりも gsutil + AWS CLI の方が早い

他の人がやっている様子を見たところ、 gsutil と AWS CLIをパイプで繋いでもできる様子。

qiita.com

また、gsutil はファイルサイズの上限があるようなので、それを超える場合もパイプでやると良いようだった。
試してみたところ、性能的にも gsutil + AWS CLIの方が早い様子があったので検証してみる。
インスタンスはEC2 の c5.large インスタンスを利用。GCS/S3バケットもEC2インスタンスも東京リージョン。

検証スクリプト

# 環境変数設定
GCS_PATH="gs://my-bucket/"
S3_PATH="s3://my-bucket/gsutil-speed/"

# 実行パラメータとして、ファイルサイズ(KB)を設定
kbs=(1 1000 5000 10000 50000 100000 500000 1000000)
# time コマンドで計測するための出力
TIMEFORMAT="%3R,%3U,%3S"
# 出力先CSVのヘッダ行
echo "kb,tool,real,user,sys" > results.csv

# ファイルサイズごとにループ
for kb in ${kbs[@]} ; do
  echo $kb
  # ファイル作成
  dd if=/dev/urandom of=${kb}.dat bs=1024 count=${kb}
  
  # コピー元となる GCS バケットに置く
  gsutil cp ${kb}.dat ${GCS_PATH}
  
  # gsutil 単体での転送を、3回計測
  for i in `seq 3`; do
    exec 3>&1 4>&2
    result=$( { time gsutil cp ${GCS_PATH}${kb}.dat ${S3_PATH}{kb}.dat 1>&3 2>&4; } 2>&1)
    exec 3>&- 4>&-
    echo "${kb},gsutil,${result}" >> results.csv
  done
  # gsutil + AWS CLI での転送を、3回計測
  for i in `seq 3`; do
    exec 3>&1 4>&2
    result=$( { time gsutil cp ${GCS_PATH}${kb}.dat - | aws s3 cp - ${S3_PATH}{kb}.dat 1>&3 2>&4; } 2>&1)
    exec 3>&- 4>&-
    echo "${kb},gsutil+cli,${result}" >> results.csv
  done
done

結果

データ量 gsutil 実行時間(秒) gsutil+AWS CLI 実行時間(秒)
1KB 1.37 1.36
1MB 1.66 1.46
5MB 2.18 1.56
10MB 2.75 1.75
50MB 7.45 2
100MB 13.41 2.6
500MB 67.98 5.52
1GB 123.01 8.66

考察

随分差がある。データ量が大きければ大きいほど乖離が大きい。

  • gsutil の場合は実行時間がデータ量に比例しているので、並列化が効いていないように見える。 なお、gsutil 単体 + -m オプションでやってみると、コケる。
[Errno 32] Broken pipeB/ 97.7 MiB]   0% Done
CommandException: 1 file/object could not be transferred.
  • gsutil + AWS CLI だと比例していないので、適度に並列化が効いているんだろうと思われる。-m オプションなしでも有効。実際にAWSでCreateMultiUpload API を叩いている。

余談

time コマンドの結果をbashの変数で受け取るのに手間取った。以下のstackoverflowから流用。

stackoverflow.com