Saat googling, saya melihat bahwa menggunakan java.io.File#length()
bisa lambat.
FileChannel
memiliki size()
metode yang tersedia juga.
Apakah ada cara yang efisien di java untuk mendapatkan ukuran file?
Saat googling, saya melihat bahwa menggunakan java.io.File#length()
bisa lambat.
FileChannel
memiliki size()
metode yang tersedia juga.
Apakah ada cara yang efisien di java untuk mendapatkan ukuran file?
Jawaban:
Baiklah, saya mencoba mengukurnya dengan kode di bawah ini:
Untuk menjalankan = 1 dan iterasi = 1 metode URL paling cepat diikuti oleh saluran. Saya menjalankan ini dengan jeda segar sekitar 10 kali. Jadi untuk akses satu kali, menggunakan URL adalah cara tercepat yang dapat saya pikirkan:
LENGTH sum: 10626, per Iteration: 10626.0
CHANNEL sum: 5535, per Iteration: 5535.0
URL sum: 660, per Iteration: 660.0
Untuk menjalankan = 5 dan iterasi = 50 gambarnya berbeda.
LENGTH sum: 39496, per Iteration: 157.984
CHANNEL sum: 74261, per Iteration: 297.044
URL sum: 95534, per Iteration: 382.136
File harus caching panggilan ke sistem file, sementara saluran dan URL memiliki beberapa overhead.
Kode:
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()
tidak mengembalikan panjang file. Ini mengembalikan jumlah byte yang tersedia untuk dibaca tanpa memblokir aliran lainnya. Jumlah byte tidak selalu sama dengan panjang file. Untuk mendapatkan panjang sebenarnya dari aliran, Anda benar-benar perlu membacanya (dan menghitung byte baca sementara itu).
Benchmark yang diberikan oleh GHad mengukur banyak hal lain (seperti refleksi, objek instantiating, dll.) Selain mendapatkan panjangnya. Jika kita mencoba untuk menyingkirkan hal-hal ini maka untuk satu panggilan saya dapatkan waktu berikut dalam mikrodetik:
jumlah file ___ 19.0, per Iterasi ___ 19.0 jumlah raf ___ 16.0, per Iterasi ___ 16.0 jumlah saluran__273.0, per Iteration__273.0
Untuk 100 berjalan dan 10.000 iterasi saya dapatkan:
file sum__1767629.0, per Iteration__1.7676290000000001 raf sum ___ 881284.0, per Iteration__0.8812840000000001 jumlah saluran ___ 414286.0, per Iteration__0.414286
Saya memang menjalankan kode yang dimodifikasi berikut memberikan sebagai argumen nama file 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));
}
}
}
Semua kasus uji dalam posting ini cacat karena mereka mengakses file yang sama untuk setiap metode yang diuji. Jadi, cache caching menghasilkan tes 2 dan 3. Untuk membuktikan pendapat saya, saya mengambil test case yang disediakan oleh GHAD dan mengubah urutan enumerasi dan berikut hasilnya.
Melihat hasil, saya pikir File.length () adalah pemenangnya.
Urutan tes adalah urutan output. Anda bahkan dapat melihat waktu yang dibutuhkan pada mesin saya bervariasi antara eksekusi tetapi File.Length () ketika tidak pertama, dan menimbulkan akses disk pertama dimenangkan.
---
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
Ketika saya memodifikasi kode Anda untuk menggunakan file yang diakses oleh jalur absolut alih-alih sumber daya, saya mendapatkan hasil yang berbeda (untuk 1 run, 1 iterasi, dan file 100.000 byte - kali untuk file 10 byte identik dengan 100.000 byte )
PANJANG jumlah: 33, per Iterasi: 33.0
Jumlah CHANNEL: 3626, per Iterasi: 3626.0
Jumlah URL: 294, per Iterasi: 294.0
Menanggapi tolok ukur rgrig, waktu yang dibutuhkan untuk membuka / menutup instance FileChannel & RandomAccessFile juga perlu diperhitungkan, karena kelas-kelas ini akan membuka aliran untuk membaca file.
Setelah memodifikasi patokan, saya mendapat hasil ini untuk 1 iterasi pada file 85MB:
file totalTime: 48000 (48 us)
raf totalTime: 261000 (261 us)
channel totalTime: 7020000 (7 ms)
Untuk 10.000 iterasi pada file yang sama:
file totalTime: 80074000 (80 ms)
raf totalTime: 295417000 (295 ms)
channel totalTime: 368239000 (368 ms)
Jika yang Anda butuhkan adalah ukuran file, file.length () adalah cara tercepat untuk melakukannya. Jika Anda berencana untuk menggunakan file untuk tujuan lain seperti membaca / menulis, maka RAF tampaknya menjadi taruhan yang lebih baik. Hanya saja jangan lupa untuk menutup koneksi file :-)
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";
}
}
}
Saya mengalami masalah yang sama. Saya perlu mendapatkan ukuran file dan tanggal modifikasi dari 90.000 file di jaringan berbagi. Menggunakan Java, dan menjadi seminimal mungkin, itu akan memakan waktu yang sangat lama. (Saya perlu mendapatkan URL dari file, dan jalur objek juga. Jadi agak bervariasi, tetapi lebih dari satu jam.) Saya kemudian menggunakan executable Win32 asli, dan melakukan tugas yang sama, hanya membuang file jalan, dimodifikasi, dan ukuran ke konsol, dan dieksekusi itu dari Jawa. Kecepatannya luar biasa. Proses asli, dan penanganan string saya untuk membaca data dapat memproses lebih dari 1000 item per detik.
Jadi, meskipun orang-orang di bawah peringkat komentar di atas, ini adalah solusi yang valid, dan memang memecahkan masalah saya. Dalam kasus saya, saya tahu folder yang saya butuhkan ukuran sebelumnya, dan saya bisa meneruskannya di baris perintah ke aplikasi win32 saya. Saya beralih dari jam ke proses direktori ke menit.
Masalahnya juga tampaknya khusus untuk Windows. OS X tidak memiliki masalah yang sama dan dapat mengakses informasi file jaringan secepat OS dapat melakukannya.
Penanganan File Java di Windows sangat buruk. Akses disk lokal untuk file baik-baik saja. Itu hanya jaringan berbagi yang menyebabkan kinerja yang mengerikan. Windows dapat memperoleh info tentang jaringan berbagi dan menghitung ukuran total dalam waktu kurang dari satu menit juga.
--Ben
Jika Anda ingin ukuran file beberapa file dalam direktori, gunakan Files.walkFileTree
. Anda dapat memperoleh ukuran dariBasicFileAttributes
yang akan Anda terima.
Ini jauh lebih cepat daripada memanggil .length()
hasil File.listFiles()
atau menggunakan Files.size()
hasil Files.newDirectoryStream()
. Dalam kasus pengujian saya sekitar 100 kali lebih cepat.
Files.walkFileTree
tersedia di Android 26+.
Sebenarnya, saya pikir "ls" mungkin lebih cepat. Pasti ada beberapa masalah di Jawa yang berhubungan dengan mendapatkan info File. Sayangnya tidak ada metode rekursif aman yang setara untuk Windows. (DIR / S cmd.exe bisa membingungkan dan menghasilkan kesalahan dalam loop tak terbatas)
Di XP, mengakses server di LAN, saya butuh 5 detik di Windows untuk mendapatkan jumlah file dalam folder (33.000), dan ukuran total.
Ketika saya mengulanginya secara berulang di Jawa, saya membutuhkan waktu lebih dari 5 menit. Saya mulai mengukur waktu yang diperlukan untuk melakukan file.length (), file.lastModified (), dan file.toURI () dan apa yang saya temukan adalah bahwa 99% dari waktu saya diambil oleh 3 panggilan itu. 3 panggilan yang sebenarnya harus saya lakukan ...
Perbedaan untuk 1000 file adalah 15ms lokal versus 1800ms di server. Pemindaian jalur server di Jawa sangat lambat. Jika OS asli bisa cepat memindai folder yang sama, mengapa tidak bisa Java?
Sebagai tes yang lebih lengkap, saya menggunakan WineMerge di XP untuk membandingkan tanggal yang dimodifikasi, dan ukuran file di server versus file secara lokal. Ini mengulangi seluruh pohon direktori dari 33.000 file di setiap folder. Total waktu, 7 detik. java: lebih dari 5 menit.
Jadi pernyataan dan pertanyaan asli dari OP itu benar, dan valid. Ini kurang terlihat ketika berhadapan dengan sistem file lokal. Melakukan perbandingan folder secara lokal dengan 33.000 item membutuhkan waktu 3 detik di WinMerge, dan memakan waktu 32 detik secara lokal di Java. Jadi sekali lagi, java versus asli adalah perlambatan 10x dalam tes dasar ini.
Java 1.6.0_22 (terbaru), Gigabit LAN, dan koneksi jaringan, ping kurang dari 1ms (keduanya dalam switch yang sama)
Java lambat.
Dari tolok ukur GHad, ada beberapa masalah yang disebutkan orang:
1> Seperti yang disebutkan BalusC: stream.available () mengalir dalam kasus ini.
Karena tersedia () mengembalikan estimasi jumlah byte yang dapat dibaca (atau dilompati) dari aliran input ini tanpa menghalangi dengan permohonan metode berikutnya untuk aliran input ini.
Jadi 1 untuk menghapus URL pendekatan ini.
2> Seperti yang disebutkan StuartH - urutan tes juga membuat perbedaan cache, jadi keluarkan dengan menjalankan tes secara terpisah.
Sekarang mulailah tes:
Ketika CHANNEL satu dijalankan sendiri:
CHANNEL sum: 59691, per Iteration: 238.764
Ketika PANJANG satu berjalan sendiri:
LENGTH sum: 48268, per Iteration: 193.072
Jadi sepertinya PANJANG adalah pemenangnya di sini:
@Override
public long getResult() throws Exception {
File me = new File(FileSizeBench.class.getResource(
"FileSizeBench.class").getFile());
return me.length();
}