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

标签 linux file-io operating-system copy filesystems

我在面试中被问到这个问题。

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

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

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

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

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

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

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

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

最佳答案

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

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

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

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

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

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

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

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

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

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

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

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

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

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

关于linux - 在本地复制大文件的最快方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49546658/

相关文章:

linux - 如何获取其中包含多个目录的目录的链接数?

linux - 确切地说,为什么 uname -m 在 Sun Grid Engine (SGE) 运行时会报告错误的体系结构?

c# - 从文本文件中删除一行

c - 在 cygwin 上使用 fscanf() 读取 "text"文件的行

python - Mac OS X 中的 virtualenv 问题

信号量可以多次初始化吗

64 位窗口上的 Python 32 位内存限制

operating-system - 全局键盘 Hook 的合法用途是什么?

windows - 在 Windows 中,为什么有些字符是非法的?

c - 将 Fork 用于命令行参数