gpt4 book ai didi

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

转载 作者:太空宇宙 更新时间:2023-11-04 08:59:09 26 4
gpt4 key购买 nike

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

最佳答案

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

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

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

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

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

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

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

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

关于c++ - 使用 sendfile() 通过线程或其他高效的复制文件方法复制文件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25031044/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com