FileInputStream kullanırken ideal arabellek boyutunu nasıl belirlersiniz?


156

Bir dosyadan bir MessageDigest (karma) oluşturan bir yöntem var ve bunu bir sürü dosya (> = 100.000) için yapmak gerekir. Performansı en üst düzeye çıkarmak için tamponun dosyalardan okumak için ne kadar büyük olması gerekir?

Çoğu kişi (her durumda burada tekrarlayacağım) temel kodu biliyor:

MessageDigest md = MessageDigest.getInstance( "SHA" );
FileInputStream ios = new FileInputStream( "myfile.bmp" );
byte[] buffer = new byte[4 * 1024]; // what should this value be?
int read = 0;
while( ( read = ios.read( buffer ) ) > 0 )
    md.update( buffer, 0, read );
ios.close();
md.digest();

Verimi en üst düzeye çıkarmak için tamponun ideal boyutu nedir? Bunun sisteme bağlı olduğunu biliyorum ve işletim sisteminin, FileSystem'in ve HDD'nin bağımlı olduğundan eminim ve karışımda başka donanım / yazılımlar da olabilir.

(Java için biraz yeni olduğumu belirtmeliyim, bu yüzden bilmediğim bazı Java API çağrıları olabilir.)

Düzenleme: Ben önceden kullanılacak sistem çeşitleri bilmiyorum, bu yüzden bir sürü kabul edemez. (Bu nedenle Java kullanıyorum.)

Düzenleme: Yukarıdaki kod, yazı daha küçük yapmak için try..catch gibi şeyler eksik

Yanıtlar:


213

Optimum arabellek boyutu birçok şeyle ilgilidir: dosya sistemi blok boyutu, CPU önbellek boyutu ve önbellek gecikmesi.

Çoğu dosya sistemi 4096 veya 8192 blok boyutlarını kullanacak şekilde yapılandırılmıştır. Teorik olarak, arabellek boyutunuzu disk bloğundan birkaç bayt daha fazla okuyacak şekilde yapılandırırsanız, dosya sistemiyle işlemler son derece verimsiz olabilir (örneğin, ara belleğinizi bir seferde 4100 bayt okuyacak şekilde yapılandırdıysanız, her bir okuma dosya sistemi tarafından 2 blok okuma gerektirir). Bloklar zaten önbellekte ise, RAM -> L3 / L2 önbellek gecikme süresini ödeyerek bitirirsiniz. Şanssızsanız ve bloklar henüz önbellekte değilse, disk-> RAM gecikmesinin de bedelini ödersiniz.

Bu yüzden çoğu arabellek boyutu 2 büyüklüğünde ve genellikle disk bloğu boyutundan daha büyük (veya bu değere eşit) olarak görülür. Bu, akış okumalarınızdan birinin birden çok disk bloğu okumasına neden olabileceği anlamına gelir - ancak bu okumalar her zaman tam bir blok kullanır - boşa giden okuma yok.

Şimdi, bu tipik bir akış senaryosunda biraz dengeleniyor çünkü diskten okunan blok, bir sonraki okumaya vurduğunuzda hala bellekte kalacak (sonuçta burada sıralı okumalar yapıyoruz) - böylece sarılıyorsunuz bir sonraki okumada RAM -> L3 / L2 önbellek gecikme fiyatını ödemek, ancak disk-> RAM gecikmesini değil. Büyüklük sırasına göre, disk-> RAM gecikmesi o kadar yavaştır ki, uğraşabileceğiniz diğer gecikmeleri hemen hemen batar.

Bu nedenle, farklı önbellek boyutlarında bir test çalıştırdıysanız (bunu kendim yapmadıysanız), muhtemelen önbellek boyutunun dosya sistemi bloğunun boyutuna kadar büyük bir etki bulacağından şüpheleniyorum. Bunun üstünde, işlerin oldukça hızlı bir şekilde dengeleneceğinden şüpheleniyorum.

Bir ton var aslında oldukça şaşırtıcı olan sistemin karmaşıklığı - koşullarına ve istisnalar burada (sadece L3 üzerindeki kolu almak -> L2 önbellek transferleri aklın almaz karmaşıktır ve her CPU türü ile değiştirir).

Bu, 'gerçek dünya' cevabına yol açar: Uygulamanız orada% 99 gibi ise, önbellek boyutunu 8192 olarak ayarlayın ve devam edin (daha da iyisi, performans üzerinde kapsüllemeyi seçin ve ayrıntıları gizlemek için BufferedInputStream kullanın). Disk verimliliğine büyük ölçüde bağımlı olan uygulamaların% 1'indeyseniz, uygulamanızı farklı disk etkileşim stratejilerini değiştirebilmeniz için hazırlayın ve kullanıcılarınızın test etmesine ve optimize etmesine (veya birtakım sonuçlara) ulaşmak için düğmeler ve kadranlar sağlayın kendini optimize etme sistemi).


3
Her ikisi için de Android uygulamam için bir cep telefonunda (Nexus 5X) bazı banchmarking yaptım: küçük dosyalar (3,5Mb) ve büyük dosyalar (175 Mb). Ve altın boyutunun 524288 uzunluktaki bayt [] olacağını öğrendim. Dosya boyutuna bağlı olarak küçük tampon 4Kb ve büyük tampon 524Kb arasında geçiş yaparsanız 10-20 ms kazanabilirsiniz, ancak buna değmez. Benim durumumda 524 Kb en iyi seçenekti.
Kirill Karmazin

19

Evet, muhtemelen çeşitli şeylere bağlı - ama çok fazla fark yaratacağından şüpheliyim. Bellek kullanımı ile performans arasında iyi bir denge olarak 16K veya 32K'yı tercih etme eğilimindeyim.

