Comment écrire un grand tampon dans un fichier binaire en C ++, rapidement?

242

J'essaie d'écrire d'énormes quantités de données sur mon SSD (disque SSD). Et par énormes quantités, je veux dire 80 Go.

J'ai parcouru le Web pour trouver des solutions, mais le meilleur que j'ai trouvé est le suivant:

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

Compilé avec Visual Studio 2010 et optimisations complètes et exécuté sous Windows7, ce programme atteint environ 20 Mo / s. Ce qui me dérange vraiment, c'est que Windows peut copier des fichiers d'un autre SSD vers ce SSD entre 150 Mo / s et 200 Mo / s. Donc au moins 7 fois plus vite. C'est pourquoi je pense que je devrais pouvoir aller plus vite.

Des idées pour accélérer mon écriture?

Dominic Hofer
la source
11
Vos résultats de chronométrage excluent-ils le temps nécessaire pour effectuer vos calculs pour remplir un []?
catchmeifyoutry
7
J'ai déjà fait cette tâche auparavant. En utilisant simple, fwrite()je pouvais obtenir environ 80% des vitesses d'écriture de pointe. C'est seulement avec que FILE_FLAG_NO_BUFFERINGj'ai pu obtenir la vitesse maximale.
Mysticial
10
Je ne suis pas sûr qu'il soit juste de comparer l'écriture de votre fichier à une copie SSD-SSD. Il se pourrait bien que le SSD à SSD fonctionne à un niveau inférieur, en évitant les bibliothèques C ++ ou en utilisant l'accès direct à la mémoire (DMA). Copier quelque chose n'est pas la même chose que d'écrire des valeurs arbitraires dans un fichier à accès aléatoire.
Igor
4
@IgorF.: C'est juste une spéculation erronée; c'est une comparaison parfaitement juste (si rien d'autre, en faveur de l'écriture de fichiers). La copie sur un lecteur sous Windows est simplement en lecture et en écriture; rien d'extraordinaire / compliqué / différent en dessous.
user541686
5
@MaximYegorushkin: Lien ou ce n'est pas arrivé. : P
user541686

Réponses:

233

Cela a fait le travail (en 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;
}

Je viens de chronométrer 8 Go en 36 secondes, ce qui représente environ 220 Mo / s et je pense que cela maximise mon SSD. Il convient également de noter que le code de la question utilise un cœur à 100%, alors que ce code utilise uniquement 2 à 5%.

Merci beaucoup à tous.

Mise à jour : 5 ans se sont écoulés, c'est 2017 maintenant. Les compilateurs, le matériel, les bibliothèques et mes exigences ont changé. C'est pourquoi j'ai apporté quelques modifications au code et fait de nouvelles mesures.

Tout d'abord, le code:

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

Ce code se compile avec Visual Studio 2017 et g ++ 7.2.0 (une nouvelle configuration requise). J'ai exécuté le code avec deux configurations:

  • Ordinateur portable, Core i7, SSD, Ubuntu 16.04, g ++ version 7.2.0 avec -std = c ++ 11 -march = native -O3
  • Bureau, Core i7, SSD, Windows 10, Visual Studio 2017 version 15.3.1 avec / Ox / Ob2 / Oi / Ot / GT / GL / Gy

Ce qui a donné les mesures suivantes (après avoir abandonné les valeurs de 1 Mo, car elles étaient des valeurs aberrantes évidentes): Les fois option1 et option3 ont maximisé mon SSD. Je ne m'attendais pas à voir cela, car l'option2 était le code le plus rapide sur mon ancienne machine à l'époque.entrez la description de l'image ici entrez la description de l'image ici

TL; DR : Mes mesures indiquent une utilisation std::fstreamexcessive FILE.

