Bagaimana cara menulis buffer besar ke file biner di C ++, cepat?

242

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?

Dominic Hofer
sumber
11
Apakah hasil penghitungan waktu Anda mengecualikan waktu yang diperlukan untuk melakukan perhitungan Anda untuk mengisi []?
catchmeifyoutry
7
Saya sebenarnya sudah melakukan tugas ini sebelumnya. Dengan menggunakan sederhana fwrite()saya bisa mendapatkan sekitar 80% dari kecepatan menulis puncak. Hanya dengan FILE_FLAG_NO_BUFFERINGitu saya bisa mendapatkan kecepatan maksimal.
Mysticial
10
Saya tidak yakin itu adil untuk membandingkan file Anda menulis ke menyalin SSD-ke-SSD. Mungkin saja SSD-ke-SSD berfungsi pada level yang lebih rendah, menghindari perpustakaan C ++, atau menggunakan akses memori langsung (DMA). Menyalin sesuatu tidak sama dengan menulis nilai acak ke file akses acak.
Igor F.
4
@IgorF .: Itu hanya spekulasi yang salah; ini adalah perbandingan yang sangat adil (jika tidak ada yang lain, mendukung penulisan file). Menyalin di drive di Windows hanya baca-dan-tulis; tidak ada yang mewah / rumit / berbeda yang terjadi di bawahnya.
user541686
5
@ MaximYegorushkin: Tautan atau tidak terjadi. : P
user541686

Jawaban:

233

Ini berhasil (pada tahun 2012):

#include <stdio.h>
const unsigned long long size = 8ULL*1024ULL*1024ULL;
unsigned long long a[size];

int main()
{
    FILE* pFile;
    pFile = fopen("file.binary", "wb");
    for (unsigned long long j = 0; j < 1024; ++j){
        //Some calculations to fill a[]
        fwrite(a, 1, size*sizeof(unsigned long long), pFile);
    }
    fclose(pFile);
    return 0;
}

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:

#include <fstream>
#include <chrono>
#include <vector>
#include <cstdint>
#include <numeric>
#include <random>
#include <algorithm>
#include <iostream>
#include <cassert>

std::vector<uint64_t> GenerateData(std::size_t bytes)
{
    assert(bytes % sizeof(uint64_t) == 0);
    std::vector<uint64_t> data(bytes / sizeof(uint64_t));
    std::iota(data.begin(), data.end(), 0);
    std::shuffle(data.begin(), data.end(), std::mt19937{ std::random_device{}() });
    return data;
}

long long option_1(std::size_t bytes)
{
    std::vector<uint64_t> data = GenerateData(bytes);

    auto startTime = std::chrono::high_resolution_clock::now();
    auto myfile = std::fstream("file.binary", std::ios::out | std::ios::binary);
    myfile.write((char*)&data[0], bytes);
    myfile.close();
    auto endTime = std::chrono::high_resolution_clock::now();

    return std::chrono::duration_cast<std::chrono::milliseconds>(endTime - startTime).count();
}

long long option_2(std::size_t bytes)
{
    std::vector<uint64_t> data = GenerateData(bytes);

    auto startTime = std::chrono::high_resolution_clock::now();
    FILE* file = fopen("file.binary", "wb");
    fwrite(&data[0], 1, bytes, file);
    fclose(file);
    auto endTime = std::chrono::high_resolution_clock::now();

    return std::chrono::duration_cast<std::chrono::milliseconds>(endTime - startTime).count();
}

long long option_3(std::size_t bytes)
{
    std::vector<uint64_t> data = GenerateData(bytes);

    std::ios_base::sync_with_stdio(false);
    auto startTime = std::chrono::high_resolution_clock::now();
    auto myfile = std::fstream("file.binary", std::ios::out | std::ios::binary);
    myfile.write((char*)&data[0], bytes);
    myfile.close();
    auto endTime = std::chrono::high_resolution_clock::now();

    return std::chrono::duration_cast<std::chrono::milliseconds>(endTime - startTime).count();
}

