为什么 RandomAccessFile writeLong 是通过多次写入调用来实现的?

发布于 2024-11-02 19:47:50 字数 997 浏览 5 评论 0原文

在分析应用程序时,我注意到 RandomAccessFile.writeLong 花费了大量时间。

我查了这个方法的代码,涉及到native方法write的8次调用。 我使用 byte[] 编写了 writeLong 的替代实现。像这样的事情:

RandomAccessFile randomAccessFile = new RandomAccessFile("out.dat", "rwd");
...
byte[] aux = new byte[8];
aux[0] = (byte) ((l >>> 56) & 0xFF);
aux[1] = (byte) ((l >>> 48) & 0xFF);
aux[2] = (byte) ((l >>> 40) & 0xFF);
aux[3] = (byte) ((l >>> 32) & 0xFF);
aux[4] = (byte) ((l >>> 24) & 0xFF);
aux[5] = (byte) ((l >>> 16) & 0xFF);
aux[6] = (byte) ((l >>> 8) & 0xFF);
aux[7] = (byte) ((l >>> 0) & 0xFF);
randomAccessFile.write(aux);

我做了一个小基准测试并得到了这些结果:

使用 writeLong():
平均调用时间:91 ms

使用 write(byte[]):
平均调用时间:11 ms

在具有 Intel(R) CPU T2300 @ 1.66GHz 的 Linux 机器上运行测试

由于本机调用会产生一些性能损失,为什么 writeLong 会以这种方式实现? 我知道这个问题应该问太阳队的人,但我希望这里有人能提供一些提示。

谢谢。

While profiling an application I noticed that RandomAccessFile.writeLong was taking a lot of time.

I checked the code for this method, and it involves eight calls of the native method write.
I wrote an alternative implementation for writeLong using a byte[]. Something like this:

RandomAccessFile randomAccessFile = new RandomAccessFile("out.dat", "rwd");
...
byte[] aux = new byte[8];
aux[0] = (byte) ((l >>> 56) & 0xFF);
aux[1] = (byte) ((l >>> 48) & 0xFF);
aux[2] = (byte) ((l >>> 40) & 0xFF);
aux[3] = (byte) ((l >>> 32) & 0xFF);
aux[4] = (byte) ((l >>> 24) & 0xFF);
aux[5] = (byte) ((l >>> 16) & 0xFF);
aux[6] = (byte) ((l >>> 8) & 0xFF);
aux[7] = (byte) ((l >>> 0) & 0xFF);
randomAccessFile.write(aux);

I made a small benchmark and got these results:

Using writeLong():
Average time for invocation: 91 ms

Using write(byte[]):
Average time for invocation: 11 ms

Test run on a Linux machine with a Intel(R) CPU T2300 @ 1.66GHz

Since native calls have some performance penalty, why is writeLong implemented that way?
I know the question should be made to the Sun guys, but I hope someone in here has some hints.

Thank you.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

〃安静 2024-11-09 19:47:51

看来 RandomAccessFile.writeLong() 并没有最大限度地减少对操作系统的调用次数。通过使用“rwd”而不是“rw”,成本会急剧增加,这应该足以表明花费时间的不是调用本身。 (事实上​​,操作系统试图将每次写入提交到磁盘,而磁盘旋转得如此之快)

{
    RandomAccessFile raf = new RandomAccessFile("test.dat", "rwd");
    int longCount = 10000;
    long start = System.nanoTime();
    for (long l = 0; l < longCount; l++)
        raf.writeLong(l);
    long time = System.nanoTime() - start;
    System.out.printf("writeLong() took %,d us on average%n", time / longCount / 1000);
    raf.close();
}
{
    RandomAccessFile raf = new RandomAccessFile("test2.dat", "rwd");
    int longCount = 10000;
    long start = System.nanoTime();
    byte[] aux = new byte[8];
    for (long l = 0; l < longCount; l++) {
        aux[0] = (byte) (l >>> 56);
        aux[1] = (byte) (l >>> 48);
        aux[2] = (byte) (l >>> 40);
        aux[3] = (byte) (l >>> 32);
        aux[4] = (byte) (l >>> 24);
        aux[5] = (byte) (l >>> 16);
        aux[6] = (byte) (l >>> 8);
        aux[7] = (byte) l;
        raf.write(aux);
    }
    long time = System.nanoTime() - start;
    System.out.printf("write byte[8] took %,d us on average%n", time / longCount / 1000);
    raf.close();
}

