Используйте sendfile () для копирования файла с потоками или другим эффективным методом копирования файла

Я пытаюсь использовать системный вызов Linux sendfile() скопировать файл, используя потоки.

Я заинтересован в оптимизации этих частей кода:

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

Код:

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

0

Решение

Копирование файла не связано с процессором; в противном случае вы, вероятно, обнаружите, что ограничение находится на уровне ядра и ничего, что вы можете сделать на уровне пользователя, не сможет распараллелить.

Такие «улучшения», сделанные на механических приводах, будут деградировать пропускная способность. Вы тратите время на поиск файла вместо того, чтобы читать и писать.

Если файл длинный и вы не собираетесь в скором времени читать или записывать данные, может возникнуть соблазн использовать O_DIRECT флаг на открытии. Это плохая идея, так как O_DIRECT API по сути сломан по замыслу.

Вместо этого вы должны использовать posix_fadvise в исходных и целевых файлах с флагами POSIX_FADV_SEQUENTIAL и POSIX_FADV_NOREUSE. После того как вызов write (или sendfile) завершен, вы должны сообщить, что данные больше не нужны — передайте POSIX_FADV_DONTNEED. Таким образом, кэш страниц будет использоваться только в той степени, которая необходима для поддержания потока данных, и страницы будут перезагружены, как только данные будут использованы (записаны на диск).

sendfile не будет передавать данные файла в пользовательское пространство, поэтому это еще больше ослабляет нагрузку на память и кэш процессора. Это единственное разумное улучшение, которое вы можете сделать для копирования файлов, не зависящих от устройства.

Выбор разумного размера куска также желателен. Учитывая, что современные накопители используют скорость более 100 Мбайт / с, вам может потребоваться увеличивать размер мегабайта за раз, и всегда кратно размеру страницы 4096 байт — таким образом (4096*256) достойный размер стартового куска для обработки в одном sendfile или же read/write звонки.

Распараллеливание чтения, как вы предлагаете, имеет смысл только для томов RAID 0 и только тогда, когда входные и выходные файлы располагаются на физических дисках. Затем вы можете иметь один поток на меньшее количество физических дисков исходного и конечного тома, расположенных между файлами. Это необходимо, только если вы не используете асинхронный файловый ввод-вывод. С асинхронным вводом / выводом вам в любом случае не понадобится более одного потока, особенно если размеры блоков большие (мегабайт +) и штраф за задержку для одного потока незначителен.

Нет смысла распараллеливать одну копию файла на SSD, если только вы не работали в какой-то очень странной системе.

3

Другие решения


По вопросам рекламы [email protected]