Dominic Hofer
la source
8
+1 Oui, c'était la première chose que j'essayais. FILE*est plus rapide que les flux. Je ne m'attendais pas à une telle différence car il "aurait dû" être lié aux E / S de toute façon.
Mysticial
12
Pouvons-nous conclure que les E / S de style C sont (étrangement) beaucoup plus rapides que les flux C ++?
SChepurin
22
@SChepurin: Si vous êtes pédant, probablement pas. Si vous êtes pratique, probablement oui. :)
user541686
10
Pourriez-vous s'il vous plaît expliquer (pour un cancre C ++ comme moi) la différence entre les deux approches, et pourquoi celle-ci fonctionne tellement plus vite que l'original?
Mike Chamberlain
11
Le préfixe ios::sync_with_stdio(false);fait-il une différence pour le code avec stream? Je suis simplement curieux de voir à quel point il y a une grande différence entre l'utilisation de cette ligne et non, mais je n'ai pas le disque assez rapide pour vérifier le cas du coin. Et s'il y a une réelle différence.
Artur Czajka
24

Essayez les opérations suivantes, dans l'ordre:

  • Taille de tampon plus petite. Écrire ~ 2 Mio à la fois pourrait être un bon début. Sur mon dernier ordinateur portable, ~ 512 Ko était le point idéal, mais je n'ai pas encore testé sur mon SSD.

    Remarque: j'ai remarqué que les très grands tampons ont tendance à diminuer les performances. J'ai déjà remarqué des pertes de vitesse avec l'utilisation de tampons de 16 Mio au lieu de 512 Kio.

  • Utilisez _open(ou _topensi vous voulez que Windows soit correct) pour ouvrir le fichier, puis utilisez _write. Cela évitera probablement beaucoup de mise en mémoire tampon, mais ce n'est pas certain.

  • Utilisation de fonctions spécifiques à Windows comme CreateFileet WriteFile. Cela évitera toute mise en mémoire tampon dans la bibliothèque standard.

user541686
la source
Vérifiez les résultats de référence publiés en ligne. Vous avez besoin d'écritures de 4 Ko avec une profondeur de file d'attente de 32 ou plus, ou bien d'écritures de 512 Ko ou plus, pour obtenir tout type de débit décent.
Ben Voigt
@BenVoigt: Oui, cela correspond à moi en disant que 512 KiB était le point idéal pour moi. :)
user541686
Oui. D'après mon expérience, des tailles de mémoire tampon plus petites sont généralement optimales. L'exception est lorsque vous utilisez FILE_FLAG_NO_BUFFERING- dans lequel les plus gros tampons ont tendance à être meilleurs. Depuis, je pense que FILE_FLAG_NO_BUFFERINGc'est à peu près DMA.
Mysticial
22

Je ne vois aucune différence entre std :: stream / FILE / device. Entre mise en mémoire tampon et non mise en mémoire tampon.

Notez également:

  • Les disques SSD "tendent" à ralentir (taux de transfert inférieurs) à mesure qu'ils se remplissent.
  • Les disques SSD "ont tendance" à ralentir (taux de transfert inférieurs) à mesure qu'ils vieillissent (en raison de bits non fonctionnels).

Je vois le code s'exécuter en 63 secondes.
Ainsi, un taux de transfert de: 260M / s (mon SSD semble légèrement plus rapide que le vôtre).

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

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

Je n'obtiens aucune augmentation en passant au FICHIER * de 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);

}

Ainsi, le flux C ++ fonctionne aussi vite que la bibliothèque sous-jacente le permet.

Mais je pense qu'il est injuste de comparer le système d'exploitation à une application qui est construite sur le système d'exploitation. L'application ne peut faire aucune hypothèse (elle ne sait pas que les disques sont SSD) et utilise donc les mécanismes de fichiers de l'OS pour le transfert.

Alors que l'OS n'a pas besoin de faire d'hypothèses. Il peut indiquer les types de lecteurs impliqués et utiliser la technique optimale pour transférer les données. Dans ce cas, un transfert direct de mémoire à mémoire. Essayez d'écrire un programme qui copie 80G d'un emplacement en mémoire à un autre et voyez à quelle vitesse c'est.

Éditer

J'ai changé mon code pour utiliser les appels de niveau inférieur:
pas de mise en mémoire tampon.

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

Cela n'a fait aucune différence.

REMARQUE : Mon disque est un disque SSD si vous avez un disque normal, vous pouvez voir une différence entre les deux techniques ci-dessus. Mais comme je m'y attendais, la non mise en mémoire tampon et la mise en mémoire tampon (lors de l'écriture de gros morceaux supérieurs à la taille du tampon) ne font aucune différence.

Modifier 2:

