Google'ı kullanırken java.io.File#length()
, kullanımın yavaş olabileceğini görüyorum .
FileChannel
sahipsize()
kullanılabilir yöntemi vardır.
Java'da dosya boyutunu almanın etkili bir yolu var mı?
Google'ı kullanırken java.io.File#length()
, kullanımın yavaş olabileceğini görüyorum .
FileChannel
sahipsize()
kullanılabilir yöntemi vardır.
Java'da dosya boyutunu almanın etkili bir yolu var mı?
Yanıtlar:
Eh, aşağıdaki kod ile ölçmek için çalıştı:
Run = 1 ve iterations = 1 için URL yöntemi çoğu zaman en hızlıdır ve bunu kanal takip eder. Bunu yaklaşık 10 kez taze bir duraklama ile çalıştırıyorum. Bir kerelik erişim için, URL'yi kullanmak aklıma gelen en hızlı yoldur:
LENGTH sum: 10626, per Iteration: 10626.0
CHANNEL sum: 5535, per Iteration: 5535.0
URL sum: 660, per Iteration: 660.0
Koşu = 5 ve yineleme = 50 için resim farklı çizer.
LENGTH sum: 39496, per Iteration: 157.984
CHANNEL sum: 74261, per Iteration: 297.044
URL sum: 95534, per Iteration: 382.136
Kanallar ve URL'de bazı ek yükler varken, dosya dosya sistemine yapılan çağrıları önbelleğe almalıdır.
Kod:
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()
dosya uzunluğunu döndürmez. Diğer akışları engellemeden okunabilecek bayt miktarını döndürür. Dosya uzunluğu ile aynı miktarda bayt olması gerekmez. Bir akıştan gerçek uzunluğu elde etmek için gerçekten okumalısınız (ve bu arada okuma baytlarını saymanız gerekir).
GHad tarafından verilen kriter, uzunluğu elde etmenin yanı sıra birçok başka şeyi (yansıma, somut nesneler vb.) Ölçer. Bu şeylerden kurtulmaya çalışırsak, bir çağrı için mikrosaniye cinsinden aşağıdaki süreleri elde ederim:
Dosya toplamı ___ 19.0, Yineleme başına ___ 19.0 raf toplamı ___ 16.0, Yineleme başına ___ 16.0 Kanal toplamı 273,0, Yineleme başına
100 koşu ve 10000 yineleme için:
yineleme başına sum__1767629.0 dosyası, 1.7676290000000001 raf toplamı ___ 881284.0, Yineleme başına0.8812840000000001 kanal toplamı ___ 414286.0, Yineleme başına ___ 0.414286
Aşağıdaki değiştirilen kodu 100MB dosyasının adını argüman olarak vererek çalıştırdım.
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));
}
}
}
Bu gönderideki tüm test senaryoları, test edilen her yöntem için aynı dosyaya eriştiklerinden kusurludur. Dolayısıyla, test 2 ve 3'ün faydalandığı disk önbellekleme vuruşları. Benim açımdan kanıtlamak için GHAD tarafından sağlanan test davasını aldım ve numaralandırma sırasını değiştirdim ve aşağıda sonuçlar var.
Sonuca baktığımda File.length () gerçekten kazanan olduğunu düşünüyorum.
Test sırası çıktı sırasıdır. Makinemde geçen sürenin yürütmeler arasında değiştiğini, ancak ilk olmadığında File.Length () olduğunu ve ilk disk erişiminin kazanıldığını görebilirsiniz.
---
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
Kodunuzu kaynak yerine mutlak bir yolla erişilen bir dosyayı kullanacak şekilde değiştirdiğimde, farklı bir sonuç alıyorum (1 çalıştırma, 1 yineleme ve 100.000 bayt dosya için - 10 baytlık bir dosya için 100.000 bayt ile aynıdır) )
UZUNLUK toplamı: 33, Yineleme başına: 33.0
KANAL toplamı: 3626, Yineleme başına: 3626.0
URL toplamı: 294, yineleme başına: 294.0
Rgrig'in karşılaştırmalı değerlendirmesine yanıt olarak, FileChannel & RandomAccessFile örneklerinin açılması / kapatılması için geçen sürenin de dikkate alınması gerekir, çünkü bu sınıflar dosyayı okumak için bir akış açacaktır.
Karşılaştırmayı değiştirdikten sonra, 85 MB'lık bir dosyada 1 yineleme için şu sonuçları aldım:
file totalTime: 48000 (48 us)
raf totalTime: 261000 (261 us)
channel totalTime: 7020000 (7 ms)
Aynı dosyada 10000 yineleme için:
file totalTime: 80074000 (80 ms)
raf totalTime: 295417000 (295 ms)
channel totalTime: 368239000 (368 ms)
Tek ihtiyacınız olan dosya boyutu ise, file.length () bunu yapmanın en hızlı yoludur. Dosyayı okuma / yazma gibi başka amaçlarla kullanmayı planlıyorsanız, RAF daha iyi bir bahis gibi görünüyor. Sadece dosya bağlantısını kapatmayı unutma :-)
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";
}
}
}
Aynı sorunla karşılaştım. Bir ağ paylaşımında dosya boyutunu ve 90.000 dosyanın değiştirilme tarihini almam gerekiyordu. Java kullanmak ve olabildiğince minimalist olmak çok uzun zaman alacaktır. (Ben de dosyadan URL ve nesnenin yolunu almak gerekiyordu. Bu yüzden biraz değişti, ama bir saatten fazla.) Sonra yerel bir Win32 çalıştırılabilir ve aynı dosyayı yaptım, sadece dosyayı damping yol, değiştirilmiş ve konsolun boyutuna getirilmiş ve Java'dan çalıştırılmıştır. Hız şaşırtıcıydı. Yerel işlem ve verileri okumak için dize işleme, saniyede 1000'den fazla öğeyi işleyebilir.
Bu yüzden insanlar yukarıdaki yorumu sıralamasına rağmen, bu geçerli bir çözüm ve sorunumu çözdü. Benim durumumda önceden belirlenmiş boyutlara ihtiyacım olan klasörleri biliyordum ve bunu komut satırında win32 uygulamama aktarabiliyordum. Bir dizini işlemek için saatlerden dakikalara gittim.
Sorun Windows'a özgü gibi görünüyordu. OS X ile aynı sorun yoktu ve işletim sisteminin yapabildiği kadar hızlı ağ dosya bilgilerine erişebiliyordu.
Windows'da Java Dosya kullanımı korkunç. Dosyalar için yerel disk erişimi gayet iyi. Sadece korkunç performansa neden olan ağ paylaşımlarıydı. Windows ağ paylaşımı hakkında bilgi alabilir ve bir dakikadan kısa sürede toplam boyutu hesaplayabilir.
--Ben
Bir dizindeki birden çok dosyanın dosya boyutunu istiyorsanız, tuşunu kullanın Files.walkFileTree
. Size, BasicFileAttributes
alacağınız boyuttan ulaşabilirsiniz .
Bu çok daha hızlı sonra aradığını .length()
sonucuna File.listFiles()
kullanılarak veya Files.size()
sonucuna Files.newDirectoryStream()
. Test vakalarımda 100 kat daha hızlıydı.
Files.walkFileTree
Android 26 ve sonraki sürümlerinde kullanılabilir.
Aslında, "ls" daha hızlı olabilir düşünüyorum. Java'da kesinlikle Dosya bilgisi almakla ilgili bazı sorunlar vardır. Ne yazık ki Windows için özyinelemeli ls eşdeğer güvenli bir yöntemi yoktur. (cmd.exe'nin DIR / S'si karışabilir ve sonsuz döngülerde hatalar oluşturabilir)
XP'de, LAN üzerindeki bir sunucuya erişmek, Windows'ta bir klasördeki dosya sayısını (33.000) ve toplam boyutu almam 5 saniye sürer.
Java ile yinelemeli olarak yinelediğimde, 5 dakikadan fazla sürüyor. File.length (), file.lastModified () ve file.toURI () için gereken süreyi ölçmeye başladım ve bulduğum şey, zamanımın% 99'unun bu 3 çağrı tarafından alındığı. Aslında yapmam gereken 3 çağrı ...
1000 dosya arasındaki fark sunucuda 1800 ms'ye karşılık 15 ms yereldir. Java'da sunucu yolu taraması gülünç derecede yavaştır. Yerel işletim sistemi aynı klasörü taramakta hızlıysa neden Java?
Daha eksiksiz bir test olarak, sunucudaki dosyaların değiştirilme tarihini ve boyutunu yerel dosyalarla karşılaştırmak için XP'de WineMerge kullandım. Bu, her klasördeki 33.000 dosyanın dizin ağacının tamamını yineliyordu. Toplam süre, 7 saniye. java: 5 dakikadan fazla.
Yani OP'nin orijinal ifadesi ve sorusu doğru ve geçerlidir. Yerel bir dosya sistemi ile uğraşırken daha az fark edilir. Klasörün 33.000 öğeyle yerel olarak karşılaştırılması WinMerge'de 3 saniye, Java'da yerel olarak 32 saniye sürer. Yine, java'ya karşı bu ilkel testlerde 10 kat yavaşlamadır.
Java 1.6.0_22 (en yeni), Gigabit LAN ve ağ bağlantıları, ping 1ms'den az (her ikisi de aynı anahtarda)
Java yavaş.
GHad'ın kıstasından, insanların bahsettiği birkaç sorun var:
1> BalusC'un belirttiği gibi: bu durumda stream.available () akar.
Available () yöntemi, bu giriş akışı için bir yöntemin bir sonraki çağrılması engellenmeden bu giriş akışından okunabilen (veya atlanabilen) bayt sayısının bir tahminini döndürür .
Yani 1 URL kaldırmak için bu yaklaşım.
2> StuartH'ın belirttiği gibi - test çalışmasının sırası da önbellek farkını yapar, bu yüzden testi ayrı olarak çalıştırarak çıkarın.
Şimdi testi başlat:
CHANNEL tek başına koştuğunda:
CHANNEL sum: 59691, per Iteration: 238.764
UZUNLUK tek başına koştuğunda:
LENGTH sum: 48268, per Iteration: 193.072
Görünüşe göre UZUNLUK burada kazanan:
@Override
public long getResult() throws Exception {
File me = new File(FileSizeBench.class.getResource(
"FileSizeBench.class").getFile());
return me.length();
}