为什么 RandomAccessFile writeLong 是通过多次写入调用来实现的?
在分析应用程序时,我注意到 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 msUsing 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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
看来 RandomAccessFile.writeLong() 并没有最大限度地减少对操作系统的调用次数。通过使用“rwd”而不是“rw”,成本会急剧增加,这应该足以表明花费时间的不是调用本身。 (事实上,操作系统试图将每次写入提交到磁盘,而磁盘旋转得如此之快)
打印
在我看来,您没有磁盘写入缓存。如果没有磁盘缓存,对于 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)
prints
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.
我会投票支持懒惰,或者(更仁慈地)不考虑后果。
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 atMappedByteBuffer
Edit: I have a machine with JDK 1.2.2, and this method has not changed since then.