打印

writeLong() took 2,321 us on average
write byte[8] took 576 us on average

在我看来,您没有磁盘写入缓存。如果没有磁盘缓存,对于 5400 RPM 磁盘,我预计每次提交的写入大约需要 11 毫秒,即 60000 毫秒/5400 => 11 毫秒

It appears that the RandomAccessFile.writeLong() doesn't minimise the number of calls to the OS. The cost increases dramatically by using "rwd" instead of "rw" which should be enough to indicate its not the calls themselves which cost the time. (its the fact the OS is try to commit every write to disk and the disk only spins so fast)

{
    RandomAccessFile raf = new RandomAccessFile("test.dat", "rwd");
    int longCount = 10000;
    long start = System.nanoTime();
    for (long l = 0; l < longCount; l++)
        raf.writeLong(l);
    long time = System.nanoTime() - start;
    System.out.printf("writeLong() took %,d us on average%n", time / longCount / 1000);
    raf.close();
}
{
    RandomAccessFile raf = new RandomAccessFile("test2.dat", "rwd");
    int longCount = 10000;
    long start = System.nanoTime();
    byte[] aux = new byte[8];
    for (long l = 0; l < longCount; l++) {
        aux[0] = (byte) (l >>> 56);
        aux[1] = (byte) (l >>> 48);
        aux[2] = (byte) (l >>> 40);
        aux[3] = (byte) (l >>> 32);
        aux[4] = (byte) (l >>> 24);
        aux[5] = (byte) (l >>> 16);
        aux[6] = (byte) (l >>> 8);
        aux[7] = (byte) l;
        raf.write(aux);
    }
    long time = System.nanoTime() - start;
    System.out.printf("write byte[8] took %,d us on average%n", time / longCount / 1000);
    raf.close();
}

prints

writeLong() took 2,321 us on average
write byte[8] took 576 us on average

It would appear to me that you have no disk write caching on. Without disk caching, I would expect each commited write to take about 11 ms for a 5400 RPM disk ie 60000 ms/5400 => 11 ms.

长安忆 2024-11-09 19:47:51

我会投票支持懒惰,或者(更仁慈地)不考虑后果。

writeLong() 的本机实现可能需要每个架构的版本来处理字节顺序(JNI 将转换为平台字节顺序)。通过将翻译保留在“跨平台”层,开发人员简化了移植工作。

至于为什么它们在 Java 端没有转换为数组,我怀疑这是由于担心垃圾收集。我猜想 RandomAccessFile 自 1.1 以来发生了最小的变化,直到 1.3 垃圾收集才开始使小对象分配变得“免费”。

但是,有一个替代 RandomAccessFile 的方法:看看 MappedByteBuffer


编辑:我有一台装有 JDK 1.2.2 的机器,这个方法有从那时起就没有改变。

I would vote for laziness, or (being more charitable) not thinking about the consequences.

A native implementation of writeLong() would potentially require versions for every architecture, to deal with byte ordering (JNI will convert to platform byte order). By keeping the translation in the "cross-platform" layer, the developers simplified the job of porting.

As to why they didn't convert to an array while on the Java side, I suspect that was due to fear of garbage collection. I would guess that RandomAccessFile has changed minimally since 1.1, and it wasn't until 1.3 that garbage collection started to make small object allocations "free".

But, there's an alternative to RandomAccessFile: take a look at MappedByteBuffer


Edit: I have a machine with JDK 1.2.2, and this method has not changed since then.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文