Kural dışı durum atılmış olsa bile akışın kapalı olduğundan emin olmak için kodda bir try / nihayet bloğuna sahip olmanız gerektiğini unutmayın.


Try..catch ile ilgili yazıyı düzenledim. Gerçek kodumda bir tane var, ancak yazıyı kısaltmak için dışarıda bıraktım.
ARKBAN

1
bunun için sabit bir boyut tanımlamak istiyorsak, hangi boyut daha iyidir? 4k, 16k veya 32k?
BattleTested

2
@MohammadrezaPanahi: Lütfen porsuk kullanıcılarına yorum kullanmayın. İkinci bir yorumdan bir saatten az bir süre beklediniz . Kullanıcıların kolayca uyuyabildiklerini, toplantılarda ya da temelde başka şeylerle meşgul olabileceğini ve yorumları cevaplamak için sıfır yükümlülükleri olduğunu lütfen unutmayın . Ancak sorunuzu cevaplamak için: tamamen bağlama bağlıdır. Bellek kısıtlaması olan bir sistemde çalışıyorsanız, muhtemelen küçük bir arabellek istersiniz. Büyük bir sistemde çalışıyorsanız, daha büyük bir arabellek kullanmak okunan aramaların sayısını azaltır. Kevin Day'in cevabı çok iyi.
Jon Skeet

7

Çoğu durumda, gerçekten o kadar önemli değil. 4K veya 16K gibi iyi bir boyut seçin ve buna sadık kalın. Vermenize Eğer olumlu bu uygulamanızda darboğaz olduğunu, o zaman en uygun tampon boyutunu bulmak için profilleme başlamalıdır. Çok küçük bir boyut seçerseniz, ekstra G / Ç işlemleri ve ekstra işlev çağrıları yapmak için zaman kaybedersiniz. Çok büyük bir boyut seçerseniz, sizi gerçekten yavaşlatacak birçok önbellek özlemi görmeye başlayacaksınız. L2 önbellek boyutunuzdan daha büyük bir arabellek kullanmayın.


4

İdeal durumda, dosyayı bir okuma işleminde okumak için yeterli belleğe sahip olmalıyız. Sistemin Dosya Sistemi'ni, tahsis birimlerini ve HDD'yi istediği gibi yönetmesine izin verdiğimiz için en iyi performans bu olurdu. Uygulamada, dosya boyutlarını önceden bilmek şanslısınız, sadece 4K'ya yuvarlanmış ortalama dosya boyutunu kullanın (NTFS'de varsayılan ayırma birimi). Ve en iyisi: birden fazla seçeneği test etmek için bir kıyaslama oluşturun.


bir dosyada okuma ve yazma için en iyi arabellek boyutu 4k mi demek istiyorsun?
BattleTested

4

BufferedStreams / okuyucular ve sonra arabellek boyutlarını kullanabilirsiniz.

BufferedXStreams'in arabellek boyutu olarak 8192 kullandığına inanıyorum, ancak Ovidiu'nun dediği gibi, muhtemelen bir sürü seçenek üzerinde bir test yapmalısınız. Gerçekten en iyi boyutların ne olduğu konusunda dosya sistemine ve disk yapılandırmalarına bağlı olacaktır.


4

Java NIO's FileChannel ve MappedByteBuffer kullanarak dosya okumak büyük olasılıkla FileInputStream'i içeren herhangi bir çözümden çok daha hızlı bir çözüme neden olacaktır. Temel olarak, büyük dosyaları bellekle eşleyin ve küçük dosyalar için doğrudan arabellekleri kullanın.


4

BufferedInputStream kaynağında şunları bulacaksınız: private static int DEFAULT_BUFFER_SIZE = 8192;
Bu varsayılan değeri kullanmanız iyi olur.
Ancak daha fazla bilgi bulabilirseniz, daha değerli cevaplar alırsınız.
Örneğin, TCP / IP'nin yükü nedeniyle adsl'iniz 1454 baytlık bir tamponu tercih edebilir. Diskler için, diskinizin blok boyutuyla eşleşen bir değer kullanabilirsiniz.


1

Diğer yanıtlarda daha önce belirtildiği gibi BufferedInputStreams kullanın.

Bundan sonra, tampon boyutu gerçekten önemli değil sanırım. Program G / Ç'ye bağlıdır ve BIS varsayılanı üzerinde büyüyen arabellek boyutu, performans üzerinde büyük bir etki yaratmaz.

Ya da program MessageDigest.update () içinde CPU'ya bağlıdır ve zamanın çoğu uygulama kodunda harcanmaz, bu nedenle ayar yapmak yardımcı olmaz.

(Hmm ... birden çok çekirdekli, dişler yardımcı olabilir.)


0

1024, çok çeşitli durumlar için uygundur, ancak pratikte daha büyük veya daha küçük bir tampon boyutuyla daha iyi performans görebilirsiniz.

Bu, dosya sistemi blok boyutu ve CPU donanımı gibi bir dizi faktöre bağlı olacaktır.

Tampon boyutu için 2 gücü seçmek de yaygındır, çünkü temeldeki donanımların çoğu 2 bloklu fle blok ve önbellek boyutlarıyla yapılandırılmıştır. Tamponlanmış sınıflar yapıcıda tampon boyutunu belirtmenize izin verir. Hiçbiri sağlanmazsa, çoğu JVM'de 2 gücü olan varsayılan bir değer kullanırlar.

Hangi arabellek boyutunu seçerseniz seçin, göreceğiniz en büyük performans artışı arabelleksizden arabelleklenmiş dosya erişimine geçiyor. Arabellek boyutunun ayarlanması performansı biraz artırabilir, ancak çok küçük veya çok büyük bir tampon boyutu kullanmıyorsanız önemli bir etkisi olması olası değildir.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.