Come scrivere un buffer di grandi dimensioni in un file binario in C ++, velocemente?


242

Sto cercando di scrivere enormi quantità di dati sul mio SSD (unità a stato solido). E per grandi quantità intendo 80 GB.

Ho navigato sul web alla ricerca di soluzioni, ma la cosa migliore che mi è venuta in mente è stata questa:

#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();
}

Compilato con Visual Studio 2010 e ottimizzazioni complete ed eseguito su Windows 7, questo programma raggiunge un massimo di 20 MB / s. Ciò che mi preoccupa davvero è che Windows può copiare i file da un altro SSD a questo SSD in un punto compreso tra 150 MB / se 200 MB / s. Quindi almeno 7 volte più veloce. Ecco perché penso che dovrei essere in grado di andare più veloce.

Qualche idea su come posso accelerare la mia scrittura?


11
I tuoi risultati di temporizzazione hanno escluso il tempo necessario per eseguire i tuoi calcoli per riempire un []?
catchmeifyoutry,

7
In realtà ho già svolto questo compito. Usando semplici fwrite()ho potuto ottenere circa l'80% delle velocità di scrittura di picco. Solo con sono FILE_FLAG_NO_BUFFERINGstato in grado di ottenere la massima velocità.
Mistico il

10
Non sono sicuro che sia giusto confrontare la scrittura del tuo file con una copia da SSD a SSD. È possibile che SSD-SSD funzioni a un livello inferiore, evitando le librerie C ++ o utilizzando l'accesso diretto alla memoria (DMA). Copiare qualcosa non equivale a scrivere valori arbitrari in un file ad accesso casuale.
Igor F.

4
@IgorF .: È solo una speculazione sbagliata; è un confronto perfettamente equo (se non altro, a favore della scrittura di file). La copia su un'unità in Windows è solo lettura e scrittura; niente di speciale / complicato / diverso succede sotto.
user541686

5
@MaximYegorushkin: collegamento o non è accaduto. : P
user541686

Risposte:


233

Questo ha fatto il lavoro (nell'anno 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;
}

Ho appena cronometrato 8 GB in 36sec, che è di circa 220 MB / se penso che massimizzi il mio SSD. Vale anche la pena notare che il codice nella domanda utilizzava un core al 100%, mentre questo codice utilizza solo il 2-5%.

Grazie mille a tutti

Aggiornamento : sono passati 5 anni è il 2017 ora. Compilatori, hardware, librerie e i miei requisiti sono cambiati. Ecco perché ho apportato alcune modifiche al codice e fatto alcune nuove misurazioni.

Innanzitutto il codice:

#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;
}

Questo codice viene compilato con Visual Studio 2017 e g ++ 7.2.0 (nuovi requisiti). Ho eseguito il codice con due configurazioni:

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

Che ha dato le seguenti misurazioni (dopo aver abbandonato i valori per 1 MB, perché erano ovvi valori anomali): Entrambe le volte l'opzione 1 e l'opzione 3 hanno superato il mio SSD. Non mi aspettavo che questo vedesse, perché allora all'opzione2 era il codice più veloce sulla mia vecchia macchina.inserisci qui la descrizione dell'immagine inserisci qui la descrizione dell'immagine

TL; DR : le mie misurazioni indicano di utilizzare std::fstreamoltre FILE.


8
+1 Sì, questa è stata la prima cosa che ho provato. FILE*è più veloce dei flussi. Non mi sarei aspettato una differenza del genere, dal momento che "avrebbe dovuto" essere collegato in ogni caso a I / O.
Mistico il

12
Possiamo concludere che l'I / O in stile C è (stranamente) molto più veloce dei flussi C ++?
SChepurin,

22
@SChepurin: se sei pedante, probabilmente no. Se sei pratico, probabilmente sì. :)
user541686

10
Potresti spiegare (per un somaro C ++ come me) la differenza tra i due approcci e perché questo funziona molto più velocemente dell'originale?
Mike Chamberlain,

11
Anticipare ios::sync_with_stdio(false);fa la differenza per il codice con stream? Sono solo curioso di sapere quanto sia grande la differenza tra l'uso di questa linea e non, ma non ho il disco abbastanza veloce per controllare il case d'angolo. E se c'è qualche vera differenza.
Artur Czajka,

24

Prova quanto segue, in ordine:

  • Dimensione del buffer più piccola. Scrivere ~ 2 MiB alla volta potrebbe essere un buon inizio. Sul mio ultimo laptop, ~ 512 KiB è stato il punto debole, ma non ho ancora testato sul mio SSD.

    Nota: ho notato che buffer molto grandi tendono a ridurre le prestazioni. Ho notato perdite di velocità con l'utilizzo di buffer da 16 MiB invece di buffer da 512 KiB in precedenza.

  • Utilizzare _open(o _topense si desidera essere corretti per Windows) per aprire il file, quindi utilizzare _write. Questo probabilmente eviterà un sacco di buffering, ma non è sicuro.

  • Utilizzo di funzioni specifiche di Windows come CreateFilee WriteFile. Ciò eviterà qualsiasi buffering nella libreria standard.


Controlla i risultati di benchmark pubblicati online. Sono necessarie scritture da 4kB con una profondità della coda di 32 o superiore, oppure scritture da 512K o superiori, per ottenere qualsiasi tipo di throughput decente.
Ben Voigt,

@BenVoigt: Sì, ciò si correla con me dicendo che 512 KiB sono stati il ​​punto debole per me. :)
user541686

Sì. In base alla mia esperienza, dimensioni di buffer inferiori sono generalmente ottimali. L'eccezione è quando stai usando FILE_FLAG_NO_BUFFERING- in cui i buffer più grandi tendono ad essere migliori. Dal momento che penso FILE_FLAG_NO_BUFFERINGsia praticamente DMA.
Mistico il

22

Non vedo alcuna differenza tra std :: stream / FILE / device. Tra buffering e non buffering.

Nota anche:

  • Le unità SSD "tendono" a rallentare (velocità di trasferimento inferiori) mentre si riempiono.
  • Le unità SSD "tendono" a rallentare (velocità di trasferimento inferiori) man mano che invecchiano (a causa di bit non funzionanti).

Sto vedendo il codice eseguito in 63 secondi.
Quindi una velocità di trasferimento di: 260 M / s (il mio SSD sembra leggermente più veloce del tuo).

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

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

Non ottengo alcun aumento passando a FILE * da 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);

}

Quindi il flusso C ++ funziona alla velocità consentita dalla libreria sottostante.

Ma penso che sia ingiusto confrontare il sistema operativo con un'applicazione costruita sopra il sistema operativo. L'applicazione non può fare ipotesi (non sa che le unità sono SSD) e quindi utilizza i meccanismi di file del sistema operativo per il trasferimento.

Mentre il sistema operativo non ha bisogno di fare ipotesi. Può dire i tipi di unità coinvolte e utilizzare la tecnica ottimale per il trasferimento dei dati. In questo caso un trasferimento diretto da memoria a memoria. Prova a scrivere un programma che copia 80G da 1 posizione in memoria a un'altra e vedi quanto è veloce.

modificare

Ho modificato il mio codice per utilizzare le chiamate di livello inferiore:
ovvero nessun 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);
}

Questo non ha fatto alcuna differenza.

NOTA : la mia unità è un'unità SSD se si dispone di un'unità normale, è possibile notare una differenza tra le due tecniche sopra. Ma come mi aspettavo, il buffering e il buffering (quando si scrivono blocchi di grandi dimensioni maggiori della dimensione del buffer) non fanno alcuna differenza.

Modifica 2:

Hai provato il metodo più veloce per copiare i file in C ++

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

    output << input.rdbuf();
}

5
Non ho effettuato il downgrade, ma la dimensione del buffer è troppo piccola. L'ho fatto con lo stesso buffer da 512 MB utilizzato dall'OP e ottengo 20 MB / s con flussi contro 90 MB / s con FILE*.
Mistico il

Anche la tua strada con fwrite (a, sizeof (unsigned long long), size, stream); anziché fwrite (a, 1, size * sizeof (unsigned long long), pFile); mi dà 220 MB / s con blocchi di 64 MB per scrittura.
Dominic Hofer,

2
@Mysticial: sorprende il fatto che la dimensione del buffer faccia la differenza (anche se ti credo). Il buffer è utile quando si hanno molte piccole scritture in modo che il dispositivo sottostante non sia infastidito da molte richieste. Ma quando si scrivono enormi blocchi non è necessario un buffer durante la scrittura / lettura (su un dispositivo di blocco). Pertanto, i dati devono essere passati direttamente al dispositivo sottostante (evitando così il buffer). Tuttavia, se vedi una differenza, ciò contraddirebbe questo e mi farei meravigliare del perché la scrittura stia effettivamente utilizzando un buffer.
Martin York,

2
La soluzione migliore NON è quella di aumentare le dimensioni del buffer ma di rimuovere il buffer e fare in modo che la scrittura passi i dati direttamente al dispositivo sottostante.
Martin York,

1
@Mysticial: 1) Non ci sono piccoli blocchi => È sempre abbastanza grande (in questo esempio). In questo caso i blocchi sono 512 MB 2) Questa è un'unità SSD (sia mia che OP), quindi nulla di tutto ciò è rilevante. Ho aggiornato la mia risposta.
Martin York,

13

La soluzione migliore è implementare una scrittura asincrona con doppio buffering.

Guarda la linea del tempo:

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

La 'F' rappresenta il tempo per il riempimento del buffer e 'W' rappresenta il tempo per la scrittura del buffer su disco. Quindi il problema nello sprecare tempo tra la scrittura di buffer su file. Tuttavia, implementando la scrittura su un thread separato, puoi iniziare subito a riempire il buffer successivo in questo modo:

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

F - riempimento 1 ° buffer
f - riempimento 2 ° buffer
W - scrittura del 1 ° buffer nel file
w - scrittura del 2 ° buffer nel file
_ - attendere il completamento dell'operazione

Questo approccio con buffer swap è molto utile quando si riempie un buffer richiede un calcolo più complesso (quindi, più tempo). Implemento sempre una classe CSequentialStreamWriter che nasconde la scrittura asincrona all'interno, quindi per l'utente finale l'interfaccia ha solo funzioni di scrittura.

E la dimensione del buffer deve essere un multiplo della dimensione del cluster di dischi. Altrimenti, si finiranno con scarse prestazioni scrivendo un singolo buffer su 2 cluster di dischi adiacenti.

Scrivere l'ultimo buffer.
Quando si chiama la funzione Write per l'ultima volta, è necessario assicurarsi che anche il buffer corrente in fase di compilazione debba essere scritto sul disco. Pertanto CSequentialStreamWriter dovrebbe avere un metodo separato, diciamo Finalize (final buffer flush), che dovrebbe scrivere sul disco l'ultima porzione di dati.

Gestione degli errori.
Mentre il codice inizia a riempire il secondo buffer e il primo viene scritto su un thread separato, ma la scrittura non riesce per qualche motivo, il thread principale dovrebbe essere consapevole di tale errore.

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

Supponiamo che l'interfaccia di CSequentialStreamWriter abbia la funzione Write che ritorni a bool o generi un'eccezione, quindi avendo un errore su un thread separato, devi ricordare quello stato, quindi la prossima volta che chiami Write o Finilize sul thread principale, il metodo restituirà Falso o genererà un'eccezione. E non importa davvero a che punto hai smesso di riempire un buffer, anche se hai scritto alcuni dati in anticipo dopo l'errore - molto probabilmente il file sarebbe danneggiato e inutile.


3
L'esecuzione dell'I / O è parallela ai calcoli è una buona idea, ma su Windows non dovresti usare i thread per realizzarla. Utilizzare invece "I / O sovrapposti", che non blocca uno dei thread durante la chiamata I / O. Significa che devi a malapena preoccuparti della sincronizzazione dei thread (semplicemente non accedi a un buffer che ha un'operazione di I / O attiva che lo utilizza).
Ben Voigt,

11

Suggerirei di provare la mappatura dei file . Ho usato mmapin passato, in un ambiente UNIX, e sono rimasto impressionato dalle alte prestazioni che sono riuscito a ottenere


1
@nalply È ancora una soluzione funzionante, efficiente e interessante da tenere a mente.
Yam Marcovic,

stackoverflow.com/a/2895799/220060 sui vantaggi e gli svantaggi di mmap. Nota in particolare "Per gli accessi sequenziali puri al file, non è sempre la soluzione migliore [...]" Anche stackoverflow.com/questions/726471 , afferma effettivamente che su un sistema a 32 bit si è limitati a 2 o 3 GB. - a proposito, non sono io che ho votato a fondo su quella risposta.
nalply

8

Potresti FILE*invece utilizzare e misurare le prestazioni che hai ottenuto? Un paio di opzioni è da usare fwrite/writeinvece di 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;
}

Se decidi di utilizzare write, prova qualcosa di simile:

#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;
}

Vorrei anche consigliarvi di esaminare memory map. Questa potrebbe essere la tua risposta. Una volta ho dovuto elaborare un file da 20 GB in un altro per archiviarlo nel database e il file non si apriva nemmeno. Quindi la soluzione per utilizzare la mappa dei ricordi. L'ho fatto Pythoncomunque.


In realtà, un FILE*equivalente diretto del codice originale che utilizza lo stesso buffer da 512 MB ottiene la massima velocità. Il buffer corrente è troppo piccolo.
Mistico il

1
@Mysticial Ma questo è solo un esempio.
cybertextron,

Nella maggior parte dei sistemi, 2corrisponde all'errore standard ma si consiglia comunque di utilizzare STDERR_FILENOinvece di 2. Un altro problema importante è che un possibile errore che puoi ottenere è EINTR per quando ricevi un segnale di interruzione, questo non è un vero errore e dovresti semplicemente riprovare.
Peyman,

6

Prova a usare le chiamate API open () / write () / close () e prova le dimensioni del buffer di output. Voglio dire, non passare l'intero buffer "molti-molti-byte" contemporaneamente, fare un paio di scritture (cioè TotalNumBytes / OutBufferSize). OutBufferSize può essere compreso tra 4096 byte e megabyte.

Un altro tentativo: utilizzare WinAPI OpenFile / CreateFile e utilizzare questo articolo MSDN per disattivare il buffering (FILE_FLAG_NO_BUFFERING). E questo articolo MSDN su WriteFile () mostra come ottenere la dimensione del blocco per l'unità per conoscere la dimensione ottimale del buffer.

Comunque, std :: ofstream è un wrapper e potrebbero esserci blocchi nelle operazioni di I / O. Tieni presente che l'attraversamento dell'intero array N-gigabyte richiede anche del tempo. Mentre stai scrivendo un piccolo buffer, arriva alla cache e funziona più velocemente.


6

fstreamgli s non sono più lenti dei flussi C, di per sé, ma usano più CPU (specialmente se il buffering non è configurato correttamente). Quando una CPU si satura, limita la velocità di I / O.

Almeno l'implementazione di MSVC 2015 copia 1 carattere alla volta nel buffer di output quando non è impostato un buffer di flusso (vedere streambuf::xsputn). Quindi assicurati di impostare un buffer di flusso (> 0) .

Posso ottenere una velocità di scrittura di 1500 MB / s (la velocità massima del mio SSD M.2) fstreamutilizzando questo codice:

#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";
}

