내가 버퍼의 크기를 바꿀 때 BufferedReader
에서 설명 할 수없는 이상한 결과가 나타납니다.BufferedReader에서 * 큰 * 버퍼로 성능이 저하되는 이유는 무엇입니까?
버퍼의 크기를 늘리면 성능이 점차 향상 될 것이고, 반환 설정은 상당히 빨리 줄어들 것이며 그 이후의 성능은 다소 평탄해질 것이라고 강력히 예상했습니다. 그러나 버퍼 크기가 매우 작 으면 버퍼 크기를 늘리면 느려질 것입니다..
여기에 최소한의 예가 나와 있습니다. 모든 작업은 텍스트 파일을 통해 실행되며 줄 길이의 합을 계산합니다.
public int traverseFile(int bufSize) throws IOException {
BufferedReader reader = new BufferedReader(new FileReader("words16"), bufSize*1024);
String line;
int total=0;
while ((line=reader.readLine())!=null)
total+=line.length();
reader.close();
return total;
}
다양한 버퍼 크기로 벤치마킹을 시도했는데 결과가 다소 이상합니다. 최대 약 256KB, 성능이 향상됩니다. 그 시점 이후에는 더 악화됩니다. 나는 그것이 버퍼를 할당하는 데 걸리는 단지 시간인지 궁금 그래서 나는 (아래 두 번째 줄 참조) 항상 같은 메모리 총량을 할당하기 위해 뭔가를 추가하는 시도 :
public int traverseFile(int bufSize) throws IOException {
byte[] pad = new byte[(65536-bufSize)*1024];
BufferedReader reader = new BufferedReader(new FileReader("words16"), bufSize*1024);
String line;
int total=0;
while ((line=reader.readLine())!=null)
total+=line.length();
reader.close();
return total;
}
이 더 확률하지 않습니다. 두 개의 다른 컴퓨터에서 여전히 동일한 결과를 얻고 있습니다. 전체 결과는 다음과 같습니다.
Benchmark Mode Samples Score Error Units
j.t.BufferSizeBenchmark.traverse_test1_4K avgt 100 363.987 ± 1.901 ms/op
j.t.BufferSizeBenchmark.traverse_test2_16K avgt 100 356.551 ± 0.330 ms/op
j.t.BufferSizeBenchmark.traverse_test3_64K avgt 100 353.462 ± 0.557 ms/op
j.t.BufferSizeBenchmark.traverse_test4_256K avgt 100 350.822 ± 0.562 ms/op
j.t.BufferSizeBenchmark.traverse_test5_1024K avgt 100 356.949 ± 0.338 ms/op
j.t.BufferSizeBenchmark.traverse_test6_4096K avgt 100 358.377 ± 0.388 ms/op
j.t.BufferSizeBenchmark.traverse_test7_16384K avgt 100 367.890 ± 0.393 ms/op
j.t.BufferSizeBenchmark.traverse_test8_65536K avgt 100 363.271 ± 0.228 ms/op
자세히 알 수 있듯이 스위트 스폿은 약 256KB입니다. 차이는 크지 않지만 확실히 측정 가능합니다.
내가 생각할 수있는 것은 이것이 메모리 캐시와 관련이 있다는 것입니다. 그것은 쓰여지고있는 RAM이 읽히고있는 RAM에서 멀리 떨어져 있기 때문입니까? 그러나 그것이 순환 버퍼라면, 나는 그것이 사실이라는 것을 확신하지 못합니다 : 쓰여지고있는 것은 읽혀지고있는 것의 배후에있을 것입니다.
words16
파일은 80MB이므로 게시 할 수는 없지만 Fedora의 표준 /usr/share/dict/words
파일은 16 번 이상입니다. 필요한 경우 링크를 게시하는 방법을 찾을 수 있습니다. 내가 버퍼의 크기를 증가 할 때
@OutputTimeUnit(TimeUnit.MILLISECONDS)
@BenchmarkMode(Mode.AverageTime)
@OperationsPerInvocation(1)
@Warmup(iterations = 30, time = 100, timeUnit = TimeUnit.MILLISECONDS)
@Measurement(iterations = 100, time = 10000, timeUnit = TimeUnit.MILLISECONDS)
@State(Scope.Thread)
@Threads(1)
@Fork(1)
public class BufferSizeBenchmark {
public int traverseFile(int bufSize) throws IOException {
byte[] pad = new byte[(65536-bufSize)*1024];
BufferedReader reader = new BufferedReader(new FileReader("words16"), bufSize*1024);
String line;
int total=0;
while ((line=reader.readLine())!=null)
total+=line.length();
reader.close();
return total;
}
@Benchmark
public int traverse_test1_4K() throws IOException {
return traverseFile(4);
}
@Benchmark
public int traverse_test2_16K() throws IOException {
return traverseFile(16);
}
@Benchmark
public int traverse_test3_64K() throws IOException {
return traverseFile(64);
}
@Benchmark
public int traverse_test4_256K() throws IOException {
return traverseFile(256);
}
@Benchmark
public int traverse_test5_1024K() throws IOException {
return traverseFile(1024);
}
@Benchmark
public int traverse_test6_4096K() throws IOException {
return traverseFile(4096);
}
@Benchmark
public int traverse_test7_16384K() throws IOException {
return traverseFile(16384);
}
@Benchmark
public int traverse_test8_65536K() throws IOException {
return traverseFile(65536);
}
public static void main(String[] args) throws RunnerException {
Options opt = new OptionsBuilder()
.include(
".*" + BufferSizeBenchmark.class.getSimpleName() + ".*")
.forks(1).build();
new Runner(opt).run();
}
}
가 왜 나쁜 성능을 얻고있다 :
여기 벤치마킹 코드입니까?