int main()
{
    const std::size_t kB = 1024;
    const std::size_t MB = 1024 * kB;
    const std::size_t GB = 1024 * MB;

    for (std::size_t size = 1 * MB; size <= 4 * GB; size *= 2) std::cout << "option1, " << size / MB << "MB: " << option_1(size) << "ms" << std::endl;
    for (std::size_t size = 1 * MB; size <= 4 * GB; size *= 2) std::cout << "option2, " << size / MB << "MB: " << option_2(size) << "ms" << std::endl;
    for (std::size_t size = 1 * MB; size <= 4 * GB; size *= 2) std::cout << "option3, " << size / MB << "MB: " << option_3(size) << "ms" << std::endl;

    return 0;
}

Kode ini dikompilasi dengan Visual Studio 2017 dan g ++ 7.2.0 (persyaratan baru). Saya menjalankan kode dengan dua pengaturan:

  • Laptop, Core i7, SSD, Ubuntu 16.04, g ++ Versi 7.2.0 dengan -std = c ++ 11 -march = asli -O3
  • Desktop, Core i7, SSD, Windows 10, Visual Studio 2017 Versi 15.3.1 dengan / Ox / Ob2 / Oi / Ot / GT / GL / Gy

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.masukkan deskripsi gambar di sini masukkan deskripsi gambar di sini

TL; DR : Pengukuran saya menunjukkan penggunaan yang std::fstreamberlebihan FILE.

Dominic Hofer
sumber
8
+1 Ya, ini adalah hal pertama yang saya coba. FILE*lebih cepat dari stream. Saya tidak akan mengharapkan perbedaan seperti itu karena "seharusnya" saya I / O terikat pula.
Mysticial
12
Bisakah kita menyimpulkan bahwa C-style I / O (anehnya) jauh lebih cepat daripada stream C ++?
SChepurin
22
@ Chepurin: Jika Anda bertingkah aneh, mungkin tidak. Jika Anda bersikap praktis, mungkin ya. :)
user541686
10
Bisakah Anda jelaskan (untuk C ++ bodoh seperti saya) perbedaan antara dua pendekatan, dan mengapa ini bekerja jauh lebih cepat daripada yang asli?
Mike Chamberlain
11
Apakah prepending 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.
Artur Czajka
24

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 _topenjika 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 CreateFiledan WriteFile. Itu akan menghindari buffering di perpustakaan standar.

pengguna541686
sumber
Periksa hasil benchmark apa pun yang diposting secara online. Anda perlu menulis 4kB dengan kedalaman antrian 32 atau lebih, atau 512K atau lebih tinggi menulis, untuk mendapatkan segala jenis throughput yang layak.
Ben Voigt
@ BenVoigt: Yup, itu berkorelasi dengan saya mengatakan 512 KiB adalah titik yang manis bagi saya. :)
user541686
Iya. Dari pengalaman saya, ukuran buffer yang lebih kecil biasanya optimal. Pengecualian adalah ketika Anda menggunakan FILE_FLAG_NO_BUFFERING- di mana buffer yang lebih besar cenderung lebih baik. Karena saya pikir FILE_FLAG_NO_BUFFERINGcukup banyak DMA.
Mysticial
22

Saya tidak melihat perbedaan antara std :: stream / FILE / perangkat. Antara buffering dan non buffering.

Juga mencatat:

  • Drive SSD "cenderung" melambat (transfer rate lebih rendah) saat terisi penuh.
  • Drive SSD "cenderung" melambat (transfer rate lebih rendah) karena semakin tua (karena bit yang tidak berfungsi).

Saya melihat kode dijalankan dalam 63 detik.
Dengan demikian kecepatan transfer: 260M / s (SSD saya terlihat sedikit lebih cepat dari milik Anda).

64 * 1024 * 1024 * 8 /*sizeof(unsigned long long) */ * 32 /*Chunks*/