Ho provato questo codice su altre piattaforme (Ubuntu, FreeBSD) e non ho notato differenze di velocità I / O, ma una differenza di utilizzo della CPU di circa 8: 1 ( fstreamutilizzata 8 volte più CPU ). Quindi si può immaginare, se avessi un disco più veloce, la fstreamscrittura rallenterebbe prima della stdioversione.


3

Se si copia qualcosa dal disco A al disco B in Explorer, Windows utilizza DMA. Ciò significa che per la maggior parte del processo di copia, la CPU sostanzialmente non farà altro che dire al controller del disco dove posizionare e ottenere dati, eliminando un intero passaggio nella catena e uno che non è affatto ottimizzato per spostare grandi quantità di dati - e intendo hardware.

Quello che fai coinvolge molto la CPU. Voglio indicarti la parte "Alcuni calcoli per riempire una parte []". Quale penso sia essenziale. Generate un [], quindi copiate da un [] in un buffer di output (ecco cosa fa fstream :: write), quindi generate di nuovo, ecc.

Cosa fare? Multithreading! (Spero che tu abbia un processore multi-core)

  • forchetta.
  • Utilizzare un thread per generare un [] dati
  • Utilizzare l'altro per scrivere i dati da un [] su disco
  • Sono necessari due array a1 [] e a2 [] e passare da uno all'altro
  • Avrai bisogno di una sorta di sincronizzazione tra i tuoi thread (semafori, coda di messaggi, ecc.)
  • Utilizzare funzioni di livello inferiore, senza buffer, come la funzione WriteFile menzionata da Mehrdad

2

Prova a utilizzare i file mappati in memoria.


@Mehrdad ma perché? Perché è una soluzione dipendente dalla piattaforma?
qehgt,

3
No ... è perché per eseguire la scrittura rapida di file sequenziali, è necessario scrivere grandi quantità di dati contemporaneamente. (Supponiamo che blocchi di 2 MiB siano probabilmente un buon punto di partenza.) I file mappati in memoria non ti consentono di controllare la granularità, quindi sei in balia di qualsiasi cosa il gestore della memoria decida di eseguire il prefetch / buffer per te. In generale, non li ho mai visti efficaci quanto la lettura / scrittura normale ReadFilee simili per l'accesso sequenziale, anche se per l'accesso casuale potrebbero essere migliori.
user541686

Ad esempio, i file mappati in memoria vengono utilizzati dal sistema operativo per il paging. Penso che sia un modo altamente ottimizzato (in termini di velocità) per leggere / scrivere i dati.
qehgt,

7
@Mysticial: le persone "sanno" un sacco di cose che sono semplicemente sbagliate.
Ben Voigt,

1
@qehgt: semmai, il paging è molto più ottimizzato per l'accesso casuale rispetto all'accesso sequenziale. La lettura di 1 pagina di dati è molto più lenta rispetto alla lettura di 1 megabyte di dati in un'unica operazione.
user541686

1

Se si desidera scrivere velocemente su flussi di file, è possibile aumentare lo streaming del buffer di lettura:

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

Inoltre, quando si scrivono molti dati su file, a volte è più veloce estendere logicamente la dimensione del file anziché fisicamente, questo perché quando si estende un file logicamente il file system non azzera il nuovo spazio prima di scriverlo. È anche intelligente estendere logicamente il file più di quanto sia effettivamente necessario per prevenire molte estensioni di file. L'estensione dei file logici è supportata su Windows chiamando SetFileValidDatao xfsctlcon i XFS_IOC_RESVSP64sistemi XFS.


0

sto compilando il mio programma in gcc in GNU / Linux e mingw in win 7 e win xp e ha funzionato bene

puoi usare il mio programma e per creare un file da 80 GB basta cambiare la riga 33 in

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

quando si esce dal programma il file verrà distrutto, quindi controllare il file quando è in esecuzione

per avere il programma che desideri basta cambiare il programma

il primo è il programma Windows e il secondo è per GNU / Linux

http://mustafajf.persiangig.com/Projects/File/WinFile.cpp

http://mustafajf.persiangig.com/Projects/File/File.cpp

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.