茫然的猴子 · BufferedReader (Java ...· 2 周前 · |
刚失恋的投影仪 · 为什么BufferedReader读取()比 ...· 2 周前 · |
酒量小的手电筒 · 西南电力设计院50年发展纪实_国史网· 2 周前 · |
体贴的扁豆 · ֣����ź���ϵͳ������3�棩� ...· 3 周前 · |
纯真的羽毛球 · Apps脚本中的日期格式 - ...· 1 月前 · |
腼腆的蚂蚁 · 对话 Momenta ...· 3 月前 · |
果断的鸵鸟 · 2023投影仪家用性价比排行榜,2023高性 ...· 4 月前 · |
我需要一次读取一个文件,并使用来自
read()
的
BufferedReader
方法。*
我发现
read()
比
readLine()
慢了大约10倍。这是意料之中吗?还是我做错什么了?
以下是Java 7的基准测试。输入测试文件有大约500万行和2.54亿个字符(~242 MB) **
read()
方法大约需要7000 ms才能读取所有字符:
@Test
public void testRead() throws IOException, UnindexableFastaFileException{
BufferedReader fa= new BufferedReader(new FileReader(new File("chr1.fa")));
long t0= System.currentTimeMillis();
int c;
while( (c = fa.read()) != -1 ){
long t1= System.currentTimeMillis();
System.err.println(t1-t0); // ~ 7000 ms
}
readLine()
方法只需要大约700 ms:
@Test
public void testReadLine() throws IOException{
BufferedReader fa= new BufferedReader(new FileReader(new File("chr1.fa")));
String line;
long t0= System.currentTimeMillis();
while( (line = fa.readLine()) != null ){
long t1= System.currentTimeMillis();
System.err.println(t1-t0); // ~ 700 ms
}
*
实用目的
:我需要知道每一行的长度,包括换行符(
\n
或
\r\n
)和剥去它们之后的行长。我还需要知道一行是否以
>
字符开头。对于给定的文件,这只在程序开始时完成一次。由于
BufferedReader.readLine()
不返回EOL字符,所以我使用
read()
方法。如果有更好的方法,请说。
** gzipped文件在这里, http://hgdownload.cse.ucsc.edu/goldenpath/hg19/chromosomes/chr1.fa.gz 。对于那些可能想知道的人,我正在编写一个类来索引fasta文件。
发布于 2016-12-25 22:32:19
在分析性能时,重要的是在开始之前拥有一个有效的基准。因此,让我们从一个简单的JMH基准开始,它显示了我们在热身之后的预期性能。
我们必须考虑的一件事是,由于现代操作系统喜欢缓存定期访问的文件数据,所以我们需要某种方法来清除测试之间的缓存。在 就这样 上有一个小的实用程序--在Linux上,您应该可以通过将某个伪文件写入某个地方来完成它。
然后,代码看起来如下:
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Mode;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
@BenchmarkMode(Mode.AverageTime)
@Fork(1)
public class IoPerformanceBenchmark {
private static final String FILE_PATH = "test.fa";
@Benchmark
public int readTest() throws IOException, InterruptedException {
clearFileCaches();
int result = 0;
try (BufferedReader reader = new BufferedReader(new FileReader(FILE_PATH))) {
int value;
while ((value = reader.read()) != -1) {
result += value;
return result;
@Benchmark
public int readLineTest() throws IOException, InterruptedException {
clearFileCaches();
int result = 0;
try (BufferedReader reader = new BufferedReader(new FileReader(FILE_PATH))) {
String line;
while ((line = reader.readLine()) != null) {
result += line.chars().sum();
return result;
private void clearFileCaches() throws IOException, InterruptedException {
ProcessBuilder pb = new ProcessBuilder("EmptyStandbyList.exe", "standbylist");
pb.inheritIO();
pb.start().waitFor();
}
如果我们用
chcp 65001 # set codepage to utf-8
mvn clean install; java "-Dfile.encoding=UTF-8" -server -jar .\target\benchmarks.jar
我们得到了以下结果(为我清除缓存需要大约2秒,而我正在HDD上运行,这就是为什么它比您慢得多的原因):
Benchmark Mode Cnt Score Error Units
IoPerformanceBenchmark.readLineTest avgt 20 3.749 ± 0.039 s/op
IoPerformanceBenchmark.readTest avgt 20 3.745 ± 0.023 s/op
惊喜吧!正如预期的那样,在JVM进入稳定模式后,这里根本没有性能差异。但是在readCharTest方法中有一个异常值:
# Warmup Iteration 1: 6.186 s/op
# Warmup Iteration 2: 3.744 s/op
这正是你所看到的问题。我能想到的最可能的原因是,OSR在这里做得不好,或者JIT运行得太晚了,无法在第一次迭代中起作用。
取决于您的用例,这可能是一个大问题或可忽略不计的问题(如果您正在读取1000个文件,这将无关紧要,如果您只读取一个,这是一个问题)。
解决这样的问题并不容易,也没有一般的解决办法,尽管有办法解决。一个简单的测试是使用
-Xcomp
选项运行代码,该选项强制HotSpot在第一次调用时编译每个方法。实际上,这样做会导致第一次调用的大延迟消失:
# Warmup Iteration 1: 3.965 s/op
# Warmup Iteration 2: 3.753 s/op
可能的解决方案
现在我们已经很好地了解了实际的问题是什么(我的猜测仍然是那些锁既没有合并,也没有使用高效的有偏锁实现),解决方案非常简单:减少函数调用的数量(因此,如果没有上面的所有内容,我们就可以得到这个解决方案,但是能够很好地控制这个问题,并且可能有一个解决方案不需要修改太多的代码)。
下面的代码运行速度比其他两种方法都快--您可以使用数组大小,但这并不重要(可能是因为与其他方法相反,
read(char[])
不需要获得锁,因此每次调用的成本都较低)。
private static final int BUFFER_SIZE = 256;
private char[] arr = new char[BUFFER_SIZE];
@Benchmark
public int readArrayTest() throws IOException, InterruptedException {
clearFileCaches();
int result = 0;
try (BufferedReader reader = new BufferedReader(new FileReader(FILE_PATH))) {
int charsRead;
while ((charsRead = reader.read(arr)) != -1) {
for (int i = 0; i < charsRead; i++) {
result += arr[i];
return result;
}
这很可能在性能上足够好,但是如果您想进一步提高性能,使用 文件映射 可能会(在这种情况下不指望有太大的改进,但是如果您知道您的文本总是ASCII,您可以进一步优化)进一步提高性能。
发布于 2016-12-26 19:06:04
因此,这是我自己问题的实际答案:不要使用
BufferedReader.read()
,使用
FileChannel
。(很明显,我没有回答我为什么要写这个标题)。下面是快速而肮脏的基准,希望其他人会发现它有用:
@Test
public void testFileChannel() throws IOException{
FileChannel fileChannel = FileChannel.open(Paths.get("chr1.fa"));
long n= 0;
int noOfBytesRead = 0;
long t0= System.nanoTime();
while(noOfBytesRead != -1){
ByteBuffer buffer = ByteBuffer.allocate(10000);
noOfBytesRead = fileChannel.read(buffer);
buffer.flip();
while ( buffer.hasRemaining() ) {
char x= (char)buffer.get();
酒量小的手电筒 · 西南电力设计院50年发展纪实_国史网 2 周前 |