Java阵列批量刷新磁盘

时间:2012-04-12 16:08:07

标签: java arrays

我有两个数组(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分钟)。实际上,我想要批量刷新(某种主内存到磁盘内存映射)。为此,我在herehere找到了一个很好的方法。但是,无法理解如何在我的javac中使用它。任何人都可以帮我解决这个或其他任何方式做得很好吗?

2 个答案:

答案 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代码有什么关系。

您的文件系统似乎非常慢(至少比本地磁盘所期望的慢十倍)。

我会做两件事:

  1. 仔细检查您是否实际写入本地磁盘,而不是写入网络共享。请记住,在某些环境中,主目录是NFS挂载。
  2. 让您的系统管理员查看机器,找出磁盘速度如此之慢的原因。如果我不喜欢,我首先检查日志并运行一些基准测试(例如使用Bonnie++)。