= 16G
= 16G/63 = 260M/s

Saya tidak mendapatkan peningkatan dengan beralih ke FILE * dari std :: fstream.

#include <stdio.h>

using namespace std;

int main()
{
    
    FILE* stream = fopen("binary", "w");

    for(int loop=0;loop < 32;++loop)
    {
         fwrite(a, sizeof(unsigned long long), size, stream);
    }
    fclose(stream);

}

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.

#include <fcntl.h>
#include <unistd.h>


const unsigned long long size = 64ULL*1024ULL*1024ULL;
unsigned long long a[size];
int main()
{
    int data = open("test", O_WRONLY | O_CREAT, 0777);
    for(int loop = 0; loop < 32; ++loop)
    {   
        write(data, a, size * sizeof(unsigned long long));
    }   
    close(data);
}

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 ++

int main()
{
    std::ifstream  input("input");
    std::ofstream  output("ouptut");

    output << input.rdbuf();
}
Martin York
sumber
5
Saya tidak mengunduh, tetapi ukuran buffer Anda terlalu kecil. Saya melakukannya dengan buffer 512 MB yang sama yang digunakan OP dan saya mendapatkan 20 MB / s dengan stream vs 90 MB / s dengan FILE*.
Mysticial
Juga caranya dengan fwrite (a, sizeof (unsigned long long), size, stream); bukannya fwrite (a, 1, size * sizeof (unsigned long long), pFile); memberi saya 220MB / s dengan potongan 64MB per tulis.
Dominic Hofer
2
@Mysticial: Ini mengejutkan saya bahwa ukuran buffer membuat perbedaan (meskipun saya percaya Anda). Buffer berguna ketika Anda memiliki banyak tulisan kecil sehingga perangkat yang mendasarinya tidak terganggu dengan banyak permintaan. Tetapi ketika Anda menulis potongan besar tidak perlu buffer ketika menulis / membaca (pada perangkat pemblokiran). Dengan demikian data harus diteruskan langsung ke perangkat yang mendasarinya (dengan demikian melewati buffer). Meskipun jika Anda melihat perbedaan ini akan bertentangan dengan ini dan membuat saya bertanya-tanya mengapa penulisan sebenarnya menggunakan buffer sama sekali.
Martin York
2
Solusi terbaik adalah BUKAN untuk meningkatkan ukuran buffer tetapi untuk menghapus buffer dan membuat tuliskan data secara langsung ke perangkat yang mendasarinya.
Martin York
1
@Mysticial: 1) Tidak ada potongan kecil => Itu selalu cukup besar (dalam contoh ini). Dalam hal ini potongan adalah 512M 2) Ini adalah drive SSD (baik milik saya maupun OP) sehingga tidak ada yang relevan. Saya telah memperbarui jawaban saya.
Martin York
13

Solusi terbaik adalah dengan mengimplementasikan penulisan async dengan buffering ganda.

Lihatlah garis waktu:

------------------------------------------------>
FF|WWWWWWWW|FF|WWWWWWWW|FF|WWWWWWWW|FF|WWWWWWWW|

'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:

------------------------------------------------> (main thread, fills buffers)
FF|ff______|FF______|ff______|________|
------------------------------------------------> (writer thread)
  |WWWWWWWW|wwwwwwww|WWWWWWWW|wwwwwwww|

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.

------------------------------------------------> (main thread, fills buffers)
FF|fX|
------------------------------------------------> (writer thread)
__|X|

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.

HandMadeOX
sumber
3
Performing I / O adalah paralel dengan perhitungan adalah ide yang sangat bagus, tetapi pada Windows Anda tidak harus menggunakan utas untuk mencapainya. Sebagai gantinya, gunakan "I / O yang tumpang tindih", yang tidak memblokir salah satu utas Anda selama panggilan I / O. Ini berarti Anda tidak perlu khawatir tentang sinkronisasi utas (hanya saja tidak mengakses buffer yang memiliki operasi I / O aktif menggunakannya).
Ben Voigt
11

Saya sarankan mencoba pemetaan file . Saya menggunakan mmapdi masa lalu, di lingkungan UNIX, dan saya terkesan dengan kinerja tinggi yang bisa saya capai

Muntah
sumber
1
@nalply Ini masih solusi yang bekerja, efisien dan menarik untuk diingat.
Yam Marcovic
stackoverflow.com/a/2895799/220060 tentang pro dan kontra dari mmap. Terutama catatan "Untuk akses berurutan murni ke file, itu juga tidak selalu merupakan solusi yang lebih baik [...]" Juga stackoverflow.com/questions/726471 , secara efektif mengatakan bahwa pada sistem 32-bit Anda dibatasi 2 atau 3 GB. - Ngomong-ngomong, bukan aku yang menurunkan jawaban itu.
nalply
8

Bisakah Anda menggunakan FILE*sebagai gantinya, dan mengukur kinerja yang Anda peroleh? Beberapa opsi adalah menggunakan fwrite/writealih-alih fstream:

#include <stdio.h>

int main ()
{
  FILE * pFile;
  char buffer[] = { 'x' , 'y' , 'z' };
  pFile = fopen ( "myfile.bin" , "w+b" );
  fwrite (buffer , 1 , sizeof(buffer) , pFile );
  fclose (pFile);
  return 0;
}

Jika Anda memutuskan untuk menggunakan write, coba sesuatu yang serupa:

#include <unistd.h>
#include <fcntl.h>

int main(void)
{
    int filedesc = open("testfile.txt", O_WRONLY | O_APPEND);

    if (filedesc < 0) {
        return -1;
    }

    if (write(filedesc, "This will be output to testfile.txt\n", 36) != 36) {
        write(2, "There was an error writing to testfile.txt\n", 43);
        return -1;
    }

    return 0;
}

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 itu Python.

cybertextron
sumber
Sebenarnya, FILE*setara langsung dari kode asli menggunakan buffer 512 MB yang sama mendapatkan kecepatan penuh. Buffer Anda saat ini terlalu kecil.
Mysticial
1
@Mysticial Tapi itu hanya sebuah contoh.
cybertextron
Di sebagian besar sistem, 2sesuai dengan kesalahan standar tetapi masih disarankan agar Anda menggunakannya STDERR_FILENOsebagai ganti 2. 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.
Peyman
6

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.

Viktor Latypov
sumber
6

fstreams 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 fstreammenggunakan kode ini:

#include <iostream>
#include <fstream>
#include <chrono>
#include <memory>
#include <stdio.h>
#ifdef __linux__
#include <unistd.h>
#endif
using namespace std;
using namespace std::chrono;
const size_t sz = 512 * 1024 * 1024;
const int numiter = 20;
const size_t bufsize = 1024 * 1024;
int main(int argc, char**argv)
{
  unique_ptr<char[]> data(new char[sz]);
  unique_ptr<char[]> buf(new char[bufsize]);
  for (size_t p = 0; p < sz; p += 16) {
    memcpy(&data[p], "BINARY.DATA.....", 16);
  }
  unlink("file.binary");
  int64_t total = 0;
  if (argc < 2 || strcmp(argv[1], "fopen") != 0) {
    cout << "fstream mode\n";
    ofstream myfile("file.binary", ios::out | ios::binary);
    if (!myfile) {
      cerr << "open failed\n"; return 1;
    }
    myfile.rdbuf()->pubsetbuf(buf.get(), bufsize); // IMPORTANT
    for (int i = 0; i < numiter; ++i) {
      auto tm1 = high_resolution_clock::now();
      myfile.write(data.get(), sz);
      if (!myfile)
        cerr << "write failed\n";
      auto tm = (duration_cast<milliseconds>(high_resolution_clock::now() - tm1).count());
      cout << tm << " ms\n";
      total += tm;
    }
    myfile.close();
  }
  else {
    cout << "fopen mode\n";
    FILE* pFile = fopen("file.binary", "wb");
    if (!pFile) {
      cerr << "open failed\n"; return 1;
    }
    setvbuf(pFile, buf.get(), _IOFBF, bufsize); // NOT important
    auto tm1 = high_resolution_clock::now();
    for (int i = 0; i < numiter; ++i) {
      auto tm1 = high_resolution_clock::now();
      if (fwrite(data.get(), sz, 1, pFile) != 1)
        cerr << "write failed\n";
      auto tm = (duration_cast<milliseconds>(high_resolution_clock::now() - tm1).count());
      cout << tm << " ms\n";
      total += tm;
    }
    fclose(pFile);
    auto tm2 = high_resolution_clock::now();
  }
  cout << "Total: " << total << " ms, " << (sz*numiter * 1000 / (1024.0 * 1024 * total)) << " MB/s\n";
}

