인터넷 검색 중에 사용 java.io.File#length()
속도가 느릴 수 있습니다.
FileChannel
있다size()
잘으로 사용할 방법을.
Java에서 파일 크기를 얻는 효율적인 방법이 있습니까?
답변
글쎄, 아래 코드를 사용하여 측정하려고했습니다.
런 = 1 및 반복 = 1의 경우 URL 방법은 대부분 채널이 뒤 따르는 가장 빠릅니다. 나는 이것을 약 10 번 신선한 일시 중지로 실행합니다. 따라서 한 번만 액세스하면 URL을 사용하는 것이 내가 생각할 수있는 가장 빠른 방법입니다.
LENGTH sum: 10626, per Iteration: 10626.0
CHANNEL sum: 5535, per Iteration: 5535.0
URL sum: 660, per Iteration: 660.0
런 = 5 및 반복 = 50의 경우 그림이 다르게 그려집니다.
LENGTH sum: 39496, per Iteration: 157.984
CHANNEL sum: 74261, per Iteration: 297.044
URL sum: 95534, per Iteration: 382.136
파일은 파일 시스템에 대한 호출을 캐싱해야하며 채널과 URL에는 약간의 오버 헤드가 있습니다.
암호:
import java.io.*;
import java.net.*;
import java.util.*;
public enum FileSizeBench {
LENGTH {
@Override
public long getResult() throws Exception {
File me = new File(FileSizeBench.class.getResource(
"FileSizeBench.class").getFile());
return me.length();
}
},
CHANNEL {
@Override
public long getResult() throws Exception {
FileInputStream fis = null;
try {
File me = new File(FileSizeBench.class.getResource(
"FileSizeBench.class").getFile());
fis = new FileInputStream(me);
return fis.getChannel().size();
} finally {
fis.close();
}
}
},
URL {
@Override
public long getResult() throws Exception {
InputStream stream = null;
try {
URL url = FileSizeBench.class
.getResource("FileSizeBench.class");
stream = url.openStream();
return stream.available();
} finally {
stream.close();
}
}
};
public abstract long getResult() throws Exception;
public static void main(String[] args) throws Exception {
int runs = 5;
int iterations = 50;
EnumMap<FileSizeBench, Long> durations = new EnumMap<FileSizeBench, Long>(FileSizeBench.class);
for (int i = 0; i < runs; i++) {
for (FileSizeBench test : values()) {
if (!durations.containsKey(test)) {
durations.put(test, 0l);
}
long duration = testNow(test, iterations);
durations.put(test, durations.get(test) + duration);
// System.out.println(test + " took: " + duration + ", per iteration: " + ((double)duration / (double)iterations));
}
}
for (Map.Entry<FileSizeBench, Long> entry : durations.entrySet()) {
System.out.println();
System.out.println(entry.getKey() + " sum: " + entry.getValue() + ", per Iteration: " + ((double)entry.getValue() / (double)(runs * iterations)));
}
}
private static long testNow(FileSizeBench test, int iterations)
throws Exception {
long result = -1;
long before = System.nanoTime();
for (int i = 0; i < iterations; i++) {
if (result == -1) {
result = test.getResult();
//System.out.println(result);
} else if ((result = test.getResult()) != result) {
throw new Exception("variance detected!");
}
}
return (System.nanoTime() - before) / 1000;
}
}
답변
GHad가 제공 한 벤치 마크는 길이를 얻는 것 외에도 많은 다른 것들 (반사, 인스턴스화 객체 등)을 측정합니다. 우리가 이러한 것들을 제거하려고하면 한 번의 호출에 대해 다음 시간을 마이크로 초 단위로 얻습니다.
반복 당 파일 합계 ___ 19.0, ___ 19.0 반복마다 raf sum ___ 16.0, ___ 16.0 반복 당 채널 합계 __273.0, __ 273.0
100 회 실행 및 10000 회 반복의 경우
반복 당 파일 sum__1767629.0, __ 1.7676290000000001 반복 당 raf sum ___ 881284.0, __ 0.8812840000000001 반복 당 채널 합계 ___ 414286.0, __ 0.414286
100MB 파일의 이름을 인수로 제공하는 다음 수정 된 코드를 실행했습니다.
import java.io.*;
import java.nio.channels.*;
import java.net.*;
import java.util.*;
public class FileSizeBench {
private static File file;
private static FileChannel channel;
private static RandomAccessFile raf;
public static void main(String[] args) throws Exception {
int runs = 1;
int iterations = 1;
file = new File(args[0]);
channel = new FileInputStream(args[0]).getChannel();
raf = new RandomAccessFile(args[0], "r");
HashMap<String, Double> times = new HashMap<String, Double>();
times.put("file", 0.0);
times.put("channel", 0.0);
times.put("raf", 0.0);
long start;
for (int i = 0; i < runs; ++i) {
long l = file.length();
start = System.nanoTime();
for (int j = 0; j < iterations; ++j)
if (l != file.length()) throw new Exception();
times.put("file", times.get("file") + System.nanoTime() - start);
start = System.nanoTime();
for (int j = 0; j < iterations; ++j)
if (l != channel.size()) throw new Exception();
times.put("channel", times.get("channel") + System.nanoTime() - start);
start = System.nanoTime();
for (int j = 0; j < iterations; ++j)
if (l != raf.length()) throw new Exception();
times.put("raf", times.get("raf") + System.nanoTime() - start);
}
for (Map.Entry<String, Double> entry : times.entrySet()) {
System.out.println(
entry.getKey() + " sum: " + 1e-3 * entry.getValue() +
", per Iteration: " + (1e-3 * entry.getValue() / runs / iterations));
}
}
}
답변
이 게시물의 모든 테스트 사례는 테스트 된 각 방법에 대해 동일한 파일에 액세스 할 때 결함이 있습니다. 따라서 테스트 2와 3이 도움이되는 디스크 캐싱 킥. 내 요점을 증명하기 위해 GHAD가 제공 한 테스트 사례를 가져 와서 열거 순서를 변경했으며 그 결과는 다음과 같습니다.
결과를 보면 File.length ()가 실제로 승자라고 생각합니다.
테스트 순서는 출력 순서입니다. 내 컴퓨터에서 걸리는 시간이 실행 시간에 따라 다르지만 처음에는 File.Length ()가 아니고 첫 번째 디스크 액세스가 발생하는 것을 볼 수 있습니다.
---
LENGTH sum: 1163351, per Iteration: 4653.404
CHANNEL sum: 1094598, per Iteration: 4378.392
URL sum: 739691, per Iteration: 2958.764
---
CHANNEL sum: 845804, per Iteration: 3383.216
URL sum: 531334, per Iteration: 2125.336
LENGTH sum: 318413, per Iteration: 1273.652
---
URL sum: 137368, per Iteration: 549.472
LENGTH sum: 18677, per Iteration: 74.708
CHANNEL sum: 142125, per Iteration: 568.5
답변
리소스 대신 절대 경로로 액세스 된 파일을 사용하도록 코드를 수정하면 다른 결과가 나타납니다 (1 실행, 1 반복 및 100,000 바이트 파일의 경우 10 바이트 파일의 시간은 100,000 바이트와 동일 함) )
길이 합계 : 33, 반복 당 : 33.0
채널 합계 : 3626, 반복 당 : 3626.0
URL 합계 : 294, 반복 당 : 294.0
답변
rgrig의 벤치 마크에 따라 FileChannel 및 RandomAccessFile 인스턴스를 열고 닫는 데 걸리는 시간도 고려해야합니다. 이러한 클래스는 파일을 읽기위한 스트림을 엽니 다.
벤치 마크를 수정 한 후 85MB 파일에서 1 회 반복에 대한 다음 결과를 얻었습니다.
file totalTime: 48000 (48 us)
raf totalTime: 261000 (261 us)
channel totalTime: 7020000 (7 ms)
동일한 파일에서 10000 회 반복 :
file totalTime: 80074000 (80 ms)
raf totalTime: 295417000 (295 ms)
channel totalTime: 368239000 (368 ms)
파일 크기 만 있으면 file.length ()가 가장 빠른 방법입니다. 파일을 읽기 / 쓰기와 같은 다른 목적으로 사용하려는 경우 RAF가 더 나은 방법으로 보입니다. 파일 연결을 닫는 것을 잊지 마십시오 🙂
import java.io.File;
import java.io.FileInputStream;
import java.io.RandomAccessFile;
import java.nio.channels.FileChannel;
import java.util.HashMap;
import java.util.Map;
public class FileSizeBench
{
public static void main(String[] args) throws Exception
{
int iterations = 1;
String fileEntry = args[0];
Map<String, Long> times = new HashMap<String, Long>();
times.put("file", 0L);
times.put("channel", 0L);
times.put("raf", 0L);
long fileSize;
long start;
long end;
File f1;
FileChannel channel;
RandomAccessFile raf;
for (int i = 0; i < iterations; i++)
{
// file.length()
start = System.nanoTime();
f1 = new File(fileEntry);
fileSize = f1.length();
end = System.nanoTime();
times.put("file", times.get("file") + end - start);
// channel.size()
start = System.nanoTime();
channel = new FileInputStream(fileEntry).getChannel();
fileSize = channel.size();
channel.close();
end = System.nanoTime();
times.put("channel", times.get("channel") + end - start);
// raf.length()
start = System.nanoTime();
raf = new RandomAccessFile(fileEntry, "r");
fileSize = raf.length();
raf.close();
end = System.nanoTime();
times.put("raf", times.get("raf") + end - start);
}
for (Map.Entry<String, Long> entry : times.entrySet()) {
System.out.println(entry.getKey() + " totalTime: " + entry.getValue() + " (" + getTime(entry.getValue()) + ")");
}
}
public static String getTime(Long timeTaken)
{
if (timeTaken < 1000) {
return timeTaken + " ns";
} else if (timeTaken < (1000*1000)) {
return timeTaken/1000 + " us";
} else {
return timeTaken/(1000*1000) + " ms";
}
}
}
답변
나는이 같은 문제에 부딪쳤다. 네트워크 공유에서 파일 크기와 수정 날짜 90,000 개의 파일을 가져와야했습니다. Java를 사용하고 가능한 최소한으로 사용하려면 시간이 오래 걸립니다. (파일에서 URL과 객체의 경로도 가져와야했습니다. 따라서 약간 다르지만 한 시간 이상이 걸렸습니다.) 그런 다음 기본 Win32 실행 파일을 사용하고 동일한 작업을 수행하여 파일을 덤프했습니다. 콘솔에 경로, 수정 및 크기를 지정하고 Java에서 실행합니다. 속도는 놀랍습니다. 기본 프로세스와 데이터를 읽는 문자열 처리는 초당 1000 개가 넘는 항목을 처리 할 수 있습니다.
따라서 사람들이 위의 의견에 순위를 매겼지만 이것은 유효한 해결책이며 내 문제를 해결했습니다. 필자의 경우 미리 크기가 필요한 폴더를 알고 명령 줄에서 win32 앱으로 전달할 수있었습니다. 나는 디렉토리를 몇 분 동안 처리하기 위해 몇 시간에서 갔다.
이 문제는 Windows에만 국한된 것으로 보입니다. OS X에는 동일한 문제가 없었으며 OS에서 가능한 한 빨리 네트워크 파일 정보에 액세스 할 수있었습니다.
Windows에서 Java 파일 처리가 끔찍합니다. 파일에 대한 로컬 디스크 액세스는 괜찮습니다. 끔찍한 성능을 유발 한 것은 네트워크 공유였습니다. Windows는 네트워크 공유에 대한 정보를 얻고 1 분 안에 전체 크기를 계산할 수도 있습니다.
-벤
답변
디렉토리에있는 여러 파일의 파일 크기를 원하면을 사용하십시오 Files.walkFileTree
. 당신은 BasicFileAttributes
당신이받을 크기를 얻을 수 있습니다 .
이것은 훨씬 더 빨리 다음 호출 .length()
의 결과에 File.listFiles()
또는 사용 Files.size()
의 결과에 Files.newDirectoryStream()
. 내 테스트 사례에서는 약 100 배 빠릅니다.