LoginSignup
5
4

コマンド3行だけでZIPファイルを3倍高速に展開する!

Posted at

はじめに

みずほリサーチ&テクロノジーズの @fujine です。
本記事では、LinuxでZIPファイルを高速に展開するテクニックを解説します。ご紹介するケースでは、4vCPUマシンで約3倍速くなりました。CPUコア数が多ければもっと速くなります!

お急ぎの方は、こちらをコピペして下さい。
ZIPファイルのパスを書き換えて実行するだけで動きます。

sudo apt install -y parallel

zipile=ZIPファイルのパス
zipinfo -1 $zipfile | parallel --bar "unzip -q $zipfile {}"

bar.gif

モチベーション

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 {}"

progress.gif

--barにすれば、プログレスバーとともに進捗率や残り時間が表示されるため、視覚的にも分かりやすいです。

$ zipinfo -1 $zipfile | parallel --bar "unzip -q $zipfile {}"

bar.gif

バックグラウンドで実行

--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展開のみならず、並列処理可能な様々なユースケースに応用できそうです。

参考リンク

5
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
4