我有两个数组(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中使用它。任何人都可以帮我解决这个或其他任何方式做得很好吗?
答案 0 :(得分:2)
在我的机器上,带有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(缓冲区),这样可以提供更好的加载性能。 (我检查一个,真的更快220万个int数组-float数组读取5秒)
我想阅读那篇文章是因为我从未见过这篇文章。
另一个问题:readLong在从代码输出文件中读取时出错
证明性能的一部分是以本机字节顺序存储值。 writeLong / readLong总是使用big endian格式,这种格式在英特尔/ AMD系统上慢得多,这些系统本身就是小端格式。
你可以使字节顺序big-endian减慢速度,或者你可以使用本机排序(DataInput / OutputStream只支持big endian)
答案 1 :(得分:1)
我正在使用16GB内存和2.13 GhZ [CPU]
运行服务器
我怀疑这个问题与您的Java代码有什么关系。
您的文件系统似乎非常慢(至少比本地磁盘所期望的慢十倍)。
我会做两件事: