简体   繁体   English

为什么RandomAccessFile writeLong用多次写调用实现?

[英]Why is RandomAccessFile writeLong implemented with multiple write calls?

While profiling an application I noticed that RandomAccessFile.writeLong was taking a lot of time. 在分析应用程序时,我注意到RandomAccessFile.writeLong花了很多时间。

I checked the code for this method, and it involves eight calls of the native method write. 我检查了这个方法的代码,它涉及8次本机方法写入调用。 I wrote an alternative implementation for writeLong using a byte[]. 我使用byte []为writeLong编写了一个替代实现。 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(): 使用writeLong():
Average time for invocation: 91 ms 平均调用时间:91毫秒

Using write(byte[]): 使用write(byte []):
Average time for invocation: 11 ms 平均调用时间:11毫秒

Test run on a Linux machine with a Intel(R) CPU T2300 @ 1.66GHz 在具有Intel(R)CPU T2300 @ 1.66GHz的Linux机器上进行测试

Since native calls have some performance penalty, why is writeLong implemented that way? 由于本机调用会有一些性能损失,为什么writeLong会以这种方式实现? I know the question should be made to the Sun guys, but I hope someone in here has some hints. 我知道应该向太阳队员提出这个问题,但我希望这里的人有一些提示。

Thank you. 谢谢。

It appears that the RandomAccessFile.writeLong() doesn't minimise the number of calls to the OS. RandomAccessFile.writeLong()似乎不会最小化对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. 通过使用“rwd”而不是“rw”来显着增加成本,这应该足以表明它本身不需要花费时间。 (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. 没有磁盘缓存,我希望每个提交的写入对于5400 RPM磁盘大约需要11毫秒,即60000毫秒/ 5400 => 11毫秒。

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). writeLong()本机实现可能需要每个体系结构的版本,以处理字节排序(JNI将转换为平台字节顺序)。 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. 至于为什么他们在Java端没有转换为数组,我怀疑这是因为害怕垃圾收集。 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". 我猜想RandomAccessFile自1.1以来已经发生了微小的变化,直到1.3,垃圾收集才开始使小对象分配“免费”。

But , there's an alternative to RandomAccessFile : take a look at MappedByteBuffer 但是 ,还有RandomAccessFile的替代方案:看看MappedByteBuffer


Edit: I have a machine with JDK 1.2.2, and this method has not changed since then. 编辑:我有一台JDK 1.2.2的机器,从那时起这个方法没有改变。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM