我尝试了使用NIO的以下代码:
FileOutputStream s = new FileOutputStream(filename)
Channel c = s.getChannel()
while(xyz)
c.write(buffer)
c.force(true)
s.getFD().sync()
c.close()
我认为c.force(true)和s.getFD().sync()应该足够了,因为force的文档说明如下:
强制将此通道文件的任何更新写入包含它的存储设备。
如果此通道文件驻留在本地存储设备上,则当此方法返回时,保证自创建此通道以来或自上次调用此方法以来对文件所做的所有更改都已写入该设备。这对于确保在系统崩溃时不会丢失关键信息非常有用。 sync的文档说明如下:
强制所有系统缓冲区与底层设备同步。此方法在所有已修改的数据和此FileDescriptor的属性已写入相关设备后返回。特别是,如果此FileDescriptor引用物理存储介质(例如文件系统中的文件),则只有在与此FileDescriptor关联的所有内存中修改的缓冲区副本都被写入物理介质后,sync才会返回。sync适用于需要将物理存储(如文件)置于已知状态的代码。
这两个调用足够了吗?我猜它们不够。
背景:我使用C / Java进行小型性能比较(2 GB,顺序写入),Java版本比C版本快两倍,并且可能比硬件更快(单个HD上的120 MB / s)。我还尝试使用Runtime.getRuntime()。exec(“sync”)执行命令行工具sync,但这并没有改变行为。
导致70 MB / s的C代码是(使用低级API(open,write,close)并不会改变太多):
FILE* fp = fopen(filename, "w");
while(xyz) {
fwrite(buffer, 1, BLOCK_SIZE, fp);
}
fflush(fp);
fclose(fp);
sync();
没有对sync的最后一次调用时,我得到了不切实际的值(超过1 GB即主存储性能)。
为什么C和Java之间有这么大的差异? 有两种可能性:在Java中,我没有正确地同步数据,或者C代码出于某些原因是次优的。
更新: 我使用“strace -cfT cmd”运行了strace。以下是结果:
C(低级API): MB / s 67.389782
%时间秒usecs / call calls errors syscall ------ ----------- ----------- --------- --------- ---------------- 87.21 0.200012 200012 1 fdatasync 11.05 0.025345 1 32772 write 1.74 0.004000 4000 1 sync
C(高级API): MB / s 61.796458
%时间秒usecs / call calls errors syscall ------ ----------- ----------- --------- --------- ---------------- 73.19 0.144009 144009 1 sync 26.81 0.052739 1 65539 write Java (1.6 SUN JRE, java.io API): MB/s 128.6755466197537
% 时间 秒数 每次使用微秒 调用次数 错误系统调用 ------ ----------- ----------- --------- --------- ---------------- 80.07 105.387609 3215 32776 写入 2.58 3.390060 3201 1059 读取 0.62 0.815251 815251 1 同步文件
Java (1.6 SUN JRE, java.nio API): MB/s 127.45830221558376
5.52 0.980061 490031 2 同步文件 1.60 0.284752 9 32774 写入 0.00 0.000000 0 80 关闭
时间值似乎只是系统时间,因此毫无意义。
更新2: 我换了另一台服务器,重新启动,并使用新格式化的ext3。现在Java和C之间只有4%的差异。我不知道出了什么问题。有时候事情很奇怪。在写这个问题之前,我应该尝试在另一个系统上进行测量。抱歉。
更新3: 总结答案如下:
- 对于Java NIO,请使用c.force(true)后跟s.getFD()。sync(),对于Java的流API,请使用s.flush()和s.getFD()。sync()。对于C中的高级API,请不要忘记同步。fflush将数据提交给操作系统,但不会将您的数据带到块设备。
- 使用strace分析命令执行的系统调用
- 在发布问题之前,请交叉检查您的结果。