Saya mendapat tugas yang bagus untuk mengetahui cara menangani file besar yang dimuat ke editor skrip aplikasi kami (ini seperti VBA untuk produk internal kami untuk makro cepat). Sebagian besar file berukuran sekitar 300-400 KB yang dapat dimuat dengan baik. Tetapi ketika mereka melampaui 100 MB, prosesnya mengalami kesulitan (seperti yang Anda harapkan).
Apa yang terjadi adalah bahwa file tersebut dibaca dan dimasukkan ke dalam RichTextBox yang kemudian dinavigasi - jangan terlalu khawatir tentang bagian ini.
Pengembang yang menulis kode awal hanya menggunakan StreamReader dan melakukan
[Reader].ReadToEnd()
yang bisa memakan waktu cukup lama untuk menyelesaikannya.
Tugas saya adalah memecah sedikit kode ini, membacanya dalam potongan menjadi buffer dan menampilkan bilah kemajuan dengan opsi untuk membatalkannya.
Beberapa asumsi:
- Kebanyakan file berukuran 30-40 MB
- Isi filenya adalah teks (bukan biner), ada yang berformat Unix, ada pula yang DOS.
- Setelah konten diambil, kami mencari tahu terminator apa yang digunakan.
- Tidak ada yang peduli setelah dimuat tentang waktu yang diperlukan untuk merender di kotak teks kaya. Ini hanya pemuatan awal teks.
Sekarang untuk pertanyaannya:
- Bisakah saya menggunakan StreamReader, lalu memeriksa properti Length (jadi ProgressMax) dan mengeluarkan Read untuk ukuran buffer yang ditetapkan dan mengulang-ulang sementara WHILST di dalam pekerja latar belakang, sehingga tidak memblokir thread UI utama? Kemudian kembalikan pembuat string ke utas utama setelah selesai.
- Isinya akan dikirim ke StringBuilder. dapatkah saya menginisialisasi StringBuilder dengan ukuran aliran jika panjangnya tersedia?
Apakah ini (menurut pendapat profesional Anda) ide bagus? Saya pernah mengalami beberapa masalah di masa lalu dengan membaca konten dari Streams, karena akan selalu melewatkan beberapa byte terakhir atau sesuatu, tetapi saya akan mengajukan pertanyaan lain jika ini masalahnya.
sumber
Jawaban:
Anda dapat meningkatkan kecepatan baca dengan menggunakan BufferedStream, seperti ini:
PEMBARUAN Maret 2013
Saya baru-baru ini menulis kode untuk membaca dan memproses (mencari teks dalam) file teks 1 GB-ish (jauh lebih besar daripada file yang terlibat di sini) dan mencapai peningkatan kinerja yang signifikan dengan menggunakan pola produsen / konsumen. Tugas produser membaca baris teks menggunakan the
BufferedStream
dan menyerahkannya ke tugas konsumen terpisah yang melakukan pencarian.Saya menggunakan ini sebagai kesempatan untuk mempelajari TPL Dataflow, yang sangat cocok untuk mengkodekan pola ini dengan cepat.
Mengapa BufferedStream lebih cepat
PEMBARUAN Desember 2014: Jarak Tempuh Anda Dapat Bervariasi
Berdasarkan komentar, FileStream harus menggunakan BufferedStream secara internal. Pada saat jawaban ini pertama kali diberikan, saya mengukur peningkatan kinerja yang signifikan dengan menambahkan BufferedStream. Saat itu saya menargetkan .NET 3.x pada platform 32-bit. Hari ini, menargetkan .NET 4.5 pada platform 64-bit, saya tidak melihat adanya peningkatan.
Terkait
Saya menemukan kasus di mana streaming file CSV yang besar dan dihasilkan ke aliran Respons dari tindakan ASP.Net MVC sangat lambat. Menambahkan BufferedStream meningkatkan kinerja sebesar 100x dalam contoh ini. Untuk lebih lanjut, lihat Output Tidak Disangga Sangat Lambat
sumber
Jika Anda membaca statistik kinerja dan tolok ukur di situs web ini , Anda akan melihat bahwa cara tercepat untuk membaca (karena membaca, menulis, dan memproses semuanya berbeda) file teks adalah cuplikan kode berikut:
Semuanya sekitar 9 metode yang berbeda telah ditandai, tetapi yang satu tampaknya keluar di sebagian besar waktu, bahkan keluar melakukan buffered reader seperti yang disebutkan oleh pembaca lain.
sumber
StringBuilder
untuk memuatnya ke memori, memuat lebih cepat karena tidak membuat string baru setiap kali Anda menambahkan karakter)Anda mengatakan Anda telah diminta untuk menunjukkan bilah kemajuan saat file besar sedang dimuat. Apakah itu karena pengguna benar-benar ingin melihat% pemuatan file yang tepat, atau hanya karena mereka menginginkan umpan balik visual bahwa ada sesuatu yang terjadi?
Jika yang terakhir benar, maka solusinya menjadi lebih sederhana. Lakukan saja
reader.ReadToEnd()
pada utas latar belakang, dan tampilkan bilah kemajuan tipe marquee alih-alih bilah yang tepat.Saya mengangkat poin ini karena menurut pengalaman saya hal ini sering terjadi. Saat Anda menulis program pemrosesan data, maka pengguna pasti akan tertarik dengan angka% lengkap, tetapi untuk pembaruan UI yang sederhana namun lambat, mereka lebih cenderung hanya ingin tahu bahwa komputer tidak macet. :-)
sumber
StreamReader
loop. Namun, ini tetap akan lebih sederhana karena tidak perlu membaca dulu untuk menghitung indikator kemajuan.Untuk file biner, cara tercepat untuk membacanya yang saya temukan adalah ini.
Dalam pengujian saya, ini ratusan kali lebih cepat.
sumber
Gunakan pekerja latar belakang dan baca baris dalam jumlah terbatas. Baca lebih lanjut hanya saat pengguna menggulir.
Dan cobalah untuk tidak pernah menggunakan ReadToEnd (). Itu salah satu fungsi yang menurut Anda "mengapa mereka membuatnya?"; itu adalah pembantu script kiddies yang bekerja dengan baik dengan hal-hal kecil, tetapi seperti yang Anda lihat, itu menyebalkan untuk file besar ...
Orang-orang yang memberi tahu Anda untuk menggunakan StringBuilder perlu membaca MSDN lebih sering:
Pertimbangan Performa
Metode Concat dan AppendFormat menggabungkan data baru ke objek String atau StringBuilder yang sudah ada. Operasi penggabungan objek String selalu membuat objek baru dari string yang ada dan data baru. Objek StringBuilder memelihara buffer untuk mengakomodasi penggabungan data baru. Data baru ditambahkan ke ujung buffer jika ruang tersedia; jika tidak, buffer baru yang lebih besar dialokasikan, data dari buffer asli disalin ke buffer baru, lalu data baru ditambahkan ke buffer baru. Kinerja operasi penggabungan untuk objek String atau StringBuilder bergantung pada seberapa sering alokasi memori terjadi.
Operasi penggabungan String selalu mengalokasikan memori, sedangkan operasi penggabungan StringBuilder hanya mengalokasikan memori jika buffer objek StringBuilder terlalu kecil untuk menampung data baru. Akibatnya, kelas String lebih disukai untuk operasi penggabungan jika sejumlah objek String digabungkan. Dalam hal ini, operasi penggabungan individu bahkan dapat digabungkan menjadi satu operasi oleh kompilator. Objek StringBuilder lebih disukai untuk operasi penggabungan jika sejumlah string digabungkan; misalnya, jika sebuah loop menggabungkan sejumlah string input pengguna secara acak.
Itu berarti besar alokasi memori, apa yang menjadi besar penggunaan sistem file swap, yang mensimulasikan bagian dari hard disk drive Anda untuk bertindak seperti memori RAM, tapi hard disk drive sangat lambat.
Opsi StringBuilder terlihat bagus untuk siapa yang menggunakan sistem sebagai pengguna tunggal, tetapi ketika Anda memiliki dua atau lebih pengguna yang membaca file besar pada saat yang sama, Anda mengalami masalah.
sumber
Ini seharusnya cukup untuk membantu Anda memulai.
sumber
Lihat cuplikan kode berikut. Anda telah menyebutkan
Most files will be 30-40 MB
. Ini mengklaim membaca 180 MB dalam 1,4 detik pada Intel Quad Core:Artikel asli
sumber
Anda mungkin lebih baik menggunakan penanganan file yang dipetakan memori di sini .. Dukungan file yang dipetakan memori akan ada di .NET 4 (Saya pikir ... Saya mendengarnya melalui orang lain yang membicarakannya), maka pembungkus ini yang menggunakan p / meminta untuk melakukan pekerjaan yang sama ..
Sunting: Lihat di sini di MSDN untuk cara kerjanya, berikut adalah entri blog yang menunjukkan bagaimana hal itu dilakukan di .NET 4 mendatang ketika keluar sebagai rilis. Tautan yang saya berikan sebelumnya adalah pembungkus di sekitar pinvoke untuk mencapai ini. Anda dapat memetakan seluruh file ke dalam memori, dan melihatnya seperti jendela geser saat menggulir file.
sumber
Semua jawaban luar biasa! namun, untuk seseorang yang mencari jawaban, ini tampaknya tidak lengkap.
Karena String standar hanya dapat berukuran X, 2Gb hingga 4Gb tergantung pada konfigurasi Anda, jawaban ini tidak benar-benar memenuhi pertanyaan OP. Salah satu caranya adalah bekerja dengan List of Strings:
Beberapa mungkin ingin Tokenise dan membagi garis saat memproses. Daftar String sekarang dapat berisi teks dalam volume yang sangat besar.
sumber
Sebuah iterator mungkin cocok untuk jenis pekerjaan ini:
Anda dapat menyebutnya menggunakan yang berikut ini:
Saat file dimuat, iterator akan mengembalikan nomor kemajuan dari 0 hingga 100, yang dapat Anda gunakan untuk memperbarui bilah kemajuan Anda. Setelah loop selesai, StringBuilder akan berisi konten file teks.
Selain itu, karena Anda menginginkan teks, kita cukup menggunakan BinaryReader untuk membaca karakter, yang akan memastikan bahwa buffer Anda berbaris dengan benar saat membaca karakter multi-byte ( UTF-8 , UTF-16 , dll.).
Ini semua dilakukan tanpa menggunakan tugas latar belakang, utas, atau mesin status kustom yang rumit.
sumber
File saya lebih dari 13 GB:
Tautan di bawah ini berisi kode yang membaca sebagian file dengan mudah:
Baca file teks besar
Informasi lebih lanjut
sumber