Saya mencoba menulis sejumlah besar data ke SSD saya (solid state drive). Dan dalam jumlah besar saya maksud 80GB.
Saya melihat-lihat web untuk mencari solusi, tetapi yang terbaik yang saya dapatkan adalah:
#include <fstream>
const unsigned long long size = 64ULL*1024ULL*1024ULL;
unsigned long long a[size];
int main()
{
std::fstream myfile;
myfile = std::fstream("file.binary", std::ios::out | std::ios::binary);
//Here would be some error handling
for(int i = 0; i < 32; ++i){
//Some calculations to fill a[]
myfile.write((char*)&a,size*sizeof(unsigned long long));
}
myfile.close();
}
Dikompilasi dengan Visual Studio 2010 dan optimisasi penuh dan berjalan di bawah Windows7, program ini mencapai maksimal 20MB / s. Yang benar-benar mengganggu saya adalah bahwa Windows dapat menyalin file dari SSD lain ke SSD ini di suatu tempat antara 150MB / s dan 200MB / s. Jadi setidaknya 7 kali lebih cepat. Itu sebabnya saya pikir saya harus bisa lebih cepat.
Adakah ide bagaimana saya bisa mempercepat tulisan saya?
c++
performance
optimization
file-io
io
Dominic Hofer
sumber
sumber
fwrite()
saya bisa mendapatkan sekitar 80% dari kecepatan menulis puncak. Hanya denganFILE_FLAG_NO_BUFFERING
itu saya bisa mendapatkan kecepatan maksimal.Jawaban:
Ini berhasil (pada tahun 2012):
Saya baru saja menghitung waktu 8GB dalam 36detik, yaitu sekitar 220MB / s dan saya pikir itu memaksimalkan SSD saya. Juga patut dicatat, kode dalam pertanyaan menggunakan satu inti 100%, sedangkan kode ini hanya menggunakan 2-5%.
Terima kasih banyak untuk semuanya.
Pembaruan : 5 tahun telah berlalu sekarang 2017 sekarang. Kompiler, perangkat keras, perpustakaan, dan persyaratan saya telah berubah. Itu sebabnya saya membuat beberapa perubahan pada kode dan melakukan beberapa pengukuran baru.
Pertama-tama kode:
Kode ini dikompilasi dengan Visual Studio 2017 dan g ++ 7.2.0 (persyaratan baru). Saya menjalankan kode dengan dua pengaturan:
Yang memberikan pengukuran berikut (setelah membuang nilai untuk 1MB, karena mereka outlier jelas): Kedua kali option1 dan option3 max out SSD saya. Saya tidak mengharapkan ini untuk melihat, karena option2 digunakan untuk menjadi kode tercepat di mesin lama saya saat itu.
TL; DR : Pengukuran saya menunjukkan penggunaan yang
std::fstream
berlebihanFILE
.sumber
FILE*
lebih cepat dari stream. Saya tidak akan mengharapkan perbedaan seperti itu karena "seharusnya" saya I / O terikat pula.ios::sync_with_stdio(false);
membuat perbedaan untuk kode dengan stream? Saya hanya ingin tahu seberapa besar perbedaan antara menggunakan baris ini dan tidak, tetapi saya tidak memiliki disk yang cukup cepat untuk memeriksa kasing sudut. Dan jika ada perbedaan nyata.Coba yang berikut ini, secara berurutan:
Ukuran buffer lebih kecil. Menulis ~ 2 MiB sekaligus mungkin merupakan awal yang baik. Di laptop terakhir saya, ~ 512 KiB adalah sweet spot, tapi saya belum menguji SSD saya.
Catatan: Saya perhatikan bahwa buffer yang sangat besar cenderung menurunkan kinerja. Saya telah memperhatikan kehilangan kecepatan dengan menggunakan buffer 16-MiB daripada buffer 512-KiB sebelumnya.
Gunakan
_open
(atau_topen
jika Anda ingin menjadi Windows-benar) untuk membuka file, kemudian gunakan_write
. Ini mungkin akan menghindari banyak buffering, tetapi tidak pasti.Menggunakan fungsi khusus Windows seperti
CreateFile
danWriteFile
. Itu akan menghindari buffering di perpustakaan standar.sumber
FILE_FLAG_NO_BUFFERING
- di mana buffer yang lebih besar cenderung lebih baik. Karena saya pikirFILE_FLAG_NO_BUFFERING
cukup banyak DMA.Saya tidak melihat perbedaan antara std :: stream / FILE / perangkat. Antara buffering dan non buffering.
Juga mencatat:
Saya melihat kode dijalankan dalam 63 detik.
Dengan demikian kecepatan transfer: 260M / s (SSD saya terlihat sedikit lebih cepat dari milik Anda).
Saya tidak mendapatkan peningkatan dengan beralih ke FILE * dari std :: fstream.
Jadi aliran C ++ berfungsi secepat yang dibolehkan oleh pustaka yang mendasarinya.
Tapi saya pikir itu tidak adil membandingkan OS dengan aplikasi yang dibangun di atas OS. Aplikasi tidak dapat membuat asumsi (tidak tahu drive adalah SSD) dan dengan demikian menggunakan mekanisme file OS untuk transfer.
Sementara OS tidak perlu membuat asumsi apa pun. Ini dapat memberi tahu jenis drive yang terlibat dan menggunakan teknik optimal untuk mentransfer data. Dalam hal ini memori langsung ke transfer memori. Cobalah menulis program yang menyalin 80G dari 1 lokasi di memori ke yang lain dan lihat seberapa cepat itu.
Edit
Saya mengubah kode saya untuk menggunakan panggilan tingkat bawah:
yaitu tidak ada buffering.
Ini tidak membuat perbedaan.
CATATAN : Drive saya adalah drive SSD jika Anda memiliki drive normal, Anda mungkin melihat perbedaan antara kedua teknik di atas. Tapi seperti yang saya harapkan non buffering dan buffering (ketika menulis potongan besar lebih besar dari ukuran buffer) tidak ada bedanya.
Edit 2:
Sudahkah Anda mencoba metode menyalin file tercepat di C ++
sumber
FILE*
.Solusi terbaik adalah dengan mengimplementasikan penulisan async dengan buffering ganda.
Lihatlah garis waktu:
'F' mewakili waktu untuk mengisi buffer, dan 'W' mewakili waktu untuk menulis buffer ke disk. Jadi masalah dalam membuang waktu antara menulis buffer ke file. Namun, dengan menerapkan penulisan pada utas terpisah, Anda dapat mulai mengisi buffer berikutnya segera seperti ini:
F - mengisi buffer 1
f - mengisi buffer 2
W - menulis buffer 1 ke file
w - menulis buffer 2 ke file
_ - menunggu sementara operasi selesai
Pendekatan ini dengan swap swap sangat berguna ketika mengisi buffer membutuhkan perhitungan yang lebih kompleks (karenanya, lebih banyak waktu). Saya selalu menerapkan kelas CSequentialStreamWriter yang menyembunyikan tulisan asinkron di dalamnya, jadi untuk pengguna akhir antarmuka baru saja menulis fungsi.
Dan ukuran buffer harus kelipatan dari ukuran cluster disk. Jika tidak, Anda akan berakhir dengan kinerja yang buruk dengan menulis buffer tunggal ke 2 kluster disk yang berdekatan.
Menulis buffer terakhir.
Ketika Anda memanggil fungsi Write untuk yang terakhir kali, Anda harus memastikan bahwa buffer saat ini sedang diisi harus ditulis ke disk juga. Jadi CSequentialStreamWriter harus memiliki metode terpisah, katakanlah Finalize (final buffer flush), yang harus menulis ke disk bagian terakhir dari data.
Menangani kesalahan.
Sementara kode mulai mengisi buffer ke-2, dan yang ke-1 sedang ditulis di utas terpisah, tetapi penulisan gagal karena beberapa alasan, utas utama harus menyadari kegagalan itu.
Mari kita asumsikan antarmuka CSequentialStreamWriter memiliki fungsi Write mengembalikan bool atau melempar pengecualian, sehingga memiliki kesalahan pada utas terpisah, Anda harus mengingat keadaan itu, jadi lain kali Anda memanggil Tulis atau Finilize pada utas utama, metode tersebut akan kembali Salah atau akan melempar pengecualian. Dan tidak masalah pada titik mana Anda berhenti mengisi buffer, bahkan jika Anda menulis beberapa data di depan setelah kegagalan - kemungkinan besar file tersebut akan rusak dan tidak berguna.
sumber
Saya sarankan mencoba pemetaan file . Saya menggunakan
mmap
di masa lalu, di lingkungan UNIX, dan saya terkesan dengan kinerja tinggi yang bisa saya capaisumber
Bisakah Anda menggunakan
FILE*
sebagai gantinya, dan mengukur kinerja yang Anda peroleh? Beberapa opsi adalah menggunakanfwrite/write
alih-alihfstream
:Jika Anda memutuskan untuk menggunakan
write
, coba sesuatu yang serupa:Saya juga menyarankan Anda untuk melihat
memory map
. Itu mungkin jawaban Anda. Pernah saya harus memproses file 20GB di lain untuk menyimpannya di database, dan file tersebut bahkan tidak membuka. Jadi solusinya dengan memanfaatkan peta moemory. Saya melakukan ituPython
.sumber
FILE*
setara langsung dari kode asli menggunakan buffer 512 MB yang sama mendapatkan kecepatan penuh. Buffer Anda saat ini terlalu kecil.2
sesuai dengan kesalahan standar tetapi masih disarankan agar Anda menggunakannyaSTDERR_FILENO
sebagai ganti2
. Masalah penting lainnya adalah bahwa satu kemungkinan kesalahan yang bisa Anda dapatkan adalah EINTR karena ketika Anda menerima sinyal interupsi, ini bukan kesalahan nyata dan Anda harus mencoba lagi.Coba gunakan open () / write () / close () panggilan API dan coba ukuran buffer output. Maksud saya jangan melewati buffer "banyak-banyak-byte" sekaligus, lakukan beberapa penulisan (yaitu, TotalNumBytes / OutBufferSize). OutBufferSize bisa dari 4096 byte ke megabyte.
Coba lagi - gunakan WinAPI OpenFile / CreateFile dan gunakan artikel MSDN ini untuk mematikan buffering (FILE_FLAG_NO_BUFFERING). Dan artikel MSDN ini di WriteFile () menunjukkan cara mendapatkan ukuran blok untuk drive untuk mengetahui ukuran buffer optimal.
Bagaimanapun, std :: ofstream adalah pembungkus dan mungkin ada pemblokiran pada operasi I / O. Perlu diingat bahwa melintasi seluruh array N-gigabyte juga membutuhkan waktu. Saat Anda menulis buffer kecil, ia sampai ke cache dan bekerja lebih cepat.
sumber
fstream
s tidak lebih lambat dari stream C, per se, tetapi mereka menggunakan lebih banyak CPU (terutama jika buffering tidak dikonfigurasi dengan benar). Ketika CPU jenuh, itu membatasi tingkat I / O.Setidaknya implementasi MSVC 2015 menyalin 1 karakter sekaligus ke buffer output saat buffer aliran tidak disetel (lihat
streambuf::xsputn
). Jadi pastikan untuk mengatur buffer aliran (> 0) .Saya bisa mendapatkan kecepatan tulis 1500MB / s (kecepatan penuh SSD M.2 saya) dengan
fstream
menggunakan kode ini:Saya mencoba kode ini pada platform lain (Ubuntu, FreeBSD) dan tidak melihat perbedaan tingkat I / O, tetapi perbedaan penggunaan CPU sekitar 8: 1 (
fstream
menggunakan CPU 8 kali lebih banyak ). Jadi bisa dibayangkan, seandainya saya lebih cepat disk,fstream
menulis akan melambat lebih cepat daripadastdio
versi.sumber
Jika Anda menyalin sesuatu dari disk A ke disk B di explorer, Windows menggunakan DMA. Itu berarti untuk sebagian besar proses penyalinan, CPU pada dasarnya tidak akan melakukan apa pun selain memberi tahu pengontrol disk tempat untuk meletakkan, dan mendapatkan data dari, menghilangkan seluruh langkah dalam rantai, dan salah satu yang sama sekali tidak dioptimalkan untuk memindahkan sejumlah besar data - dan maksud saya perangkat keras.
Apa yang Anda lakukan melibatkan banyak CPU. Saya ingin mengarahkan Anda ke bagian "Beberapa perhitungan untuk mengisi []". Yang menurut saya sangat penting. Anda menghasilkan [], lalu Anda menyalin dari [] ke buffer output (itulah yang dilakukan oleh fstream :: write), kemudian Anda menghasilkan lagi, dll.
Apa yang harus dilakukan? Multithreading! (Saya harap Anda memiliki prosesor multi-core)
sumber
Coba gunakan file yang dipetakan memori.
sumber
ReadFile
dan dengan demikian untuk akses berurutan, meskipun untuk akses acak mereka mungkin lebih baik.Jika Anda ingin menulis dengan cepat ke file stream maka Anda dapat membuat streaming buffer baca menjadi lebih besar:
Juga, ketika menulis banyak data ke file kadang-kadang lebih cepat secara logis memperluas ukuran file daripada fisik, ini karena ketika secara logis memperluas file sistem file tidak nol ruang baru sebelum menulis untuk itu. Ini juga pintar untuk secara logis memperpanjang file lebih dari yang sebenarnya Anda butuhkan untuk mencegah banyak perluasan file. Ekstensi file logis didukung pada Windows dengan memanggil
SetFileValidData
atauxfsctl
denganXFS_IOC_RESVSP64
sistem XFS.sumber
im mengkompilasi program saya di gcc di GNU / Linux dan mingw di win 7 dan win xp dan bekerja dengan baik
Anda dapat menggunakan program saya dan untuk membuat file 80 GB cukup ganti baris 33 menjadi
ketika keluar dari program file akan dihancurkan kemudian periksa file ketika sedang berjalan
untuk memiliki program yang Anda inginkan, ubah saja program tersebut
firt satu adalah program windows dan yang kedua adalah untuk GNU / Linux
http://mustafajf.persiangig.com/Projects/File/WinFile.cpp
http://mustafajf.persiangig.com/Projects/File/File.cpp
sumber