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

Fastest way to copy a large file locally

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

我说了就用cp吧。然后我被要求模仿实现 cp 本身。

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

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

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

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

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

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

此外,是否有类似 OS 的技巧,我可以将两个文件指向磁盘中的相同数据?我的意思是我知道有符号链接,但除此之外?

But I quickly realized reading in parallel is OK but writing will not work in parallel(without locking I mean) since data from one thread might overwrite others.

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

So I thought okay, lets read in parallel, put it in a queue and then a single thread will take it off the queue and write it to the file one by one.

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

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

您还希望创建初始大小与输入文件相同的输出文件。这样你的写操作永远不必扩展文件。

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

"The fastest way to copy a file" 将取决于系统 - 从存储介质到 CPU。最有可能的瓶颈是存储介质——但不一定是。想象一下 high-end 存储可以比您的系统更快地移动数据可以创建物理页面映射以将数据读入...

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

因此对于普通 single-rotating-disk workstation/desktop/laptop 系统上的本地副本,最重要的是尽量减少物理磁盘寻道。这意味着以大块(例如 1 MB)读取和写入 single-threaded,以便系统可以进行任何优化,例如 read-ahead 或写入合并。

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

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

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

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

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

要显着提高从同一个旋转物理磁盘到同一个旋转物理磁盘的副本性能,您无能为力 - 这些磁盘磁头会跳舞,这需要时间。

SSD 更容易 - 要获得最大 IO 速率,只需通过多线程使用并行 IO。但同样,"normal" IO 可能会达到最大值的 80-90%。

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

最后,最大化 IO 速率的一个重要部分是不要做会显着降低速度的事情。将物理磁盘拖到几个 KB/sec IO 速率真的很容易 - read/write 小块 from/to 整个磁盘的随机位置。如果您的写入过程将 16 字节的块丢弃到随机位置,则磁盘将几乎所有时间都用于查找,并且在执行此操作时根本不会移动太多数据。

事实上,不 "killing yourself" 糟糕的 IO 模式比花费大量精力试图在最佳情况下加快四五个百分点重要得多。

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