使用以下代码作为基准,系统可以在几分之一秒内将10,000行写入磁盘:
void withSync() {
int f = open( "/tmp/t8" , O_RDWR | O_CREAT );
lseek (f, 0, SEEK_SET );
int records = 10*1000;
clock_t ustart = clock();
for(int i = 0; i < records; i++) {
write(f, "012345678901234567890123456789" , 30);
fsync(f);
}
clock_t uend = clock();
close (f);
printf(" sync() seconds:%lf writes per second:%lf\n", ((double)(uend-ustart))/(CLOCKS_PER_SEC), ((double)records)/((double)(uend-ustart))/(CLOCKS_PER_SEC));
}
在上面的代码中,可以在几分之一秒内写入10,000个记录并刷新到磁盘,输出如下:
sync() seconds:0.006268 writes per second:0.000002
在Java版本中,写入10,000条记录需要4秒钟。这只是Java的限制,还是我错过了什么?
public void testFileChannel() throws IOException {
RandomAccessFile raf = new RandomAccessFile(new File("/tmp/t5"),"rw");
FileChannel c = raf.getChannel();
c.force(true);
ByteBuffer b = ByteBuffer.allocateDirect(64*1024);
long s = System.currentTimeMillis();
for(int i=0;i<10000;i++){
b.clear();
b.put("012345678901234567890123456789".getBytes());
b.flip();
c.write(b);
c.force(false);
}
long e=System.currentTimeMillis();
raf.close();
System.out.println("With flush "+(e-s));
}
返回:
With flush 4263
请帮助我了解在Java中将记录写入磁盘的正确/最快方法。
注意:我正在将RandomAccessFile
类与ByteBuffer
结合使用,因此最终我们需要对此文件进行随机读/写访问。
答案 0 :(得分:5)
实际上,我很惊讶测试速度并不慢。强制行为取决于操作系统,但广义上它会强制数据到磁盘。如果你有一个SSD,你可以达到每秒40K的写入速度,但是使用硬盘驱动器则不会。在C示例中,它显然没有将数据提交到磁盘,因为即使最快的SSD也不能执行超过235K的IOPS(制造商保证它不会比这更快:D)
如果您每次都需要提交到磁盘的数据,您可能会觉得它很慢并且完全取决于硬件的速度。如果您只需要刷新到操作系统的数据,如果程序崩溃但操作系统没有,您将不会丢失任何数据,您可以无需强制写入数据。更快的选择是使用内存映射文件。这将为您提供随机访问,而无需为每条记录进行系统调用。
我有一个库Java Chronicle,它可以每秒读取/写入5-20百万条记录,文本或二进制格式的延迟为80 ns,可以随机访问,并且可以在进程之间共享。这只能很快地工作,因为它不会在每条记录上将数据提交到磁盘,但您可以测试,如果JVM在任何时候崩溃,则没有写入编年史的数据丢失。
答案 1 :(得分:1)
此代码与您在C中编写的代码更相似。在我的机器上只需5毫秒。如果每次写入后确实需要刷新,则需要大约60毫秒。您的原始代码在此计算机上花了大约11秒钟。顺便说一下,关闭输出流也会刷新。
public static void testFileOutputStream() throws IOException {
OutputStream os = new BufferedOutputStream( new FileOutputStream( "/tmp/fos" ) );
byte[] bytes = "012345678901234567890123456789".getBytes();
long s = System.nanoTime();
for ( int i = 0; i < 10000; i++ ) {
os.write( bytes );
}
long e = System.nanoTime();
os.close();
System.out.println( "outputstream " + ( e - s ) / 1e6 );
}
答案 2 :(得分:0)
Java相当于fputs是file.write("012345678901234567890123456789");
,你调用了4个函数而C中只有1个,延迟似乎很明显
答案 3 :(得分:0)
我认为这与您的C版本最相似。我认为你的java示例中的直接缓冲区导致比C版本更多的缓冲区副本。我的(旧)盒子大约需要2.2秒。
public static void testFileChannelSimple() throws IOException {
RandomAccessFile raf = new RandomAccessFile(new File("/tmp/t5"),"rw");
FileChannel c = raf.getChannel();
c.force(true);
byte[] bytes = "012345678901234567890123456789".getBytes();
long s = System.currentTimeMillis();
for(int i=0;i<10000;i++){
raf.write(bytes);
c.force(true);
}
long e=System.currentTimeMillis();
raf.close();
System.out.println("With flush "+(e-s));
}