Saya mencoba mencari tahu apakah ada perbedaan kinerja (atau keuntungan) ketika kita menggunakan nio FileChannel
versus normal FileInputStream/FileOuputStream
untuk membaca dan menulis file ke sistem file. Saya mengamati bahwa pada mesin saya keduanya bekerja pada tingkat yang sama, juga berkali-kali FileChannel
jalannya lebih lambat. Bisakah saya tahu lebih detail membandingkan kedua metode ini. Berikut adalah kode yang saya gunakan, file yang saya uji ada di sekitar 350MB
. Apakah ini pilihan yang baik untuk menggunakan kelas berbasis NIO untuk File I / O, jika saya tidak melihat akses acak atau fitur canggih lainnya?
package trialjavaprograms;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
public class JavaNIOTest {
public static void main(String[] args) throws Exception {
useNormalIO();
useFileChannel();
}
private static void useNormalIO() throws Exception {
File file = new File("/home/developer/test.iso");
File oFile = new File("/home/developer/test2");
long time1 = System.currentTimeMillis();
InputStream is = new FileInputStream(file);
FileOutputStream fos = new FileOutputStream(oFile);
byte[] buf = new byte[64 * 1024];
int len = 0;
while((len = is.read(buf)) != -1) {
fos.write(buf, 0, len);
}
fos.flush();
fos.close();
is.close();
long time2 = System.currentTimeMillis();
System.out.println("Time taken: "+(time2-time1)+" ms");
}
private static void useFileChannel() throws Exception {
File file = new File("/home/developer/test.iso");
File oFile = new File("/home/developer/test2");
long time1 = System.currentTimeMillis();
FileInputStream is = new FileInputStream(file);
FileOutputStream fos = new FileOutputStream(oFile);
FileChannel f = is.getChannel();
FileChannel f2 = fos.getChannel();
ByteBuffer buf = ByteBuffer.allocateDirect(64 * 1024);
long len = 0;
while((len = f.read(buf)) != -1) {
buf.flip();
f2.write(buf);
buf.clear();
}
f2.close();
f.close();
long time2 = System.currentTimeMillis();
System.out.println("Time taken: "+(time2-time1)+" ms");
}
}
java
optimization
file
nio
operations
Keshav
sumber
sumber
transferTo
SayatransferFrom
akan lebih konvensional untuk menyalin file. Teknik mana pun yang tidak membuat hard drive Anda lebih cepat atau lebih lambat, meskipun saya kira mungkin ada masalah jika membaca potongan kecil pada suatu waktu dan menyebabkan kepala menghabiskan banyak waktu mencari.Jawaban:
Pengalaman saya dengan ukuran file yang lebih besar
java.nio
adalah lebih cepat darijava.io
. Sangat cepat. Seperti di kisaran> 250%. Yang mengatakan, saya menghilangkan hambatan yang jelas, yang saya sarankan mikro-benchmark Anda mungkin menderita. Area potensial untuk diselidiki:Ukuran buffer. Algoritma yang pada dasarnya Anda miliki adalah
Pengalaman saya sendiri adalah bahwa ukuran buffer ini sudah siap untuk penyetelan. Saya telah menggunakan 4KB untuk satu bagian dari aplikasi saya, 256KB untuk bagian lain. Saya menduga kode Anda menderita dengan buffer yang begitu besar. Jalankan beberapa tolok ukur dengan buffer 1KB, 2KB, 4KB, 8KB, 16KB, 32KB, dan 64KB untuk membuktikannya sendiri.
Jangan lakukan tolok ukur java yang membaca dan menulis ke disk yang sama.
Jika Anda melakukannya, maka Anda benar-benar membuat benchmark disk, dan bukan Java. Saya juga menyarankan bahwa jika CPU Anda tidak sibuk, maka Anda mungkin mengalami beberapa hambatan lainnya.
Jangan gunakan buffer jika Anda tidak perlu.
Mengapa menyalin ke memori jika target Anda adalah disk lain atau NIC? Dengan file yang lebih besar, latensi yang ditimbulkan tidak mudah.
Seperti kata orang lain, gunakan
FileChannel.transferTo()
atauFileChannel.transferFrom()
. Keuntungan utama di sini adalah bahwa JVM menggunakan akses OS ke DMA ( Akses Memori Langsung ), jika ada. (Ini tergantung pada implementasi, tetapi versi Sun dan IBM modern pada CPU tujuan umum baik untuk digunakan.) Yang terjadi adalah data langsung menuju / dari disk, ke bus, dan kemudian ke tujuan ... melewati sirkuit apa pun melalui RAM atau CPU.Aplikasi web yang saya habiskan siang dan malam bekerja sangat berat. Saya telah melakukan tolok ukur mikro dan tolok ukur dunia nyata juga. Dan hasilnya di blog saya, lihat-lihat:
Gunakan data produksi dan lingkungan
Micro-benchmark rentan terhadap distorsi. Jika Anda bisa, lakukan upaya untuk mengumpulkan data dari apa yang Anda rencanakan, dengan beban yang Anda harapkan, pada perangkat keras yang Anda harapkan.
Tolok ukur saya solid dan dapat diandalkan karena terjadi pada sistem produksi, sistem gemuk, sistem di bawah beban, dikumpulkan dalam log. Bukan drive SATA 7200 RPM 2.5 "saya sementara saya menonton dengan intens ketika JVM mengerjakan hard disk saya.
Apa yang sedang Anda jalankan? Itu penting.
sumber
Jika hal yang ingin Anda bandingkan adalah kinerja penyalinan file, maka untuk pengujian saluran Anda harus melakukan ini sebagai gantinya:
Ini tidak akan lebih lambat daripada melindungi diri Anda dari satu saluran ke saluran lainnya, dan berpotensi akan secara besar-besaran lebih cepat. Menurut Javadocs:
sumber
Berdasarkan tes saya (Win7 64bit, 6GB RAM, Java6), NIO transferFrom hanya cepat dengan file kecil dan menjadi sangat lambat pada file yang lebih besar. NIO databuffer flip selalu mengungguli IO standar.
Menyalin 1000x2MB
Menyalin 100x20mb
Menyalin 1x1000mb
Metode transferTo () bekerja pada potongan file; tidak dimaksudkan sebagai metode penyalinan file tingkat tinggi: Bagaimana cara menyalin file besar di Windows XP?
sumber
Menjawab bagian "kegunaan" dari pertanyaan:
Satu hal yang agak halus dari penggunaan
FileChannel
lebihFileOutputStream
adalah melakukan salah satu operasi pemblokirannya (mis.read()
Atauwrite()
) dari utas yang dalam keadaan terputus akan menyebabkan saluran menutup dengan tiba-tibajava.nio.channels.ClosedByInterruptException
.Sekarang, ini bisa menjadi hal yang baik jika apa pun yang
FileChannel
digunakan adalah bagian dari fungsi utama utas, dan desain memperhitungkannya.Tetapi bisa juga sial jika digunakan oleh beberapa fitur tambahan seperti fungsi logging. Misalnya, Anda dapat mendapati output logging Anda tiba-tiba ditutup jika fungsi logging kebetulan dipanggil oleh utas yang juga terputus.
Sangat disayangkan ini begitu halus karena tidak memperhitungkan ini dapat menyebabkan bug yang mempengaruhi integritas penulisan. [1] [2]
sumber
Saya menguji kinerja FileInputStream vs FileChannel untuk mendekode file base64 yang disandikan. Dalam pengalaman saya, saya menguji file yang agak besar dan io tradisional sedikit lebih cepat daripada nio.
FileChannel mungkin memiliki keuntungan dalam versi sebelumnya dari jvm karena sinkronisasi biaya overhead di beberapa kelas terkait, tetapi jvm modern cukup baik dalam menghapus kunci yang tidak dibutuhkan.
sumber
Jika Anda tidak menggunakan fitur transferTo atau fitur non-blocking, Anda tidak akan melihat perbedaan antara IO tradisional dan NIO (2) karena IO tradisional memetakan ke NIO.
Tetapi jika Anda dapat menggunakan fitur NIO seperti transferFrom / Ke atau ingin menggunakan Buffer, maka tentu saja NIO adalah cara yang harus dilakukan.
sumber
Pengalaman saya adalah, bahwa NIO jauh lebih cepat dengan file-file kecil. Tetapi ketika datang ke file besar FileInputStream / FileOutputStream jauh lebih cepat.
sumber
java.nio
adalah lebih cepat dengan file yang lebih besar daripadajava.io
, tidak lebih kecil.java.nio
cepat selama file cukup kecil untuk dipetakan ke memori. Jika bertambah besar (200 MB dan lebih banyak)java.io
lebih cepat.FileChannel.read()
. Tidak hanya ada satu pendekatan tunggal untuk membaca file menggunakanjava.nio
.