使用sendfile()使用线程或其他高效的复制文件方法复制文件
-
21-12-2019 - |
题
我正在尝试使用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限制;如果你可能会发现限制是在内核级别,并且用户leve无法做任何事情都会并行化它。
在机械驱动器上完成的这种"改进"实际上将 降解;降解 的吞吐量。你在浪费时间沿着文件寻找,而不是读写它。
如果文件很长,并且您不希望很快需要读取或写入数据,则可能很容易使用 O_DIRECT
国旗打开。这是个坏主意,因为 O_DIRECT
API本质上是 被设计打破.
相反,你应该使用 posix_fadvise
在源文件和目标文件上,使用POSIX_FADV_SEQUENTIAL和POSIX_FADV_NOREUSE标志。写入(或sendfile)调用完成后,您需要建议不再需要数据-传递POSIX_FADV_DONTNEED。这样,页面缓存只会在保持数据流动所需的范围内使用,并且一旦数据被消耗(写入磁盘),页面就会被回收。
该 sendfile
不会将文件数据推送到用户空间,因此它进一步缓解了内存和处理器缓存的一些压力。这是关于复制不是特定于设备的文件的唯一其他明智的改进。
选择合理的块大小也是可取的。鉴于现代驱动器推送超过100Mbytes/s,您可能希望一次推送一兆字节,并且始终是4096字节页面大小的倍数-因此 (4096*256)
是一个体面的起始块大小来处理在一个单一的 sendfile
或 read
/write
电话。
正如您所建议的那样,读取并行化仅在RAID0卷上有意义,并且只有当输入和输出文件跨越物理磁盘时才有意义。然后,您可以在文件跨界的源卷和目标卷物理磁盘数量中每个线程中有一个线程。只有当您不使用异步文件I/O时,这才是必要的。使用异步I/O,无论如何您都不需要多个线程,特别是在块大小很大(兆字节+)并且单线程延迟损失可以忽略不计的情况下。
在Ssd上并行化单个文件副本是没有意义的,除非你确实在一些非常奇怪的系统上。