はじめに
みずほリサーチ&テクロノジーズの @fujine です。
本記事では、LinuxでZIPファイルを高速に展開するテクニックを解説します。ご紹介するケースでは、4vCPUマシンで約3倍速くなりました。CPUコア数が多ければもっと速くなります!
お急ぎの方は、こちらをコピペして下さい。
ZIPファイルのパス
を書き換えて実行するだけで動きます。
sudo apt install -y parallel
zipile=ZIPファイルのパス
zipinfo -1 $zipfile | parallel --bar "unzip -q $zipfile {}"
モチベーション
Kaggle等のデータ分析コンペでは、データセットをZIP形式でダウンロードすることが多いです。数GBのZIPファイルなら展開もすぐに終わりますが、数十〜数百GBの巨大ZIPになると、展開するだけで数十分(下手したら数時間!)かかる なんてこともあります。
また、コンペでよく使われるGoogle Colabやクラウドコンピューティングの多くは従量課金制です。展開に時間が掛かるほど費用も積み上がってしまうため、「短時間で展開できればお財布にも優しい」というメリットがあります💰
そこで、ZIPファイルをなるべく高速に展開する方法を模索しました。
検証環境
Debian環境で検証します。CPUコア数は4です。
$ cat /etc/debian_version
11.8
$ lscpu | grep On-line
On-line CPU(s) list: 0-3
使用するデータセット
KaggleのPetals to the Metal - Flower Classification on TPUコンペのデータセットを使用します。
Kaggle APIを使用している方は、データセットを以下コマンドでダウンロードできます。
$ kaggle competitions download tpu-getting-started
ZIPファイルのサイズは4.8GB、圧縮されているファイル数は193件です。
$ zipfile=tpu-getting-started.zip
$ du -h $zipfile
4.8G tpu-getting-started.zip
$ zipinfo -1 $zipfile | wc -l
193
通常の方法で展開
まずは通常のunzipコマンドで展開します。所要時間は約53秒でした。
$ time unzip -q $zipfile
real 0m53.407s
user 0m37.133s
sys 0m15.895s
より高速に展開
ZIP内の各ファイルをマルチコアで並列展開することで、高速化を図ります。並列処理にはGNU parallelパッケージを使用します。
$ sudo apt install -y parallel
以下コマンドで並列展開します。やっていることはシンプルで、
-
zipinfo -1 $zipfile
はZIP内のファイル名を一覧出力し、パイプに渡す -
parallel
はファイル名を引数として受け取り、ジョブの{}
に代入する -
unzip -q $zipfile {ファイル名}
ジョブが並列で実行される
という流れです。
$ time zipinfo -1 $zipfile | parallel "unzip -q $zipfile {}"
real 0m18.243s
user 0m53.139s
sys 0m13.357s
所要時間は約18秒となり、先ほどのunzipよりも3倍弱ほど高速化に成功しました。仕組み上、CPUコア数が多いマシンほど更に高速に展開できるはずです。
有効なシーン
ZIPファイルに多数ファイルが圧縮されており、かつ、複数のCPUコアを搭載したマシンであるほど効果的です。
逆に、CPUコアが1つのみだったり、ZIPファイルに少数のファイルしか圧縮されていない場合、効果は薄いです。
parallelの便利なオプション
これだけでも十分便利ですが、parallel
には他にも様々なオプションが用意されています。
以下、個人的に便利だと思ったオプションをいくつかご紹介します。
進捗を表示
--progress
で、ジョブの実行数やCPU使用率などが出力されます。
$ zipinfo -1 $zipfile | parallel --progress "unzip -q $zipfile {}"
--bar
にすれば、プログレスバーとともに進捗率や残り時間が表示されるため、視覚的にも分かりやすいです。
$ zipinfo -1 $zipfile | parallel --bar "unzip -q $zipfile {}"
バックグラウンドで実行
--bg
でジョブがバックグランド実行されます。並列処理を裏で実行しつつ、すぐ別の作業に取り掛かりたい時に便利です。
$ zipinfo -1 $zipfile | parallel --bg "unzip -q $zipfile {}"
$
実行前にコマンドを確認
--dry-run
で、実行されるコマンドが出力されます(実際には実行されません)。コマンドが正しいかを実行前に確認するのに重宝します。
$ zipinfo -1 $zipfile | parallel --dry-run "unzip -q $zipfile {}" | head -n 5
unzip -q tpu-getting-started.zip sample_submission.csv
unzip -q tpu-getting-started.zip tfrecords-jpeg-192x192/test/00-192x192-462.tfrec
unzip -q tpu-getting-started.zip tfrecords-jpeg-192x192/test/01-192x192-462.tfrec
unzip -q tpu-getting-started.zip tfrecords-jpeg-192x192/test/02-192x192-462.tfrec
unzip -q tpu-getting-started.zip tfrecords-jpeg-192x192/test/03-192x192-462.tfrec
ジョブの最大並列数を指定
--jobs
で最大並列数を指定します。デフォルトは全てのCPUコアが利用されるため、最大並列数を下げることで、他の実行中プロセスへの影響を低減できます。
指定値は整数(コア数)だけでなく、50%
のように指定することも可能です。
# 最大で3並列
$ zipinfo -1 $zipfile | parallel --jobs 3 "unzip -q $zipfile {}"
# 最大で50%
$ zipinfo -1 $zipfile | parallel --jobs 50% "unzip -q $zipfile {}"
引数の1件目をスキップ
--skip-first-line
で、引数の1件目のみをスキップさせます。
どこで役立つかというと、例えばCSVファイルの各行をパイプで渡す際、1行目のヘッダーだけスキップし、2行目以降のデータだけを並列処理させる、といったことが簡単にできます。
メモリをスワップさせない
--noswap
を指定すれば、メモリのスワップが発生している時に新しいジョブを実行しないように制御できます。
並列展開するとCPUコア数に比例してメモリ消費量も増えてしまうため、このオプションだけでスワップ回避まで面倒を見てくれるのは非常に有難いです。
実行結果を出力
--joblog ログファイル名
で、実行結果がログ出力されます。
各ジョブの実行時間(JobRuntime
)や終了ステータス(Exitval
)などが出力されるため、失敗したジョブや遅いジョブがあればすぐに特定できます。
$ zipinfo -1 $zipfile | parallel --joblog report.txt "unzip -q $zipfile {}"
$ head report.txt
Seq Host Starttime JobRuntime Send Receive Exitval Signal Command
1 : 1697803718.751 0.012 0 0 0 0 unzip -q tpu-getting-started.zip sample_submission.csv
3 : 1697803718.759 0.161 0 0 0 0 unzip -q tpu-getting-started.zip tfrecords-jpeg-192x192/test/01-192x192-462.tfrec
2 : 1697803718.755 0.171 0 0 0 0 unzip -q tpu-getting-started.zip tfrecords-jpeg-192x192/test/00-192x192-462.tfrec
4 : 1697803718.763 0.175 0 0 0 0 unzip -q tpu-getting-started.zip tfrecords-jpeg-192x192/test/02-192x192-462.tfrec
5 : 1697803718.769 0.184 0 0 0 0 unzip -q tpu-getting-started.zip tfrecords-jpeg-192x192/test/03-192x192-462.tfrec
6 : 1697803718.925 0.184 0 0 0 0 unzip -q tpu-getting-started.zip tfrecords-jpeg-192x192/test/04-192x192-462.tfrec
7 : 1697803718.930 0.190 0 0 0 0 unzip -q tpu-getting-started.zip tfrecords-jpeg-192x192/test/05-192x192-462.tfrec
8 : 1697803718.944 0.190 0 0 0 0 unzip -q tpu-getting-started.zip tfrecords-jpeg-192x192/test/06-192x192-462.tfrec
9 : 1697803718.963 0.187 0 0 0 0 unzip -q tpu-getting-started.zip tfrecords-jpeg-192x192/test/07-192x192-462.tfrec
終わりに
本記事では、GNUのparallel
パッケージを活用したZIPファイルの並列展開をご紹介しました。
parallel
はZIP展開のみならず、並列処理可能な様々なユースケースに応用できそうです。