3

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

4

2 回答 2

2

在我的机器上,3.8 GHz i7 和 SSD

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 总是使用大端格式,这在英特尔/AMD 系统上要慢得多,这些系统本身就是小端格式。

您可以将字节顺序设为大端,这会减慢速度,或者您可以使用本机排序(DataInput/OutputStream 仅支持大端)

于 2012-04-12T18:47:26.357 回答
1

我正在使用 16GB 内存和 2.13 GhZ [CPU] 运行它的服务器

我怀疑这个问题与您的 Java 代码有关。

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

我会做两件事:

  1. 仔细检查您实际上是在写入本地磁盘,而不是网络共享。请记住,在某些环境中,主目录是 NFS 挂载。
  2. 请您的系统管理员查看机器,找出磁盘如此缓慢的原因。如果我站在他们的立场上,我会先检查日志并运行一些基准测试(例如使用Bonnie++)。
于 2012-04-12T16:46:13.553 回答