Trong khi googling, tôi thấy rằng việc sử dụng java.io.File#length()
có thể chậm.
FileChannel
có mộtsize()
phương pháp có sẵn là tốt.
Có một cách hiệu quả trong java để có được kích thước tập tin?
Trong khi googling, tôi thấy rằng việc sử dụng java.io.File#length()
có thể chậm.
FileChannel
có mộtsize()
phương pháp có sẵn là tốt.
Có một cách hiệu quả trong java để có được kích thước tập tin?
Câu trả lời:
Vâng, tôi đã cố gắng đo nó với mã dưới đây:
Đối với lượt chạy = 1 và lần lặp = 1, phương thức URL nhanh nhất được theo dõi bởi kênh. Tôi chạy cái này với một số tạm dừng mới khoảng 10 lần. Vì vậy, đối với truy cập một lần, sử dụng URL là cách nhanh nhất tôi có thể nghĩ đến:
LENGTH sum: 10626, per Iteration: 10626.0
CHANNEL sum: 5535, per Iteration: 5535.0
URL sum: 660, per Iteration: 660.0
Đối với lượt chạy = 5 và số lần lặp = 50, hình ảnh sẽ khác nhau.
LENGTH sum: 39496, per Iteration: 157.984
CHANNEL sum: 74261, per Iteration: 297.044
URL sum: 95534, per Iteration: 382.136
Tệp phải được lưu trữ các cuộc gọi đến hệ thống tệp, trong khi các kênh và URL có một số chi phí.
Mã số:
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()
không trả về chiều dài tập tin. Nó trả về số lượng byte có sẵn để đọc mà không chặn các luồng khác. Nó không nhất thiết phải có cùng số lượng byte với độ dài tệp. Để có được độ dài thực từ một luồng, bạn thực sự cần phải đọc nó (và đếm số byte đã đọc trong khi đó).
Điểm chuẩn được đưa ra bởi GHad đo lường rất nhiều thứ khác (chẳng hạn như phản xạ, khởi tạo vật thể, v.v.) bên cạnh việc lấy chiều dài. Nếu chúng ta cố gắng loại bỏ những thứ này thì trong một cuộc gọi, tôi sẽ nhận được những lần sau tính bằng micrô giây:
tổng số tệp ___ 19.0, mỗi lần lặp ___ 19.0 raf sum ___ 16.0, mỗi lần lặp ___ 16.0 kênh sum__273.0, mỗi lần lặp _273.0
Trong 100 lần chạy và 10000 lần lặp tôi nhận được:
tập tin sum171767629.0, mỗi lần lặp _1.7676290000000001 raf sum ___ 881284.0, mỗi lần lặp _0,8812840000000001 tổng kênh ___ 414286.0, mỗi lần lặp _0.414286
Tôi đã chạy mã sửa đổi sau đây làm đối số tên của tệp 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));
}
}
}
Tất cả các trường hợp thử nghiệm trong bài đăng này đều thiếu sót khi họ truy cập cùng một tệp cho mỗi phương pháp được thử nghiệm. Vì vậy, bộ nhớ đệm đĩa đá trong đó kiểm tra 2 và 3 lợi ích từ. Để chứng minh quan điểm của mình, tôi đã lấy trường hợp thử nghiệm do GHAD cung cấp và thay đổi thứ tự liệt kê và dưới đây là kết quả.
Nhìn vào kết quả tôi nghĩ File.length () là người chiến thắng thực sự.
Thứ tự kiểm tra là thứ tự đầu ra. Bạn thậm chí có thể thấy thời gian thực hiện trên máy của tôi khác nhau giữa các lần thực hiện nhưng File.Lipse () khi không phải lần đầu tiên và phát sinh truy cập đĩa đầu tiên đã giành chiến thắng.
---
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
Khi tôi sửa đổi mã của bạn để sử dụng tệp được truy cập bởi một đường dẫn tuyệt đối thay vì tài nguyên, tôi nhận được một kết quả khác (cho 1 lần chạy, 1 lần lặp và tệp 100.000 byte - lần cho tệp 10 byte giống hệt với 100.000 byte )
Tổng LENGTH: 33, mỗi lần lặp: 33,0
KÊNH tổng: 3626, mỗi lần lặp: 3626.0
Tổng số URL: 294, mỗi lần lặp: 294.0
Để đáp ứng với điểm chuẩn của rgrig, thời gian để mở / đóng các phiên bản FileChannel & RandomAccessFile cũng cần được tính đến, vì các lớp này sẽ mở một luồng để đọc tệp.
Sau khi sửa đổi điểm chuẩn, tôi nhận được các kết quả này cho 1 lần lặp trên tệp 85 MB:
file totalTime: 48000 (48 us)
raf totalTime: 261000 (261 us)
channel totalTime: 7020000 (7 ms)
Đối với 10000 lần lặp trên cùng một tệp:
file totalTime: 80074000 (80 ms)
raf totalTime: 295417000 (295 ms)
channel totalTime: 368239000 (368 ms)
Nếu tất cả những gì bạn cần là kích thước tệp, file.length () là cách nhanh nhất để làm điều đó. Nếu bạn có kế hoạch sử dụng tệp cho các mục đích khác như đọc / viết, thì RAF dường như là một lựa chọn tốt hơn. Chỉ cần đừng quên đóng kết nối tập tin :-)
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";
}
}
}
Tôi gặp vấn đề tương tự. Tôi cần lấy kích thước tệp và ngày sửa đổi của 90.000 tệp trên mạng chia sẻ. Sử dụng Java và tối giản nhất có thể, sẽ mất một thời gian rất dài. (Tôi cũng cần phải lấy URL từ tệp và đường dẫn của đối tượng. Vì vậy, nó thay đổi đôi chút, nhưng hơn một giờ.) Sau đó, tôi đã sử dụng một tệp thực thi Win32 gốc và thực hiện cùng một tác vụ, chỉ cần hủy tệp đường dẫn, sửa đổi và kích thước tới bàn điều khiển và thực hiện điều đó từ Java. Tốc độ thật tuyệt vời. Quá trình riêng và xử lý chuỗi của tôi để đọc dữ liệu có thể xử lý hơn 1000 mục một giây.
Vì vậy, mặc dù mọi người đã xếp hạng nhận xét trên, đây là một giải pháp hợp lệ và đã giải quyết được vấn đề của tôi. Trong trường hợp của tôi, tôi biết các thư mục tôi cần có kích thước trước thời hạn và tôi có thể chuyển nó trong dòng lệnh cho ứng dụng win32 của mình. Tôi đã đi từ giờ để xử lý một thư mục đến phút.
Vấn đề dường như cũng là cụ thể của Windows. OS X không có cùng một vấn đề và có thể truy cập thông tin tệp mạng nhanh như hệ điều hành có thể làm như vậy.
Xử lý tệp Java trên Windows là khủng khiếp. Truy cập đĩa cục bộ cho các tập tin là tốt mặc dù. Đó chỉ là chia sẻ mạng gây ra hiệu suất khủng khiếp. Windows có thể nhận thông tin về chia sẻ mạng và tính tổng kích thước trong một phút.
--Ben
Nếu bạn muốn kích thước tệp của nhiều tệp trong một thư mục, hãy sử dụng Files.walkFileTree
. Bạn có thể có được kích thước từ BasicFileAttributes
mà bạn sẽ nhận được.
Điều này nhanh hơn nhiều sau đó gọi .length()
kết quả File.listFiles()
hoặc sử dụng Files.size()
trên kết quả của Files.newDirectoryStream()
. Trong trường hợp thử nghiệm của tôi, nó đã nhanh hơn khoảng 100 lần.
Files.walkFileTree
có sẵn trên Android 26+.
Trên thực tế, tôi nghĩ rằng "ls" có thể nhanh hơn. Chắc chắn có một số vấn đề trong Java liên quan đến việc lấy thông tin Tệp. Thật không may, không có phương pháp an toàn tương đương ls đệ quy cho Windows. (DIR / S của cmd.exe có thể bị lẫn lộn và tạo ra lỗi trong các vòng lặp vô hạn)
Trên XP, truy cập máy chủ trên mạng LAN, tôi mất 5 giây trong Windows để lấy số lượng tệp trong một thư mục (33.000) và tổng kích thước.
Khi tôi lặp lại đệ quy thông qua điều này trong Java, tôi phải mất hơn 5 phút. Tôi bắt đầu đo thời gian cần thiết để thực hiện file.length (), file.lastModified () và file.toURI () và điều tôi nhận thấy là 99% thời gian của tôi được thực hiện bởi 3 cuộc gọi đó. 3 cuộc gọi tôi thực sự cần phải làm ...
Sự khác biệt cho 1000 tệp là 15ms cục bộ so với 1800ms trên máy chủ. Quá trình quét đường dẫn máy chủ trong Java rất chậm. Nếu hệ điều hành gốc có thể nhanh chóng quét cùng thư mục đó, tại sao Java không thể?
Để thử nghiệm đầy đủ hơn, tôi đã sử dụng WineMerge trên XP để so sánh ngày sửa đổi và kích thước của các tệp trên máy chủ so với các tệp cục bộ. Điều này đã lặp lại trên toàn bộ cây thư mục gồm 33.000 tệp trong mỗi thư mục. Tổng thời gian, 7 giây. java: hơn 5 phút.
Vì vậy, tuyên bố và câu hỏi ban đầu từ OP là đúng và hợp lệ. Nó ít được chú ý khi làm việc với một hệ thống tập tin cục bộ. Thực hiện so sánh cục bộ thư mục với 33.000 mục mất 3 giây trong WinMerge và mất 32 giây cục bộ trong Java. Vì vậy, một lần nữa, java so với bản địa là một sự chậm lại 10 lần trong các thử nghiệm thô sơ này.
Java 1.6.0_22 (mới nhất), Gigabit LAN và các kết nối mạng, ping nhỏ hơn 1ms (cả hai trong cùng một công tắc)
Java chậm.
Từ điểm chuẩn của GHad, có một vài vấn đề mọi người đã đề cập:
1> Giống như BalusC đã đề cập: stream.av Available () được truyền trong trường hợp này.
Vì có sẵn () trả về ước tính số lượng byte có thể được đọc (hoặc bỏ qua) từ luồng đầu vào này mà không bị chặn bởi lời gọi tiếp theo của phương thức cho luồng đầu vào này.
Vì vậy, 1 để loại bỏ URL phương pháp này.
2> Như StuartH đã đề cập - thứ tự chạy thử cũng tạo ra sự khác biệt về bộ đệm, vì vậy hãy loại bỏ nó bằng cách chạy thử nghiệm riêng.
Bây giờ bắt đầu kiểm tra:
Khi CHANNEL một người chạy một mình:
CHANNEL sum: 59691, per Iteration: 238.764
Khi LENGTH một người chạy một mình:
LENGTH sum: 48268, per Iteration: 193.072
Vì vậy, có vẻ như LENGTH là người chiến thắng ở đây:
@Override
public long getResult() throws Exception {
File me = new File(FileSizeBench.class.getResource(
"FileSizeBench.class").getFile());
return me.length();
}