Utilisez sendfile() pour copier le fichier avec des threads ou une autre méthode de copie de fichier efficace

StackOverflow https://stackoverflow.com//questions/25031044

Question

J'essaie d'utiliser l'appel système Linux sendfile() pour copier un fichier à l'aide de threads.

Je souhaite optimiser ces parties du code :

fseek(fin, size * (number) / MAX_THREADS, SEEK_SET);  
fseek(fout, size * (number) / MAX_THREADS, SEEK_SET); 
/* ... */
fwrite(buff, 1, len, fout);  

Code:

void* FileOperate::FileCpThread::threadCp(void *param)
{
    Info *ft = (Info *)param;
    FILE *fin = fopen(ft->fromfile, "r+");
    FILE *fout = fopen(ft->tofile, "w+");

    int size = getFileSize(ft->fromfile);

    int number =  ft->num;
    fseek(fin, size * (number) / MAX_THREADS, SEEK_SET);
    fseek(fout, size * (number) / MAX_THREADS, SEEK_SET);

    char buff[1024] = {'\0'};
    int len = 0;
    int total = 0;

    while((len = fread(buff, 1, sizeof(buff), fin)) > 0)
    {
        fwrite(buff, 1, len, fout);
        total += len;

        if(total > size/MAX_THREADS)
        {
            break;
        }
    }

    fclose(fin);
    fclose(fout);
}
Était-ce utile?

La solution

La copie de fichiers n'est pas liée au processeur ;si c'était le cas, vous constaterez probablement que la limitation se situe au niveau du noyau et que rien de ce que vous pouvez faire au niveau de l'utilisateur ne la paralléliserait.

De telles "améliorations" apportées aux entraînements mécaniques seront en fait dégrader le débit.Vous perdez du temps à parcourir le fichier au lieu de le lire et de l'écrire.

Si le fichier est long et que vous ne pensez pas avoir besoin des données lues ou écrites de sitôt, il peut être tentant d'utiliser l'option O_DIRECT drapeau ouvert.C'est une mauvaise idée, puisque le O_DIRECT L'API est essentiellement cassé par conception.

Au lieu de cela, vous devriez utiliser posix_fadvise sur les fichiers source et de destination, avec les indicateurs POSIX_FADV_SEQUENTIAL et POSIX_FADV_NOREUSE.Une fois l'appel d'écriture (ou sendfile) terminé, vous devez indiquer que les données ne sont plus nécessaires - transmettez POSIX_FADV_DONTNEED.De cette façon, le cache des pages ne sera utilisé que dans la mesure nécessaire pour maintenir le flux des données, et les pages seront recyclées dès que les données auront été consommées (écrites sur le disque).

Le sendfile ne transmettra pas les données des fichiers vers l'espace utilisateur, ce qui relâchera encore une partie de la pression exercée sur la mémoire et le cache du processeur.C'est à peu près la seule autre amélioration sensible que vous pouvez apporter à la copie de fichiers qui ne sont pas spécifiques à un appareil.

Il est également souhaitable de choisir une taille de morceau raisonnable.Étant donné que les disques modernes dépassent 100 Mo/s, vous souhaiterez peut-être pousser un mégaoctet à la fois, et toujours un multiple de la taille de page de 4 096 octets - ainsi (4096*256) est une taille de morceau de départ décente à gérer en un seul sendfile ou read/write appels.

La parallélisation en lecture, telle que vous la proposez, n'a de sens que sur les volumes RAID 0, et uniquement lorsque les fichiers d'entrée et de sortie chevauchent les disques physiques.Vous pouvez alors avoir un thread pour le moindre du nombre de disques physiques de volume source et de destination chevauchés par le fichier.Cela n'est nécessaire que si vous n'utilisez pas d'E/S de fichiers asynchrones.Avec les E/S asynchrones, vous n'auriez de toute façon pas besoin de plus d'un thread, surtout pas si la taille des morceaux est grande (mégaoctets et plus) et que la pénalité de latence d'un seul thread est négligeable.

La parallélisation d'une seule copie de fichier sur des disques SSD n'a aucun sens, à moins que vous ne soyez effectivement sur un système très étrange.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top