2011-01-31 7 views
2

Je suis mmaping deux fichiers texte avec un entier écrit sur chaque ligne. Je les lis depuis le lecteur et je voulais faire une fusion triée sur eux. Les deux fichiers d'entrée "1piece0" et "1piece1" ont une liste d'entiers triés. Le fichier de sortie a la taille en tant que les deux fichiers combinés, mais pas beaucoup d'entiers. Problème: Les deux fichiers d'entrée ont 25430000 lignes, tandis que le fichier de sortie doit contenir 50860000 lignes, mais il n'a que 17259463 lignes. Ceci est mon code actuel.std :: fusionner en utilisant 2 matrices mmaped?

#include <stdio.h> 
#include <stdlib.h> 
#include <sys/types.h> 
#include <sys/stat.h> 
#include <unistd.h> 
#include <fcntl.h> 
#include <sys/mman.h> 
#include <algorithm> 

#define FILESIZE 25430000 * sizeof(int) 
#define FILE0 279288034 
#define FILE1 279287226 
int main() 
{ 
    int i; 
    int fd; 
    int fd2; 
    int fd3; 
    int result; 
    int *map; 
    int *map2; 
    int *map3; 

    fd3 = open("file.out", O_RDWR | O_CREAT | O_TRUNC, (mode_t)0755); 
    if (fd3 == -1) { 
     perror("Error opening file for writing"); 
     exit(EXIT_FAILURE); 
    } 
    result = lseek(fd3, FILE0 + FILE1 - 1, SEEK_SET); 
    if(result == -1) { 
     close(fd); 
     perror("Error calling lseek\n"); 
     exit(EXIT_FAILURE); 
    } 

    result = write(fd3,"",1); 
    if(result != 1) { 
     close(fd3); 
     perror("error writing last byte"); 
     exit(EXIT_FAILURE); 
    } 
    map3 =(int *) mmap(0, FILE0 + FILE1, PROT_READ | PROT_WRITE, MAP_SHARED, fd3, 0); 
    if(map == MAP_FAILED) { 
     close(fd); 
     perror("Error mmapinG fd3"); 
     exit(EXIT_FAILURE); 
    } 


    fd = open("1piece0", O_RDONLY); 
    if(fd == -1) { 
     perror("Error opening file for writing"); 
     exit(EXIT_FAILURE); 
    } 

    map = (int *)mmap(0, FILE0, PROT_READ, MAP_SHARED, fd, 0); 
    if(map == MAP_FAILED) { 
     close(fd); 
     perror("error mapping file"); 
     exit(EXIT_FAILURE); 
    } 

    fd2 = open("1piece1", O_RDONLY); 
    if(fd2 == -1) { 
     perror("Error opening file for writing"); 
     exit(EXIT_FAILURE); 
    } 

    map2 = (int *)mmap(0, FILE1, PROT_READ, MAP_SHARED, fd2, 0); 
    if(map == MAP_FAILED) { 
     close(fd2); 
     perror("error mapping file"); 
     exit(EXIT_FAILURE); 
    } 

// while(1); 
    std::merge(map, map + 25430000, map2, map2 + 25430000, map3); 

    if(munmap(map, FILE0) == -1) { 
     perror("error unmapping map"); 
    } 
    close(fd); 

    if(munmap(map3, FILE0 + FILE1) == -1) { 
     perror("error unmapping map3"); 
    } 
    close(fd3); 

    if(munmap(map2, FILE1) == -1) { 
     perror("error unmapping map2"); 
    } 
    close(fd2); 

    return 0; 
} 

Pouvez-vous s'il vous plaît me dire ce que je fais mal?

Mise à jour: Par lignes, je veux dire un nombre entier, puis un caractère de nouvelle ligne.

+1

Parlez-nous de ce qui se passe mal. –

+0

@Jens - Well Les fichiers sont sous la forme d'un entier puis d'un caractère de retour à la ligne. Donc, je voulais trier les fusionner. Je pense que je ne peux pas les mmap. Le problème exact est qu'il ne semble pas y avoir assez de lignes dans le fichier de sortie. –

+0

La condition if à côté de map2 et map3 ne devrait-elle pas être vérifiée par rapport à map2 et map3, au lieu de map? – yasouser

Répondre

2

Vous ne pouvez pas traiter les lignes de texte comme des tâches binaires à manipuler comme des pointeurs int.

Vous pouvez traiter des fichiers texte sous forme de texte à être extracted and used:

void merge_ints(std::istream &a_in, std::istream &b_in, std::ostream &out) { 
    int a, b; 
    std::istream *remaining = 0; 
    if (!(a_in >> a)) { 
    remaining = &b_in; 
    } 
    else if (!(b_in >> b)) { 
    out << a << '\n'; 
    remaining = &a_in; 
    } 
    else while (a_in && b_in) { 
    if (a < b) { 
     out << a << '\n'; 
     if (!(a_in >> a)) { 
     out << b << '\n'; 
     remaining = &b_in; 
     } 
    } 
    else { 
     out << b << '\n'; 
     if (!(b_in >> b)) { 
     out << a << '\n'; 
     remaining = &a_in; 
     } 
    } 
    } 
    for (int x; *remaining >> x;) { 
    out << x << '\n'; 
    } 
} 

Taking advantage of std::merge:

void merge_ints(std::istream &a, std::istream &b, std::ostream &out) { 
    typedef std::istream_iterator<int> In; 
    std::merge(In(a), In(), In(b), In(), std::ostream_iterator<int>(out, "\n")); 
} 

int main() { 
    stringstream a ("1\n3\n5\n"), b ("2\n4\n6\n7\n"), out; 
    merge_ints(a, b, out); 
    cout << out.str(); 
} 
+0

La principale raison pour laquelle je ne veux pas utiliser les flux est qu'ils sont trop lents. Je voulais que le programme soit aussi rapide que possible. J'ai seulement un 100mb de mémoire principale donc je voulais les mmap, mais je pense que je vais devoir écrire le code pour les stocker en morceaux sur la mémoire et effectuer une fusion .. C Fichier E/S est plus rapide n'est pas il? Ne devrais-je pas l'utiliser? –

+0

@Skkard: Pourquoi ne pas le tester? En utilisant la famille scanf, c'est bien aussi. Le point principal est que si vous avez des données de texte, vous ne pouvez pas les traiter comme des données non-texte ("binaire"). –

+1

C'est plus rapide, mais pas autant que je l'avais espéré. Merci d'avoir éclairci les choses :). –

2

Qu'entendez-vous par "lignes"?

Lorsque vous carte mémoire, il traite les données comme si c'était de la mémoire et ici vous le lisez comme un tableau d'ints. Par conséquent, les entrées doivent être en format binaire natif (c'est-à-dire avec les octets stockés de la même manière, de même taille et de même endianness), et 25430000 est le nombre d'ints que vous lisez dans chaque collection.

Est-ce ainsi que vos entrées sont stockées?

Il y a beaucoup de "nombres magiques" ici.

+0

C'est un fichier texte. Devrais-je le convertir en un fichier binaire? Comment est-ce que je fais cela? –

+0

Si vous le faites de cette façon, vous devez le faire, mais vous pouvez utiliser des flux pour lire le texte. Le plus simple est d'utiliser istream_iterator sur le descripteur de fichier ouvert (ifstream) comme type d'itérateur d'entrée. – CashCow