【问题标题】:Use sendfile() to copy file with threads or other efficient copy file method使用 sendfile() 通过线程或其他有效的复制文件方法复制文件
【发布时间】:2014-09-21 18:05:32
【问题描述】:

我正在尝试使用 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);
}

【问题讨论】:

    标签: c++ linux multithreading sendfile


    【解决方案1】:

    文件复制不受 CPU 限制;如果您可能会发现限制是在内核级别,而您在用户级别无法执行的任何操作都无法将其并行化。

    对机械驱动器进行的这种“改进”实际上会降低吞吐量。您正在浪费时间查找文件而不是读取和写入文件。

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

    相反,您应该在源文件和目标文件上使用posix_fadvise,并带有 POSIX_FADV_SEQUENTIAL 和 POSIX_FADV_NOREUSE 标志。写入(或发送文件)调用完成后,您需要告知不再需要数据 - 传递 POSIX_FADV_DONTNEED。这样,页面缓存将仅用于保持数据流动所需的范围,并且一旦数据被消耗(写入磁盘),页面将被回收。

    sendfile 不会将文件数据推送到用户空间,因此它进一步减轻了来自内存和处理器缓存的一些压力。对于复制非特定设备的文件,这是您可以做出的唯一其他明智的改进。

    选择合理的块大小也是可取的。鉴于现代驱动器推送超过 100Mbytes/s,您可能希望一次推送 1MB,并且始终是 4096 字节页面大小的倍数 - 因此 (4096*256) 是一个不错的起始块大小,可以在单个 @987654327 中处理@ 或read/write 调用。

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

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

    【讨论】:

    • 你的意思是不用多线程来使用sendfile()?
    • @user3804288 不,不需要。
    • 我可能会误解,但 sendfile 不是意味着默认传递整个文件大小,它会自行循环吗?这很重要,因为如果它在给您控制权之前循环超过 2GB,这就是在您运行 POSIX_FADV_NOREUSE 之前不必要地“破坏”了多少磁盘缓存。
    猜你喜欢
    • 2011-07-21
    • 2012-12-18
    • 2012-12-12
    • 2010-10-06
    • 2015-10-28
    • 2017-10-24
    • 2014-01-22
    • 2012-08-17
    • 1970-01-01
    相关资源
    最近更新 更多