인터넷 검색 중에 사용 java.io.File#length()
속도가 느릴 수 있습니다.
FileChannel
있다size()
잘으로 사용할 방법을.
Java에서 파일 크기를 얻는 효율적인 방법이 있습니까?
인터넷 검색 중에 사용 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;
}
}
stream.available()
파일 길이를 반환하지 않습니다. 다른 스트림을 차단하지 않고 읽을 수있는 바이트 수를 반환합니다. 반드시 파일 길이와 바이트 수가 같을 필요는 없습니다. 스트림에서 실제 길이를 얻으려면 실제로 읽어야 합니다 (그리고 읽은 바이트를 계산하십시오).
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
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 배 빠릅니다.
Files.walkFileTree
로 Android 26 이상에서 사용할 수 있습니다.
실제로 "ls"가 더 빠를 수 있다고 생각합니다. Java에는 파일 정보를 얻는 것과 관련하여 분명히 몇 가지 문제가 있습니다. 불행히도 Windows에는 안전한 안전한 재귀 방법이 없습니다. (cmd.exe의 DIR / S는 혼란스럽고 무한 루프에서 오류를 생성 할 수 있습니다)
XP에서 LAN의 서버에 액세스하면 Windows에서 폴더의 파일 수 (33,000)와 총 크기를 가져 오는 데 5 초가 걸립니다.
Java에서 이것을 통해 재귀 적으로 반복하면 5 분 이상 걸립니다. file.length (), file.lastModified () 및 file.toURI ()를 수행하는 데 걸리는 시간을 측정하기 시작했으며, 내가 찾은 것은 99 %의 시간이 3 번의 호출에 걸린다는 것입니다. 내가 실제로해야 할 3 가지 전화 ...
1000 파일의 차이는 서버에서 15ms 로컬과 1800ms입니다. Java에서 서버 경로 스캔이 엄청나게 느립니다. 기본 OS가 동일한 폴더를 빠르게 검색 할 수 있다면 왜 Java를 할 수 없습니까?
보다 완전한 테스트로 XP의 WineMerge를 사용하여 서버의 파일과 로컬의 파일의 수정 된 날짜와 크기를 비교했습니다. 이것은 각 폴더에 33,000 개의 파일로 구성된 전체 디렉토리 트리를 반복했습니다. 총 시간, 7 초 자바 : 5 분 이상.
따라서 OP의 원래 진술과 질문은 사실이며 유효합니다. 로컬 파일 시스템을 다룰 때 눈에 띄지 않습니다. WinMerge에서 폴더와 33,000 개의 항목을 로컬로 비교하면 3 초가 걸리고 Java에서는 로컬로 32 초가 걸립니다. 다시 말하지만, 자바 대 네이티브는 이러한 기초 테스트에서 10 배의 속도 저하입니다.
Java 1.6.0_22 (최신), 기가비트 LAN 및 네트워크 연결, 핑이 1ms 미만 (둘 다 동일한 스위치에 있음)
자바는 느리다.
GHad의 벤치 마크에서 사람들이 언급 한 몇 가지 문제가 있습니다.
1> BalusC와 같이 언급 :이 경우 stream.available ()이 흐릅니다.
available ()은 다음 입력 스트림에 대한 메소드 호출에 의해 차단되지 않고이 입력 스트림에서 읽거나 건너 뛸 수있는 바이트 수의 추정값 을 리턴 합니다.
따라서이 방법으로 URL을 먼저 제거하십시오.
2> StuartH가 언급했듯이-테스트 실행 순서에 따라 캐시 차이가 발생하므로 테스트를 별도로 실행하여 제거하십시오.
이제 테스트를 시작하십시오 :
채널 하나가 단독으로 실행될 때 :
CHANNEL sum: 59691, per Iteration: 238.764
LENGTH 하나만 실행할 때 :
LENGTH sum: 48268, per Iteration: 193.072
LENGTH가 승자 인 것 같습니다.
@Override
public long getResult() throws Exception {
File me = new File(FileSizeBench.class.getResource(
"FileSizeBench.class").getFile());
return me.length();
}