Avez-vous essayé la méthode la plus rapide pour copier des fichiers en C ++

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

    output << input.rdbuf();
}
Martin York
la source
5
Je n'ai pas déprécié, mais la taille de votre tampon est trop petite. Je l'ai fait avec le même tampon de 512 Mo que l'OP utilise et j'obtiens 20 Mo / s avec des flux contre 90 Mo / s avec FILE*.
Mysticial
Aussi votre chemin avec fwrite (a, sizeof (unsigned long long), size, stream); au lieu de fwrite (a, 1, size * sizeof (unsigned long long), pFile); me donne 220 Mo / s avec des morceaux de 64 Mo par écriture.
Dominic Hofer
2
@Mysticial: Cela me surprend que la taille du tampon fasse une différence (même si je vous crois). Le tampon est utile lorsque vous avez beaucoup de petites écritures afin que le périphérique sous-jacent ne soit pas gêné par de nombreuses requêtes. Mais lorsque vous écrivez d'énormes morceaux, il n'est pas nécessaire d'avoir un tampon lors de l'écriture / lecture (sur un périphérique de blocage). En tant que telles, les données doivent être transmises directement au périphérique sous-jacent (contournant ainsi le tampon). Bien que si vous voyez une différence, cela contredirait cela et je me demande pourquoi l'écriture utilise réellement un tampon.
Martin York
2
La meilleure solution n'est PAS d'augmenter la taille du tampon, mais de supprimer le tampon et de faire en sorte que l'écriture transmette les données directement au périphérique sous-jacent.
Martin York
1
@Mysticial: 1) Il n'y a pas de petits morceaux => Il est toujours assez grand (dans cet exemple). Dans ce cas, les morceaux sont de 512M 2) Il s'agit d'un lecteur SSD (à la fois le mien et l'OP), donc rien de tout cela n'est pertinent. J'ai mis à jour ma réponse.
Martin York
13

La meilleure solution consiste à implémenter une écriture asynchrone avec double tampon.

Regardez la chronologie:

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

Le «F» représente le temps de remplissage du tampon et le «W» représente le temps d'écriture du tampon sur le disque. Donc, le problème de perdre du temps entre l'écriture de tampons dans un fichier. Cependant, en implémentant l'écriture sur un thread séparé, vous pouvez commencer à remplir immédiatement le tampon suivant comme ceci:

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

F - remplissage du premier tampon
f - remplissage du deuxième tampon
W - écriture du premier tampon dans le fichier
w - écriture du deuxième tampon dans le fichier
_ - attendez la fin de l'opération

Cette approche avec des échanges de tampons est très utile lorsque le remplissage d'un tampon nécessite un calcul plus complexe (donc plus de temps). J'implémente toujours une classe CSequentialStreamWriter qui cache l'écriture asynchrone à l'intérieur, donc pour l'utilisateur final, l'interface n'a que des fonctions d'écriture.

Et la taille du tampon doit être un multiple de la taille du cluster de disques. Sinon, vous obtiendrez de mauvaises performances en écrivant un seul tampon dans 2 clusters de disques adjacents.

Écriture du dernier tampon.
Lorsque vous appelez la fonction Write pour la dernière fois, vous devez vous assurer que le tampon en cours de remplissage doit également être écrit sur le disque. Ainsi, CSequentialStreamWriter devrait avoir une méthode distincte, disons Finalize (final buffer flush), qui devrait écrire sur le disque la dernière partie des données.

La gestion des erreurs.
Alors que le code commence à remplir le deuxième tampon et que le premier est écrit sur un thread séparé, mais que l'écriture échoue pour une raison quelconque, le thread principal doit être conscient de cet échec.

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

Supposons que l'interface d'un CSequentialStreamWriter a une fonction Write renvoie bool ou lève une exception, ayant ainsi une erreur sur un thread séparé, vous devez vous souvenir de cet état, donc la prochaine fois que vous appellerez Write ou Finilize sur le thread principal, la méthode retournera Faux ou lèvera une exception. Et peu importe à quel moment vous avez cessé de remplir un tampon, même si vous avez écrit des données à l'avance après l'échec - le fichier sera probablement corrompu et inutile.

