在本地复制大文件的最快方法

Posted

技术标签:

【中文标题】在本地复制大文件的最快方法【英文标题】:Fastest way to copy a large file locally 【发布时间】:2018-09-07 20:32:44 【问题描述】:

我在一次采访中被问到这个问题。

我说让我们使用 cp。然后我被要求模仿实现 cp 本身。

所以我想好吧,让我们打开文件,一个一个地读取并写入另一个文件。

然后我被要求进一步优化它。我想让我们进行大块的读写操作。关于什么是好的块大小,我没有一个好的答案。请帮我解决这个问题。

然后我被要求进一步优化。我想也许我们可以从不同的线程并行读取并并行写入。

但我很快意识到并行读取是可以的,但写入不会并行工作(我的意思是没有锁定),因为来自一个线程的数据可能会覆盖其他线程。

所以我想好吧,让我们并行读取,将其放入队列中,然后单个线程将其从队列中取出并逐个写入文件。

这甚至会提高性能吗? (我的意思不是小文件。如果是大文件,开销会更大)

另外,有没有一种操作系统技巧,我可以将两个文件指向磁盘中的相同数据?我的意思是我知道有符号链接,但除此之外?

【问题讨论】:

但我很快意识到并行读取是可以的,但写入不会并行工作(我的意思是没有锁定),因为来自一个线程的数据可能会覆盖其他线程。 这是基于什么在?有多种方法可以从多个线程以不需要锁定的方式写入文件。您可以多次使用pwrite()open() 文件。并行写入大多数文件的真正问题是物理磁盘磁头所需的额外寻道。但是,如果文件系统是高端 HPC 文件系统,则文件可以分布在多个磁盘上,并且并行写入可以更快 【参考方案1】:

“复制文件的最快方式”将取决于系统——从存储介质一直到 CPU。最可能的瓶颈将是存储介质——但并非必须如此。想象一下高端存储可以比您的系统更快地移动数据创建物理页面映射以将数据读入......

一般来说,移动大量数据的最快方法是尽可能少地复制数据,并避免任何额外的操作,尤其是物理磁盘磁头寻道等 S-L-O-W 操作。

因此,对于普通单旋转磁盘工作站/台式机/笔记本电脑系统上的本地副本,最重要的事情是尽量减少物理磁盘寻道。这意味着以大块(例如 1 MB)读取和写入单线程,以便系统可以进行任何优化,例如预读或写入合并。

这可能会使您达到系统最大复制性能的 95% 甚至更好。即使是标准 C 缓冲 fopen()/fread()/fwrite() 也可能获得至少 80-90% 的最佳性能。

您可以通过几种方式获得最后几个百分点。首先,通过将您的 IO 块大小与文件系统块大小的倍数相匹配,以便您始终从文件系统中读取完整块。其次,您可以使用直接 IO 绕过通过页面缓存复制数据。去磁盘->用户空间或用户空间->磁盘比去磁盘->页面缓存->用户空间和用户空间->页面缓存->磁盘要快,但是对于单旋转磁盘副本,这不会很重要,如果它甚至可以衡量的话。

您可以使用各种dd 选项来测试复制这样的文件。尝试使用directnotrunc

您也可以尝试使用sendfile() 来避免将数据完全复制到用户空间。根据实现,这可能比使用直接 IO 更快。

预分配目标文件可能会或可能不会提高复制性能 - 这取决于文件系统。但是,如果文件系统不支持稀疏文件,那么将文件预分配到特定长度可能会非常非常慢。

要显着提高从同一个旋转物理磁盘复制到和复制到同一物理磁盘的性能,您可以做的不多 - 这些磁盘磁头会跳舞,这需要时间。

SSD 更容易 - 要获得最大 IO 速率,只需通过多个线程使用并行 IO。但同样,“正常”IO 可能是最大值的 80-90%。

对于其他类型的存储系统(例如大型 RAID 阵列和/或可以跨多个底层存储设备条带化单个文件的复杂文件系统),优化 IO 性能变得更加有趣和复杂。在此类系统上最大化 IO 涉及将软件的 IO 模式与存储特性相匹配,这可能非常复杂。

最后,最大化 IO 速率的一个重要部分是不要做显着减慢速度的事情。将物理磁盘拖到几 KB/秒的 IO 速率真的很容易——从磁盘上的随机位置读取/写入小块。如果您的写入过程将 16 字节块放到随机位置,则磁盘将花费几乎所有时间寻找,并且在执行此操作时根本不会移动太多数据。

事实上,不要用糟糕的 IO 模式“杀死自己”比花费大量精力试图在最佳情况下加快 4 或 5 个百分点要重要得多。

因为如果 IO 是一个简单系统的瓶颈,那就去买一个更快的磁盘。

【讨论】:

您好安德鲁,感谢您的详细解释。我只有一个疑问。 这意味着读取和写入大块(例如 1 MB)的单线程,以便系统可以进行任何优化,例如预读或写入合并。 --- 怎么做我们决定什么是好的块大小?甚至 1 GB 也能按上述逻辑工作,对吗? 感谢大家的帮助【参考方案2】:

但我很快意识到并行读取是可以的,但写入不会并行工作(我的意思是没有锁定),因为来自一个线程的数据可能会覆盖其他线程。

多线程通常不会加速这样的进程。您可能获得的任何性能优势都可能被同步开销所抵消。

所以我想好吧,让我们并行读取,将其放入队列中,然后单个线程将其从队列中取出并逐个写入文件。

这只会给支持异步 I/O 的系统带来优势。

要获得最大速度,您希望写入缓冲区大小是磁盘集群因子的增量(假设是硬文件系统)。这可以在允许排队异步 I/O 的系统上加速(例如,Windoze)。

您还想创建初始大小与输入文件相同的输出文件。这样您的写操作就不必扩展文件。

可能最快的文件复制可能是对输入和输出文件进行内存映射并进行内存复制。这在将映射文件视为页面文件的系统中尤其有效。

【讨论】:

可能最快的文件复制可能是对输入和输出文件进行内存映射并进行内存复制。这在将映射文件视为页面文件的系统中尤其有效。 实际上,以这种方式使用的mmap() 不太可能接近最快的方式。阅读这篇 LKML 帖子:marc.info/?l=linux-kernel&m=95496636207616&w=2 它以“但是你的测试套件(只复制一次数据)可能对 mmap() 来说是最糟糕的。Linus” 这就是我有资格的原因。由于太监文件系统的性质,我怀疑 Linux 上的 mmap 实际上将内存直接映射到文件,就像内存映射在具有硬文件系统的操作系统上所做的那样。

以上是关于在本地复制大文件的最快方法的主要内容,如果未能解决你的问题,请参考以下文章

处理大文件的最快方法?

从多个文件中读取大数据并在python中聚合数据的最快方法是啥?

java读大文件最快性能

将大文件从本地机器复制到 S3 时读取端点 URL 超时

使用 scikit-bio 读取 fastq 的最快方法

在 PHP 中获取大文件(> 2 GB)文件大小的最佳方法? [复制]