我有两个数组(int和long),其中包含数百万个条目。到现在为止,我一直在使用DataOutputStream并使用一个长缓冲区来完成此操作,因此磁盘I / O成本降低了(Nio或多或少与我拥有巨大的缓冲区相同,因此I / O访问成本较低)
DataOutputStream dos = new DataOutputStream(new BufferedOutputStream(new FileOutputStream("abc.txt"),1024*1024*100));
for(int i = 0 ; i < 220000000 ; i++){
long l = longarray[i];
dos.writeLong(l);
}
但是,这需要几秒钟(超过5分钟)。实际上,我要批量刷新(某种主内存到磁盘内存的映射)。为此,我在here和here中找到了一种不错的方法。但是,无法理解如何在我的javac中使用它。有人可以帮我解决这个问题吗?
最佳答案
在我的机器上,配备SSD的3.8 GHz i7
DataOutputStream dos = new DataOutputStream(new BufferedOutputStream(new FileOutputStream("abc.txt"), 32 * 1024));
long start = System.nanoTime();
final int count = 220000000;
for (int i = 0; i < count; i++) {
long l = i;
dos.writeLong(l);
}
dos.close();
long time = System.nanoTime() - start;
System.out.printf("Took %.3f seconds to write %,d longs%n",
time / 1e9, count);
版画
Took 11.706 seconds to write 220,000,000 longs
使用内存映射文件
final int count = 220000000;
final FileChannel channel = new RandomAccessFile("abc.txt", "rw").getChannel();
MappedByteBuffer mbb = channel.map(FileChannel.MapMode.READ_WRITE, 0, count * 8);
mbb.order(ByteOrder.nativeOrder());
long start = System.nanoTime();
for (int i = 0; i < count; i++) {
long l = i;
mbb.putLong(l);
}
channel.close();
long time = System.nanoTime() - start;
System.out.printf("Took %.3f seconds to write %,d longs%n",
time / 1e9, count);
// Only works on Sun/HotSpot/OpenJDK to deallocate buffer.
((DirectBuffer) mbb).cleaner().clean();
final FileChannel channel2 = new RandomAccessFile("abc.txt", "r").getChannel();
MappedByteBuffer mbb2 = channel2.map(FileChannel.MapMode.READ_ONLY, 0, channel2.size());
mbb2.order(ByteOrder.nativeOrder());
assert mbb2.remaining() == count * 8;
long start2 = System.nanoTime();
for (int i = 0; i < count; i++) {
long l = mbb2.getLong();
if (i != l)
throw new AssertionError("Expected "+i+" but got "+l);
}
channel.close();
long time2 = System.nanoTime() - start2;
System.out.printf("Took %.3f seconds to read %,d longs%n",
time2 / 1e9, count);
// Only works on Sun/HotSpot/OpenJDK to deallocate buffer.
((DirectBuffer) mbb2).cleaner().clean();
在我的3.8 GHz i7上打印。
Took 0.568 seconds to write 220,000,000 longs
在较慢的机器上打印
Took 1.180 seconds to write 220,000,000 longs
Took 0.990 seconds to read 220,000,000 longs
还有其他不创建它的方法吗?因为我的主内存上已经有该阵列,并且我不能分配超过500 MB的内存来做到这一点?
这不会使用少于1 KB的堆。如果查看此调用之前和之后使用了多少内存,通常将看不到有任何增加。
另一件事,这是否意味着有效加载也意味着MappedByteBuffer?
以我的经验,使用内存映射文件是最快的,因为可以减少系统调用和复制到内存中的次数。
因为在某些文章中我发现read(buffer)可以提供更好的加载性能。 (我检查了一个更快的2.2亿个int数组-float数组读取了5秒)
我想读那篇文章,因为我从未见过。
另一个问题:readLong从代码输出文件读取时出现错误
证明的部分性能是以本地字节顺序存储值。 writeLong / readLong始终使用大字节序格式,这在本机上很少为字节序格式的Intel / AMD系统上要慢得多。
您可以将字节顺序设置为big-endian,这会减慢其速度,也可以使用本机顺序(DataInput / OutputStream仅支持big-endian)。