使用sendfile()通过线程或其他有效的复制文件方法复制文件

时间:2014-07-30 07:28:11

标签: c++ linux multithreading 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);
}

1 个答案:

答案 0 :(得分:3)

文件复制不受CPU限制;如果它是你可能会发现限制是在内核级别,你在用户级别可以做的任何事情都不会并行化。

在机械驱动器上进行的这种“改进”实际上会降低吞吐量。你是在浪费时间寻找文件,而不是阅读和写作。

如果文件很长并且您不希望很快就需要读取或写入数据,则可能很有可能在打开时使用O_DIRECT标志。这是个坏主意,因为O_DIRECT API基本上是broken by design

相反,您应该在源文件和目标文件上使用posix_fadvise,使用POSIX_FADV_SEQUENTIAL和POSIX_FADV_NOREUSE标志。写入(或sendfile)调用完成后,您需要建议不再需要数据 - 通过POSIX_FADV_DONTNEED。这样,页面缓存将仅用于保持数据流动所需的程度,并且一旦数据被消耗(写入磁盘),页面就会被回收。

sendfile不会将文件数据推送到用户空间,因此它进一步放松了内存和处理器缓存的一些压力。这是关于复制非特定于设备的文件的唯一合理改进。

选择合理的块大小也是可取的。鉴于现代驱动器的速度超过100Mbytes / s,您可能希望一次推送一个兆字节,并且始终是4096字节页面大小的倍数 - 因此(4096*256)是一个不错的起始块大小来处理单个sendfileread / write来电。

读取并行化,正如您所建议的那样,仅在RAID 0卷上有意义,并且仅在输入和输出文件都跨越物理磁盘时才有意义。然后,您可以根据文件跨越的源和目标卷物理磁盘的数量中的较小者来拥有一个线程。只有在您不使用异步文件I / O时才需要这样做。使用异步I / O,无论如何都不需要多个线程,尤其是如果块大小很大(兆字节+)并且单线程延迟惩罚可以忽略不计。

在SSD上并行化单个文件副本是没有意义的,除非你确实在一些非常奇怪的系统上。