float
たとえば、それぞれの平均を計算するなど、大きなベクトルの大きなセットに対して計算を行っているとします。
public static float avg(float[] data, int offset, int length) {
float sum = 0;
for (int i = offset; i < offset + length; i++) {
sum += data[i];
}
return sum / length;
}
すべてのベクトルをメモリ内に保存している場合はfloat[]
、次のようにループを実装できます。
float[] data; // <-- vectors here
float sum = 0;
for (int i = 0; i < nVectors; i++) {
sum += avg(data, i * vectorSize, vectorSize);
}
ベクトルが代わりにファイルに保存されている場合、OSがすべてをキャッシュしたら、理論的には、最初のソリューションと同じくらい高速にメモリマッピングを行う必要があります。
RandomAccessFile file; // <-- vectors here
MappedByteBuffer buffer = file.getChannel().map(READ_WRITE, 0, 4*data.length);
FloatBuffer floatBuffer = buffer.asFloatBuffer();
buffer.load(); // <-- this forces the OS to cache the file
float[] vector = new float[vectorSize];
float sum = 0;
for (int i = 0; i < nVectors; i++) {
floatBuffer.get(vector);
sum += avg(vector, 0, vector.length);
}
ただし、私のテストでは、メモリマップドバージョンはメモリ内バージョンよりも約5倍遅いことが示されています。私はそれFloatBuffer.get(float[])
がメモリをコピーしていることを知っています、そしてそれが減速の理由だと思います。もっと速くなることはできますか?メモリのコピーをまったく回避し、OSのバッファからデータを取得する方法はありますか?
試してみたい場合に備えて、この要点に完全なベンチマークをアップロードしました。
$ java -Xmx1024m ArrayVsMMap 100 100000 100
編集:
結局、このシナリオで私が得た最高のものはMappedByteBuffer
、通常のシナリオを使用するよりもfloat[]
35%も遅くなります。これまでの秘訣は次のとおりです。
- 変換を回避するには、ネイティブバイトオーダーを使用します。
buffer.order(ByteOrder.nativeOrder())
- を使用
MappedByteBuffer
してラップFloatBuffer
buffer.asFloatBuffer()
floatBuffer.get(int index)
バルクバージョンの代わりにシンプルバージョンを使用すると、メモリのコピーが回避されます。
この要点で新しいベンチマークと結果を確認できます。
1.35のスローダウンは5の1つよりもはるかに優れていますが、それでも1にはほど遠いです。おそらくまだ何かが足りないか、JVMで改善する必要があるものです。