HandMadeOX
la source
3
L'exécution d'E / S est parallèle aux calculs est une très bonne idée, mais sous Windows, vous ne devez pas utiliser de threads pour y parvenir. À la place, utilisez "E / S superposées", qui ne bloque pas l'un de vos threads lors de l'appel d'E / S. Cela signifie que vous avez à peine à vous soucier de la synchronisation des threads (il suffit de ne pas accéder à un tampon qui a une opération d'E / S active l'utilisant).
Ben Voigt
11

Je suggère d'essayer le mappage de fichiers . J'ai utilisé mmapdans le passé, dans un environnement UNIX, et j'ai été impressionné par les hautes performances que j'ai pu atteindre

Ralph
la source
1
@nalply C'est toujours une solution fonctionnelle, efficace et intéressante à garder à l'esprit.
Yam Marcovic
stackoverflow.com/a/2895799/220060 sur les avantages et les inconvénients de mmap. Surtout notez "Pour des accès séquentiels purs au fichier, ce n'est pas toujours la meilleure solution [...]" Aussi stackoverflow.com/questions/726471 , il indique effectivement que sur un système 32 bits vous êtes limité à 2 ou 3 Go. - au fait, ce n'est pas moi qui ai dévalorisé cette réponse.
2012
8

Pourriez-vous utiliser à la FILE*place et mesurer les performances que vous avez gagnées? Quelques options sont à utiliser fwrite/writeau lieu defstream :

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

Si vous décidez d'utiliser write , essayez quelque chose de similaire:

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

Je vous conseillerais également de vous renseigner memory map. C'est peut-être votre réponse. Une fois, j'ai dû traiter un fichier de 20 Go dans un autre pour le stocker dans la base de données, et le fichier ne s'ouvrait même pas. Donc, la solution pour utiliser la carte mémoire. Je l'ai fait Pythoncependant.

cybertextron
la source
En fait, un FILE*équivalent simple du code d'origine utilisant le même tampon de 512 Mo obtient sa pleine vitesse. Votre mémoire tampon actuelle est trop petite.
Mysticial
1
@Mysticial Mais ce n'est qu'un exemple.
cybertextron
Dans la plupart des systèmes, 2correspond à une erreur standard, mais il est toujours recommandé de l'utiliser à la STDERR_FILENOplace de 2. Un autre problème important est qu'une erreur possible que vous pouvez obtenir est EINTR lorsque vous recevez un signal d'interruption, ce n'est pas une vraie erreur et vous devez simplement réessayer.
Peyman
6

Essayez d'utiliser les appels API open () / write () / close () et testez la taille du tampon de sortie. Je veux dire ne passez pas tout le tampon "plusieurs-plusieurs-octets" à la fois, faites quelques écritures (c.-à-d. TotalNumBytes / OutBufferSize). OutBufferSize peut être compris entre 4096 octets et mégaoctets.

Un autre essai - utilisez WinAPI OpenFile / CreateFile et utilisez cet article MSDN pour désactiver la mise en mémoire tampon (FILE_FLAG_NO_BUFFERING). Et cet article MSDN sur WriteFile () montre comment obtenir la taille de bloc pour que le lecteur connaisse la taille de tampon optimale.

Quoi qu'il en soit, std :: ofstream est un wrapper et il peut y avoir un blocage des opérations d'E / S. Gardez à l'esprit que la traversée de l'ensemble de la baie de N gigaoctets prend également un certain temps. Pendant que vous écrivez un petit tampon, il arrive dans le cache et fonctionne plus rapidement.

Viktor Latypov
la source
6