Saya mencoba kode ini pada platform lain (Ubuntu, FreeBSD) dan tidak melihat perbedaan tingkat I / O, tetapi perbedaan penggunaan CPU sekitar 8: 1 ( fstreammenggunakan CPU 8 kali lebih banyak ). Jadi bisa dibayangkan, seandainya saya lebih cepat disk, fstreammenulis akan melambat lebih cepat daripada stdioversi.

rustyx
sumber
3

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)

  • garpu.
  • Gunakan satu utas untuk menghasilkan data []
  • Gunakan yang lain untuk menulis data dari [] ke disk
  • Anda akan membutuhkan dua array a1 [] dan a2 [] dan beralih di antara mereka
  • Anda akan memerlukan semacam sinkronisasi antara utas Anda (semaphores, antrian pesan, dll.)
  • Gunakan fungsi level yang lebih rendah, tidak dibuat-buat, seperti fungsi WriteFile yang disebutkan oleh Mehrdad
ganda
sumber
2

Coba gunakan file yang dipetakan memori.

qehgt
sumber
@Madrdad tapi mengapa? Karena itu solusi yang bergantung pada platform?
qehgt
3
Tidak ... itu karena untuk melakukan penulisan file sekuensial cepat, Anda perlu menulis sejumlah besar data sekaligus. (Katakanlah, potongan 2-MiB mungkin merupakan titik awal yang baik.) File yang dipetakan dengan memori tidak memungkinkan Anda mengontrol granularitas, jadi Anda berada di bawah kekuasaan apa pun yang diputuskan oleh manajer memori untuk prefetch / buffer untuk Anda. Secara umum, saya belum pernah melihat mereka seefektif membaca / menulis normal ReadFiledan dengan demikian untuk akses berurutan, meskipun untuk akses acak mereka mungkin lebih baik.
user541686
Tetapi file yang dipetakan dengan memori digunakan oleh OS untuk paging, misalnya. Saya pikir ini adalah cara yang sangat optimal (dalam hal kecepatan) untuk membaca / menulis data.
qehgt
7
@Mysticial: Orang-orang tahu "banyak hal yang benar-benar salah.
Ben Voigt
1
@ qehgt: Jika ada, paging lebih dioptimalkan untuk akses acak daripada akses berurutan. Membaca 1 halaman data jauh lebih lambat daripada membaca 1 megabyte data dalam satu operasi.
user541686
1

Jika Anda ingin menulis dengan cepat ke file stream maka Anda dapat membuat streaming buffer baca menjadi lebih besar:

wfstream f;
const size_t nBufferSize = 16184;
wchar_t buffer[nBufferSize];
f.rdbuf()->pubsetbuf(buffer, nBufferSize);

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 SetFileValidDataatau xfsctldengan XFS_IOC_RESVSP64sistem XFS.


sumber
0

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

makeFile("Text.txt",1024,8192000);

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

Etaan
sumber