LoginSignup
5

More than 5 years have passed since last update.

DataOutputStreamが遅い

Posted at

DataOutputStreamでfloatのデータを大量に書き込みたい時に、

float[] data = ...;
DataOutputStream out = ...;

for(int i = 0; i < data.length; i++){
    out.writeFloat(data[i]);
}

というように書くのが常套手段ですが、これは非常に遅いです。何故遅いかを調べてみました。

中の実装はどうなっているか

(ざっくり再現したコードで、JDKと全く同じではありません)

floatをintに変換

書き込むためにはバイナリにしなければいけないため、float型の値を同じビットレイアウトで表現されたint型に変換されます。そのためにFloat#floatToIntBits(float)が呼ばれます。

float f = ...;
int v = Float.floatToIntBits(f);

intをbyteに変換

JavaのOutputStreamは原則byteかbyte配列の書き込みしかサポートされていません。そのためintはbyteに変換されます。

float f = ...;
int v = Float.floatToIntBits(f);

byte b1 = (byte) ((v >>> 24) & 0xff);
byte b2 = (byte) ((v >>> 16) & 0xff);
byte b3 = (byte) ((v >>> 8 ) & 0xff);
byte b4 = (byte) ((v >>> 0 ) & 0xff);

書き込む

最後に1byte毎に書き込みます。floatだけではなくintも同じ挙動になります。

float f = ...;
int v = Float.floatToIntBits(f);

byte b1 = (byte) ((v >>> 24) & 0xff);
byte b2 = (byte) ((v >>> 16) & 0xff);
byte b3 = (byte) ((v >>> 8 ) & 0xff);
byte b4 = (byte) ((v >>> 0 ) & 0xff);

out.write(b1);
out.write(b2);
out.write(b3);
out.write(b4);

doubleだと・・・?

doubleやlongの場合も同じようなコードになりますが、OutputStream#write(byte[])が呼び出されるためかなり速いです。

double d = ...;
long v = Double.doubleToLongBits(d);

buffre[0] = (byte) (v >>> 56) & 0xff);
buffre[1] = (byte) (v >>> 48) & 0xff);
buffre[2] = (byte) (v >>> 40) & 0xff);
buffre[3] = (byte) (v >>> 32) & 0xff);
buffre[4] = (byte) (v >>> 24) & 0xff);
buffre[5] = (byte) (v >>> 16) & 0xff);
buffre[6] = (byte) (v >>>  8) & 0xff);
buffre[7] = (byte) (v >>>  0) & 0xff);

out.write(buffer);

何が結局速いのか

自分が実装したコードでは、

  1. intへの変換(Float#floatToRawIntBits(float)を呼び出す)
  2. byte配列に直接書き込む
  3. byte配列の終端に来たらOutputStream#write(byte[])を呼び出す

を行って高速化しました。面倒なのは1byte書き込むことに発生する境界チェックです。

もし書き込むデータが4bytes単位しかなく、バッファ用の配列サイズも4bytesの倍数であれば、floatの書き込み1回ごとに配列の境界チェックができます。

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