fstreams ne sont pas plus lents que les flux C, en soi, mais ils utilisent plus de CPU (surtout si la mise en mémoire tampon n'est pas correctement configurée). Lorsqu'un CPU sature, il limite le taux d'E / S.

Au moins, l'implémentation MSVC 2015 copie 1 caractère à la fois dans le tampon de sortie lorsqu'un tampon de flux n'est pas défini (voir streambuf::xsputn). Donc , assurez - vous de mettre un tampon de flux (> 0) .

Je peux obtenir une vitesse d'écriture de 1500 Mo / s (la pleine vitesse de mon SSD M.2) en fstreamutilisant ce code:

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

J'ai essayé ce code sur d'autres plates-formes (Ubuntu, FreeBSD) et je n'ai remarqué aucune différence de taux d'E / S, mais une différence d' utilisation du processeur d'environ 8: 1 ( fstreamutilisé 8 fois plus de processeur ). On peut donc imaginer que si j'avais un disque plus rapide, l' fstreamécriture ralentirait plus tôt que la stdioversion.

rustyx
la source
3

Si vous copiez quelque chose du disque A vers le disque B dans l'explorateur, Windows utilise DMA. Cela signifie que pour la plupart du processus de copie, le CPU ne fera rien d'autre que de dire au contrôleur de disque où placer et obtenir les données, éliminant ainsi une étape entière de la chaîne, et qui n'est pas du tout optimisée pour déplacer de grandes quantités de données - et je veux dire le matériel.

Ce que vous faites implique beaucoup de CPU. Je veux vous indiquer la partie "Quelques calculs pour remplir une []". Je pense que c'est essentiel. Vous générez un [], puis vous copiez d'un [] vers un tampon de sortie (c'est ce que fstream :: write fait), puis vous générez à nouveau, etc.

Que faire? Multithreading! (J'espère que vous avez un processeur multicœur)

  • fourchette.
  • Utilisez un thread pour générer une donnée []
  • Utilisez l'autre pour écrire des données d'un [] sur un disque
  • Vous aurez besoin de deux tableaux a1 [] et a2 [] et basculez entre eux
  • Vous aurez besoin d'une sorte de synchronisation entre vos threads (sémaphores, file d'attente de messages, etc.)
  • Utilisez des fonctions de niveau inférieur, sans tampon, comme la fonction WriteFile mentionnée par Mehrdad
dualed
la source
2

Essayez d'utiliser des fichiers mappés en mémoire.

qehgt
la source
@Mehrdad mais pourquoi? Parce que c'est une solution dépendante de la plateforme?
qehgt
3
Non ... c'est parce que pour effectuer une écriture de fichiers séquentielle rapide, vous devez écrire de grandes quantités de données à la fois. (Disons que les blocs de 2 Mo sont probablement un bon point de départ.) Les fichiers mappés en mémoire ne vous permettent pas de contrôler la granularité, vous êtes donc à la merci de tout ce que le gestionnaire de mémoire décide de pré-extraire / mettre en mémoire tampon pour vous. En général, je ne les ai jamais vus être aussi efficaces que la lecture / écriture normale avec ReadFileet autres pour un accès séquentiel, bien que pour un accès aléatoire, ils puissent être meilleurs.
user541686
Mais les fichiers mappés en mémoire sont utilisés par le système d'exploitation pour la pagination, par exemple. Je pense que c'est une façon très optimisée (en termes de vitesse) de lire / écrire des données.
qehgt
7
@Mysticial: Les gens «savent» beaucoup de choses qui sont tout simplement erronées.
Ben Voigt
1
@qehgt: Si quoi que ce soit, la pagination est beaucoup plus optimisée pour l'accès aléatoire que l'accès séquentiel. La lecture d'une page de données est beaucoup plus lente que la lecture d'un mégaoctet de données en une seule opération.
user541686
1

Si vous voulez écrire rapidement dans des flux de fichiers, vous pouvez agrandir le tampon de lecture:

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

De plus, lors de l'écriture de nombreuses données dans des fichiers, il est parfois plus rapide d' étendre logiquement la taille du fichier plutôt que physiquement, car en étendant logiquement un fichier, le système de fichiers ne met pas à zéro le nouvel espace avant d'y écrire. Il est également judicieux d'étendre logiquement le fichier plus que ce dont vous avez réellement besoin pour éviter de nombreuses extensions de fichier. L'extension de fichier logique est prise en charge sous Windows en appelant SetFileValidDataou xfsctlavec XFS_IOC_RESVSP64sur les systèmes XFS.


la source
0

im compiler mon programme dans gcc sous GNU / Linux et mingw dans win 7 et win xp et a bien fonctionné

vous pouvez utiliser mon programme et pour créer un fichier de 80 Go, changez simplement la ligne 33 en

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

lorsque vous quittez le programme, le fichier sera détruit, puis vérifiez le fichier lorsqu'il est en cours d'exécution

d'avoir le programme que vous voulez changer simplement le programme

le premier est le programme windows et le second est pour GNU / Linux

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

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

Ethaan
la source