Efficace de la lecture d'un très gros fichier texte en C++
J'ai un très gros fichier texte(45 GO). Chaque ligne du fichier texte contient deux espaces séparés 64 bits entiers non signés, comme illustré ci-dessous.
4624996948753406865 10214715013130414417
4305027007407867230 4569406367070518418
10817905656952544704 3697712211731468838
...
...
Je veux lire le fichier et d'effectuer certaines opérations sur les nombres.
Mon Code en C++:
void process_data(string str)
{
vector<string> arr;
boost::split(arr, str, boost::is_any_of(" \n"));
do_some_operation(arr);
}
int main()
{
unsigned long long int read_bytes = 45 * 1024 *1024;
const char* fname = "input.txt";
ifstream fin(fname, ios::in);
char* memblock;
while(!fin.eof())
{
memblock = new char[read_bytes];
fin.read(memblock, read_bytes);
string str(memblock);
process_data(str);
delete [] memblock;
}
return 0;
}
Je suis relativement novice en c++. Lorsque j'exécute ce code, je suis confronté à ces problèmes.
- À cause de la lecture du fichier en octets, parfois à la dernière ligne d'un bloc correspond à une inachevé ligne dans le fichier d'origine("4624996948753406865 10214" à la place de la chaîne "4624996948753406865 10214715013130414417" du fichier principal).
- Ce code fonctionne très très lent. Il faut environ 6secs à exécuter pour un bloc opérations en 64 bits Intel Core i7 920 système avec 6 go de mémoire RAM. Est-il des techniques d'optimisation que je peux utiliser pour améliorer l'exécution?
- Est-il nécessaire d'inclure "\n" avec caractère vide dans l'élan fonction split?
J'ai lu sur mmap fichiers en C++, mais je ne suis pas sûr de savoir si c'est la bonne façon de le faire. Si oui, veuillez joindre quelques liens.
À l'aide de
Modifier votre question afin de montrer votre compilation de commande (et la version du compilateur &
n'est-il pas plus simple pour enregistrer les données sous forme binaire au lieu de décimales texte mis en forme; j'ai fait quelque chose de similaire dans LabVIEW et a eu comme un facteur de 10 pour l'amélioration de la lecture des fichiers binaires au lieu de l'analyse des fichiers texte (bien sûr cela dépend de l'analyseur de mise en œuvre, mais encore..). Comme un avantage supplémentaire, vous n'auriez pas besoin
eof
pas une bonne chose.Modifier votre question afin de montrer votre compilation de commande (et la version du compilateur &
libstdc++
)n'est-il pas plus simple pour enregistrer les données sous forme binaire au lieu de décimales texte mis en forme; j'ai fait quelque chose de similaire dans LabVIEW et a eu comme un facteur de 10 pour l'amélioration de la lecture des fichiers binaires au lieu de l'analyse des fichiers texte (bien sûr cela dépend de l'analyseur de mise en œuvre, mais encore..). Comme un avantage supplémentaire, vous n'auriez pas besoin
20*8=160
octets pour stocker par exemple, le nombre 4624996948753406865
+ espace, mais seulement de 8 octetsOriginalL'auteur Pattu | 2014-11-04
Vous devez vous connecter pour publier un commentaire.
J'avais cette refonte de la loi sur le streaming, au lieu de sur un bloc.
Une approche plus simple serait:
Si vous savez à peu près combien de valeurs sont attendus, à l'aide de
std::vector::reserve
à l'avant pourrait accélérer davantage.Alternativement, vous pouvez utiliser un fichier mappé en mémoire et itérer sur la séquence de caractères.
Mise à jour j'ai modifié le programme ci-dessus pour analyser
uint32_t
s dans un vecteur.Lors de l'utilisation d'un exemple de fichier d'entrée de 4,5 Go[1] le programme s'exécute en 9 secondes[2]:
Bien sûr, il alloue au moins 402653184 * 4 * octet = 1.5 gibibytes. Alors, quand
vous lisez un 45 GO de fichiers, vous aurez besoin d'une estimation de 15GiB de RAM pour stocker tout
le vecteur (en supposant l'absence de fragmentation sur la réaffectation): La 45GiB analyser
complète en 10 min 45s:
Par comparaison, juste en cours d'exécution
wc -l 45gib_uint32s.txt
a mis environ 12 minutes (sans priorité en temps réel l'ordonnancement).wc
est hyper rapidePlein De Code Utilisé Pour Indice De Référence
[1] généré avec
od -t u4 /dev/urandom -A none -v -w4 | pv | dd bs=1M count=$((9*1024/2)) iflag=fullblock > smaller.txt
[2] de toute évidence, c'était avec le fichier mis en cache dans le cache de tampons sur linux - le gros fichier n'a pas cet avantage
Par tous les moyens, mais
bad_alloc
indique que vous simplement ne pas avoir la mémoire pour le vecteur, en premier lieu, ou l'allocation échoue en raison de la fragmentation. Essayez d'appelerreserve()
avec la capacité finale (ou un peu plus) pour éviter d'avoir à réaffecter.Assurez-vous que vous avez suffisamment de mémoire. J'ai analysé les 4.5 Go en 9 secondes et 45 Go en moins de 11 minutes (voir mise à Jour réponse avec code). (Note de la 9s est due à la mise en cache disque lit, bien sûr. Tenir compte de ces la vraie vie timings, non pas tant l'analyse de repères)
Une grande réponse. Est
wc -l 45gib_uint32s.txt
a pris ~12 minutes droit?Permettez-moi de reproduire cela. Générer les données de grande taille va prendre environ 30 minutes.
OriginalL'auteur sehe
Je ne peux que deviner que le goulot d'étranglement est en:
-Parce que vous allouer un 45 MO long segment dans la mémoire.
Vous devez lire le fichier ligne par ligne, comme décrit ici:
Pour le profil de votre programme, vous pouvez imprimer horloge() entre chaque ligne, comme décrit dans:
OriginalL'auteur Oren Kishon
Vous pouvez carte mémoire le fichier dans la mémoire, mais qui seraient dépendants de la plateforme (unix qui serait mmap sur windows CreateFileMapping/MapViewIntoFile); encore si dans système 32 bits, vous pouvez avoir des problèmes si il n'est pas un assez grand bloc de mémoire virtuelle de gauche (64 bits des systèmes n'aurait pas ce problème).
Mappage de la mémoire est censé être plus rapide que de lire les données en bloc par bloc à partir du disque.
OriginalL'auteur MichaelMoser
Sur Linux, à l'aide de C
<stdio.h>
au lieu de C++ flux pourrait aider à la performance (comme C++, les flux sont construits au-dessus duFILE
-s). Vous pouvez utiliser readline(3) ou fgets(3) ou fscanf(3). Vous pouvez définir une grande mémoire tampon (par exemple, 64Kbytes ou 256Kbytes) à l'aide de setbuffer(3) etc... Mais je suppose que votre (amélioré) programme I/O bound, pas de CPU.Ensuite, vous pouvez jouer avec posix_fadvise(2)
Vous pourriez envisager d'utiliser le mappage de mémoire mmap(2) & madvise(2) (voir aussi
m
mode pour fopen(3)). Voir aussi readahead(2)Enfin, si votre algorithme permet, vous pouvez
csplit
les fichiers en petits morceaux et de traiter chacun d'entre eux dans des processus parallèles.OriginalL'auteur Basile Starynkevitch