沼津暮らし

沼津で暮らしているウェブエンジニアが雑多に書くブログ

gzip のバッファサイズの最適値を調べてみる ベンチマークで推移を調査

最近データをストレージにためる業務をしています。

当然ストレージに置くときにはデータを圧縮するのですが、データサイズを小さくするのが優先なのでgzipで圧縮を行っています。

圧縮されたデータを読み込む部分を少しでも早くするために今日は読み込みを行うときのバッファサイズと処理時間について調べてみたいと思います。

バッファサイズって何?

ざっくりとした理解ではネットワークの送信バッファとかと同じ理解です。

データを読み込み圧縮処理を一度にどれくらいのデータサイズで行うかというオプションだと思います。

小さすぎてもオーバヘッドが大きくなり効率が悪いですし、ある程度の大きさ以上にしても効率は変わらなくなりメモリリソースを無駄にしてしまいます。

調査方法

32MBのint型のデータを1000パターンのランダムで生成したデータに対して20回圧縮されたデータを読み込む処理を実行した結果の平均の秒数を出します。

試すバッファサイズは32KBから初めて倍に増やして計測します。

8KB
16KB
32KB
64KB
128KB
256KB
512KB
1024KB
2048KB
4096KB
8192KB
16384KB
32768KB

計測プログラム

以下のプログラムで計測。

import java.io.*;
import java.nio.ByteBuffer;
import java.util.Random;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;

public class test{

  public int run() throws IOException{
    byte[] data = new byte[ 1024 * 1024 * 32 ];
    Random rnd = new Random();
    ByteBuffer wrapBuffer = ByteBuffer.wrap( data );
    for( int i = 0 ; i < ( 1024 * 1024 * 8 ) ; i++ ){
      wrapBuffer.putInt( rnd.nextInt( 1000 ) );
    }
    ByteArrayOutputStream bout = new ByteArrayOutputStream();
    GZIPOutputStream out = new GZIPOutputStream( bout );
    out.write( data );
    out.close();
    byte[] gzipData = bout.toByteArray();

    byte[] buf = new byte[ 1024 * 1024 * 32 ];
    for( int bufferSize = 1024 * 8 ; bufferSize <= 1024 * 1024 * 32 ; bufferSize = bufferSize * 2 ){
      long total = 0;
      for( int i = 0 ; i < 20 ; i++ ){
        long start = System.currentTimeMillis();
        GZIPInputStream in = new GZIPInputStream( new ByteArrayInputStream( gzipData ) , bufferSize );
        while( 0 < in.read( buf , 0 , buf.length ) ){
        }
        total += System.currentTimeMillis() - start;
      }
      System.out.println( String.format( "%dKB,%d" , ( bufferSize / 1024 ) , ( total / 20 ) ) );
    }

    return 0;
  }

  public static void main( final String[] args ) throws IOException{
    System.exit( new test().run() );
  }

}

結果

f:id:koijima_proto:20180126224408p:plain

64KB-1MBまでは大体同じくらいとなる。

その後は大きくしていくにつれて処理時間が増える。

BufferSize 処理時間(msec)
8KB 165
16KB 155
32KB 149
64KB 146
128KB 145
256KB 146
512KB 145
1024KB 145
2048KB 149
4096KB 150
8192KB 155
16384KB 157
32768KB 165

バッファサイズを大きくした時にはgcが多発しているので、startのタイムスタンプを取得する前に「Runtime.getRuntime().gc()」を実行して gc を除いた時間も計測。

BufferSize 処理時間(msec)
8KB 165
16KB 155
32KB 149
64KB 146
128KB 145
256KB 145
512KB 145
1024KB 145
2048KB 148
4096KB 149
8192KB 149
16384KB 150
32768KB 152

それでも2MBあたりを過ぎてから処理時間は遅くなるのはヒープの確保にかかる時間が増大したためだと思われる。

なので、64KB-256KBくらいに設定しておくのが良いと思われます。

まとめ

デフォルトでも大して問題ないが、64KB-256KBで調整するのが良い。