当前位置 :首页 >> 情感

Linux管道到底能有多慢速?

2024-01-13   来源 : 情感

size; while(remaining> 0) { // Keep invoking 于大write于大 until we've written the entirety // of the buffer. Remember that write returns how much // it could write into the destination 就让是 in this case, // our pipe. ssize_twritten = write( STDOUT_FILENO, buf + (buf_size - remaining), remaining ); remaining -= written; } } }

为了简练起见,这段及左边的标识符段都开头了所有正确地安仅有检查。memset 除了保障负载可被扫描,还起到了另一个起到,我们将在左边研讨。

所有的临时工都是通过 write 读过取顺利完成的,其余其余部分是适当整个统枚举据流都被擦除。擦除前端非常典型,只是将统枚举据擦除到 buf 中的面,并在擦除足够的统枚举据时取消。

构建后,索引中的面的标识符可以按如下模式接入:

%./write | ./ read 3.7GiB/s, 256KiB buffer, 40960 iterations (10GiB piped)

我们擦除有所多种不同的 256KiB 统枚举据流,其中的面凿出了 40960 次“X”,并测算运输量。令人烦恼的是,排外应速度比 fizzbuzz 很慢 10 倍!我们只是将bit擦除燃气,而没花钱任何其他临时工。断言证明,通过采用 write 和 read,我们未获比这相比较的排外应速度。

write的主因

为了找出接入程序来的星期花在了什么上,我们可以采用 perf:

% perf record -g sh -c './write | ./read' 3.2GiB/ s, 256KiB buffer, 40960iterations ( 10GiB piped) [ perf record: Woken up 6timesto writedata ] [ perf record: Captured andwrote 2.851MB perf.data ( 21201samples) ]

-g 默认就是指示 perf 日志读过取示意图:这可以让我们从上到下拍照星期花费在哪中的。

我们可以采用 perf report 来拍照花费星期的范围内内。上面是一个未加编辑的完整版,详细列出了 write 的星期花费所在:

% perf report -g 就让是symbol-filter=write - 48.05% 0.05% write libc-2.33.so [.] 短时GI短时_libc_write - 48.04% 短时GI短时_libc_write - 47.69% entry_SYSCALL_64_after_hwframe - do_syscall_64 - 47.54% ksys_write - 47.40% vfs_write - 47.23% new_sync_write - pipe_write + 24.08% copy_page_from_iter + 11.76% 短时alloc_pages + 4.32% schedule + 2.98% 短时wake_up_common_lock 0.95% _raw_spin_lock_irq 0.74% alloc_pages 0.66% prepare_to_wait_event

47% 的星期花在了 pipe_write 上,也就是我们在向燃气擦除时,write 所干的事情。这十分怪异——我们花了左右一半的星期透过擦除,另一半星期透过擦除。

在 pipe_write中的面,3/4 的星期采用激活或分派网址(copy_page_from_iter和短时alloc_page)。如果我们从没对文件子系统和服务器空间内相互间的通信是如何临时工的有所认识到,就才会发觉这有一定凡事。不管怎样,要仅仅仅仅阐释暴发了什么,我们需首先以阐释燃气如何临时工。

燃气是由什么包含?

在 include/linux/pipe_fs_i.h 中的面可以看到度量燃气的统计多线程,fs/pipe.c 中的面有对其透过的转换。

Linux 燃气是一个圆形统枚举据流,保存对统枚举据擦除和擦除的网址的提到:

上示意图中的面的圆形统枚举据流有 8 个滑动位,但确实或多或少,绑定为 16 个。x86-64 核心中的面每个网址大小不一是 4KiB,但在其他核心中的面确实有所多种不同。这个燃气总共可以容纳 32KiB 的统枚举据。这是一个举例来说:每个燃气都有一个上限,即它在剩以以前可以容纳的统枚举据总和。

示意图中的面的镜子其余部分坚称当以前燃气统枚举据,非镜子其余部分坚称燃气中的面的空余空间内。

实在太排外直觉,head 磁盘燃气的擦除前端。也就是说,擦除程序来将擦除 head 就是连到的统枚举据流,如果只能较很慢移动到下一个统枚举据流,则适当地上升 head。在写就统枚举据流中的面,len 磁盘我们在其中的面写就了多少。

相排外,tail 磁盘燃气的擦除前端:擦除程序来将从那中的开始采用燃气。offset 就是指示从何处开始擦除。

忽略,tail 可以出今日 head 最后,如示意图中的面下面,因为我们采用的是周而复始/圆形统枚举据流。还要忽略,当我们并未仅仅仅仅凿出燃气时,一些滑动位确实并未采用——中的面间的 NULL 单元。如果燃气已剩(网址中的面并未NULL和能用空间内),write 将被阻断。如果燃气为空(仅有 NULL),则 read 将被阻断。

上面是 pipe_fs_i.h 中的面 C 统计多线程的节略发行版:

structpipe_inode_info{ unsignedinthead; unsignedinttail; structpipe_buffer* bufs; };

structpipe_buffer{ structpage* page; unsignedintoffset, len; };

这中的我们开头了许多的设计文件,也还并未说明 struct page 中的面存什么,但这是阐释如何从燃气透过举例来说的关键统计多线程。

举例来说燃气

今日让我们回到 pipe_write 的度量,先以以前阐释以末尾揭示的 perf 负载。pipe_write 临时工基本概念简要陈述如下:

1.如果燃气已剩,继续以前进空间内并重新启动;

2.如果 head 当以前就是连到的统枚举据流有空间内,首先以凿出该空间内;

3.总括整天滑动位,还有悉数的bit要写就时,分派在此之后网址并凿出它们,备份head。

擦除燃气时的转换

上述转换被一个锁保护,pipe_write 根据只能给与和释放出来该锁。

pipe_read 是 pipe_ write 的终前端,多种都是在于消费网址,仅仅仅仅擦除网址后将其释放出来,并备份 tail。

因此,当以前的检视每一次转化成了一个令人非常不快的原因:

每个网址激活两次,一次从服务器磁盘激活到文件子系统,另一次从文件子系统激活到服务器磁盘; 一次激活一个 4KiB 的网址,此后还与诸如举例来说相互间的不间断、网址分派与释放出来等其他转换环环相扣在一起; 由于大幅分派新网址,早就检视的磁盘确实不月份; 检视此后只能多年来给与和释放出来燃气锁。

在本机组人员,顺序 RAM 擦除排外应速度约为 16GiB/s:

%sysbench memory 就让是memory-block-size=1G 就让是memory-oper= read就让是threads=1 run ...102400.00 MiB transferred (15921.22 MiB/sec)

考虑到上面列出的所有主因,与单线程顺序 RAM 排外应速度相比,很慢 4 倍就让不足为怪。

修正统枚举据流或燃气大小不一以缩减子系统读过取和不间断数据量,或者修正其他常量不才会有多大帮助。幸运的是,有一种作法可以仅仅仅仅避免举例来说缓很慢。

用拼凑透过一般化

这种将统枚举据流从服务器磁盘激活到文件子系统再激活进去,是只能透过较很慢 IO 的人时常遇见的头疼主因。一种相似的解决建议书是将文件子系统转换从检视每一次中的面剔除,仅仅上督导 IO 转换。例如,我们可以仅仅上与二路由器交互,并绕过文件子系统以极高于时间延迟局域网。

并不一定当我们擦除套接字、文件或也就是说的燃气时,首先以擦除文件子系统中的面的某个统枚举据流,然后让文件子系统顺利完成其临时工。在燃气的主因下,燃气就是文件子系统中的面的一系列统枚举据流。如果我们注意安仅有性,则所有这些激活都是不可取的。

幸好,当我们要在燃气中的面较很慢移动统枚举据时,Linux 还包括子系统读过取以较外应速度,而无必需激活。说明而言:

splice 将统枚举据从燃气较很慢移动到文件xml,排外之亦然; vmsplice 将统枚举据从服务器磁盘较很慢移动到燃气中的面。

关键是,这两种转换都在不激活任何以下内容的主因下临时工。

既然我们发觉了燃气的临时工基本概念,就可以约莫似乎这两个转换是如何临时工的:它们只是从某处“可让”一个既有的统枚举据流,然后将其取出燃气环统枚举据流,或者排外过来,而不是在只能时分派新网址:

我们很快就才会看着它是如何临时工的。

Splicing 借助于

我们用 vmsplice 替换 write。vmsplice 签名如下:

structiovec{ void*iov_base; // Starting addresssize_tiov_len; // Number of bytes};

// Returns how much we've spliced into the pipessize_tvmsplice( intfd, conststruct iovec *iov, size_tnr_segs, unsignedintflags );

fd是最大限度燃气,struct iovec *iov 是必定会较很慢移动到燃气的统枚举据流嵌套。忽略,vmsplice 送回“拼凑”到燃气中的面的数目,确实不是完整数目,就像 write 送回擦除的数目一样。别忘了燃气受其在圆形统枚举据流中的面的滑动位数目的放宽,而 vmsplice 不受此放宽。

采用 vmsplice 时还只能小心翼翼一点。服务器磁盘是在不激活的主因下较很慢移动到燃气中的面的,因此在倚重拼凑统枚举据流以以前,需适当擦除前端采用它。

为此,fizzbuzz 采用双后端建议书,其临时工基本概念如下:

将 256KiB 统枚举据流分出; 将燃气大小不一增设为 128KiB,相当于将燃气的圆形统枚举据流增设为有着128KiB/4KiB=32 个滑动位; 在擦除以前半个统枚举据流或采用 vmsplice 将其较很慢移动到燃气中的面相互间透过选取,并对另一半统枚举据流督导有所多种不同转换。

燃气大小不一增设为 128KiB,并且继续以前进 vmsplice 仅仅仅仅负载一个 128KiB 统枚举据流,这就保障了当顺利完成一次 vmsplic 乘积时,我们已知以前一个统枚举据流已被仅仅仅仅擦除——否则未将在此之后 128KiB 统枚举据流仅仅仅仅 vmsplice 到 128KiB 燃气中的面。

今日,我们显然还并未向统枚举据流擦除任何以下内容,但我们将保存双后端建议书,因为任何仅仅擦除以下内容的程序来都只能典型的建议书。

我们的写就周而复始今日像是像这样:

intmain{ size_tbuf_size = 1<< 18; // 256KiBchar* buf = malloc(buf_size); memset(( void*)buf, 'X', buf_size); // output Xschar* bufs[ 2] = { buf, buf + buf_size/ 2}; intbuf_ix = 0; // Flip between the two buffers, splicing until we're done.while( true) { structiovecbufvec= { .iov_base = bufs[buf_ix],.iov_len = buf_size/ 2};buf_ix = (buf_ix + 1) % 2; while(bufvec.iov_len> 0) { ssize_tret = vmsplice(STDOUT_FILENO, Companybufvec, 1, 0); bufvec.iov_base = ( void*) ((( char*) bufvec.iov_base) + ret); bufvec.iov_len -= ret;}}}

都有是采用 vmsplice 而不是 write 擦除的结果:

%./write 就让是write_with_vmsplice | ./ read12.7GiB/s, 256KiB buffer, 40960 iterations (10GiB piped)

这使我们所必需的激活量缩减了一半,并且把运输量提升了三倍多,达到 12.7GiB/s。将擦除前端更更名采用 splice 后,消除了所有激活,又获了 2.5 倍的加速:

%./write 就让是write_with_vmsplice | ./ read就让是read_with_splice 32.8GiB/s, 256KiB buffer, 40960 iterations (10GiB piped)

网址一般化

紧接著呢?让我们问 perf:

% perf record -g sh -c './write 就让是write_with_vmsplice | ./read 就让是read_with_splice'33.4GiB/ s, 256KiB buffer, 40960iterations ( 10GiB piped) [ perf record: Woken up 1timesto writedata ] [ perf record: Captured andwrote 0. 305MB perf.data ( 2413samples) ] % perf report 就让是symbol-filter=vmsplice- 49.59% 0. 38% writelibc- 2.33.so [.] vmsplice - 49.46% vmsplice - 45.17% entry_SYSCALL_64_after_hwframe - do_syscall_64- 44.30% 短时do_sys_vmsplice + 17.88% iov_iter_get_pages + 16.57% 短时mutex_lock.constprop. 03.89% add_to_pipe 1.17% iov_iter_advance 0. 82% mutex_unlock 0. 75% pipe_lock 2.01% 短时entry_text_start 1.45% syscall_return_via_sysret

大其余部分星期耗损在锁定燃气以透过擦除(短时mutex_lock.constprop.0)和将网址较很慢移动到燃气中的面(iov_iter_get_pages)两个转换。关于锁定能一般化的不多,但我们可以提升 iov_iter_get_pages 的安仅有性。

顾名思义,iov_iter_get_pages 将我们获取给 vmsplice 的 struct iovecs 变换为 struct pages,以取出燃气。为了阐释这个算子的仅仅功能,以及如何较快它的排外应速度,我们需首先以认识到 CPU 和 Linux 如何许多组织网址。

较很慢认识到分页

如你熟知,数据流十分仅仅上提到 RAM 中的面的后方:而是分派各种各种类型磁盘重定向,这些重定向被判别为物理学重定向。这种抽象化被特指各种各种类型磁盘,我们在这中的不介绍它的各种优势——但最轻纤的是,它并行了接入多个数据流对同一物理学磁盘的公平竞争。

无论何种主因下,每当我们督导一个程序来并从磁盘读过取/磁盘到磁盘时,CPU 都只能将各种各种类型重定向变换为物理学重定向。磁盘从每个各种各种类型重定向到每个相同物理学重定向的给定是不真实世界的。因此,磁盘被包含大小不一一致的块,被称作网址,各种各种类型网址被给定到物理学网址:

4KiB 并并未什么时是:每种核心都根据各种优劣选取一种大小不一——我们将很快将探究其中的面的一些。

为了使这点更陈述,让我们似乎一下采用 malloc 分派 10000 bit:

void* buf = malloc( 10000); printf( "%p", buf); // 0x6f42430

当我们采用它们时,我们的 10k bit在各种各种类型磁盘中的面像是是月份的,但将被给定到 3 个不须月份的物理学页:

文件子系统的训练任务之一是负责管理此给定,这体今日特指std的统计多线程中的面。CPU 就是所选std形态(因为它只能阐释std),文件子系统根据只能对其透过转换。在 x86-64 核心上,std是一个 4 级 512 二路的树干,本身设在磁盘中的面。 该树干的每个键值是(你猜对了!)4KiB 大小不一,键值内就是指滑动一级的每个请注意为 8 bit(4KiB/8bytes = 512)。这些请注意还包括下一个键值的重定向以及其他文档。

每个数据流都有一个std——换句话说,每个数据流都保存了一个各种各种类型重定向空间内。当文件子系统字符串连动到数据流时,它将特定缓存 CR3 增设为该树干根的物理学重定向。然后,每当只能将各种各种类型重定向变换为物理学重定向时,CPU 将该重定向旧楼包含若干段,并采用它们基元该树干,以及算出物理学重定向。

为了缩减这些某种程度的抽象化性,都有是各种各种类型重定向 0x0000f2705af953c0 如何判别为物理学重定向的非常简单描述:

搜索从第一级开始,特指“page global directory”,或 PGD,其物理学后方磁盘在 CR3 中的面。重定向的以前 16 位未采用。 我们采用紧接著的 9 位 PGD 请注意,并滑动基元到第二级“page upper directory”,或 PUD。紧接著的9位采用从 PUD 中的面选取请注意。该每一次在上面的两个层次上重复,即 PMD(“page middle directory”)和 PTE(“page table entry”)。PTE 去找我们要URL的仅仅物理学页在哪中的,然后我们采用以以前12位来URL页内的倍数。

网址表的均匀分布形态受放宽在只能新网址时逐步建立给定。每当数据流只能磁盘时,文件子系统将用新请注意备份std。

struct page 的起到

struct page 统计多线程是这种系统的关键其余部分:它是文件子系统用来提到单个物理学页、磁盘其物理学重定向及其各种其他文档的形态。例如,我们可以从 PTE 中的面还包括的资讯(上面描述的std的以以前一级)中的面获 struct page。有时候,它被为广泛采用检视网址说明事务的所有标识符。

在燃气布景下,struct page 采用将其统枚举据保存有圆形统枚举据流中的面,正如我们从没看着的:

structpipe_inode_info{ unsignedinthead; unsignedinttail; structpipe_buffer* bufs; };

structpipe_buffer{ structpage* page; unsignedintoffset, len; };

然而,vmsplice 给与各种各种类型磁盘作为能用,而 struct page 仅仅上提到物理学磁盘。

因此我们只能将给定的各种各种类型磁盘块变换成两组 struct pages。这正是 iov_iter_get_pages 所花钱的,也是我们花费一半星期的范围内内:

ssize_tiov_iter_get_pages( struct iov_iter *i, // input: a sized buffer in virtual memorystruct page **pages, // output: the list of pages which back the input bufferssize_tmaxsize, // maximum number of bytes to getunsignedmaxpages, // maximum number of pages to getsize_t*start // offset into first page, if the input buffer wasn't page-aligned);

struct iov_iter 是一种 Linux 文件子系统统计多线程,坚称基元磁盘块的各种模式,以外 struct iovec。在我们的比如说中的面,它将就是连到 128KiB 的统枚举据流。vmsplice 采用 iov_iter_get_pages 将能用统枚举据流变换为两组 struct pages,并保存它们。既然从没发觉了分页的临时工基本概念,你可以约莫似乎一下 iov_iter_get_pages 是如何临时工的,下一节详细说明它。

我们从没较很慢认识到了许多新某种程度,概括如下:

早期 CPU 采用各种各种类型磁盘透过检视; 磁盘按固定大小不一的网址透过许多组织; CPU 采用将各种各种类型页给定到物理学页的std,把各种各种类型重定向变换为物理学重定向; 文件子系统根据只能向std添加和删除请注意; 燃气是由对物理学页的提到包含的,因此 vmsplice 需将一系列各种各种类型磁盘变换为物理学页,并保存它们。

给与页的开销

在 iov_iter_get_pages 中的面所花费的星期,显然仅仅仅仅花在另一个算子,get_user_page_fast 中的面:

%perf report -g 就让是symbol-filter=iov_iter_get_pages - 17.08% 0.17% write [kernel.kallsyms] [k] iov_iter_get_pages- 16.91% iov_iter_get_pages- 16.88% internal_get_user_pages_fast11.22% try_grab_compound_head

get_user_pages_fast 是 iov_iter_get_ pages 的一般化发行版:

intget_user_pages_fast( // virtual address, page alignedunsignedlongstart, // number of pages to retrieveintnr_pages, // flags, the meaning of which we won't get intounsignedintgup_flags, // output physical pagesstructpage **pages )

这中的的“user”(与“kernel”比较)就是指的是将各种各种类型页变换为对物理学页的提到。

为了给与 struct pages,get_user_pages_fast 仅仅仅仅按照 CPU 转换,但在软件中的面:它基元std以抽取所有物理学页,将结果磁盘在 struct pages 中的。我们的比如说中的面是一个 128KiB 的统枚举据流和 4KiB 的页,因此 nr_pages = 32。get_user_page_fast 只能基元std树干,抽取 32 个叶子,并将结果磁盘在 32 个 struct pages 中的面。

get_user_pages_fast 还只能适当物理学页在读过取方不再只能以以前不才会被倚重。这是通过在文件子系统中的面采用磁盘在 struct page 中的面的提到枚举来借助于的,该枚举采用获知物理学页在今后何时可以释放出来和倚重。get_user_pages_fast 的读过取者需在某个时候采用 put_page 旋即释放出来网址,以缩减提到枚举。

以以前,get_user_pages_fast 根据各种各种类型重定向应该从没在std中的面而展现多种不同。这就是 _fast 词组的来源:文件子系统首先以 将先以以前通过基元std来给与从没存有的std请注意和适当的 struct page,这开销比较较极高于,然后通过其他低开销的作法送回转化成 struct page。我们在开始时memset磁盘的断言,将适当活着不才会在 get_user_pages_fast 的“很慢”轨迹中的面中止,因为std请注意将在统枚举据流充剩“X”时成立。

忽略,get_user_pages 算子族不仅仅对燃气依赖于——显然它在许多驱动程式来中的面都是核心。一个典型的词汇与我们提及的文件子系统旁二路有关:二路由器驱动程式来确实才会采用它将某些服务器磁盘范围内变换为物理学页,然后将物理学页后方发送至给二路由器,并让二路由器仅仅上与该磁盘范围内交互,而无必需文件子系统参与。

大表面积网址

到目以前为止,我们所呈现出的页大小不一毕竟有所多种不同——在 x86-64 核心上为 4KiB。但许多 CPU 核心,以外 x86-64,都还包括较大的网址尺寸。x86-64 的主因下,我们不仅仅有 4KiB 的页(“标准”大小不一),还有 2MiB 甚至 1GiB 的页(“巨大”页)。在本文的悉数其余部分中的面,我们只研讨2MiB的大页,因为 1GiB 的页相当出名,对于我们的训练任务来说纯属节约。

当代都用核心中的面的页大小不一,来自维基

大页的主要优势在于负责管理开销低于,因为覆盖有所多种不同磁盘量所必需的页极少。此外其他转换的开销也低于,例如将各种各种类型重定向判别为物理学重定向,因为所只能的std少了一级:以一个 21 位的倍数代替页中的面原本的12位倍数,从而少一级std。

这加重了检视此变换的 CPU 其余部分的担忧,因而在许多主因下提升了安仅有性。但是在我们的比如说中的面,担忧不在于基元std的操作子系统,而在文件子系统中的面接入的软件。

在 Linux 上,我们可以通过多种模式分派 2MiB 大页,例如分派与 2MiB 中间的磁盘,然后采用 madvise 去找文件子系统为获取的统枚举据流采用大页:

void* buf = aligned_alloc( 1<< 21, size); madvise(buf, size, MADV_HUGEPAGE)

连动到大页又给我们的程序来带来了约 50% 的安仅有性降低:

%./write 就让是write_with_vmsplice 就让是huge_page | ./ read就让是read_with_splice 51.0GiB/s, 256KiB buffer, 40960 iterations (10GiB piped)

然而,降低的主因十分仅仅仅仅显而易见。我们确实才会心地善良地看来,通过采用大页, struct page 将只提到 2MiB 页,而不是 4KiB 网址。

惋惜的是,主因并非如此:文件子系统标识符假定 struct page 提到当以的“标准”大小不一的页。这种借助于作采用大页(并不一定Linux称之为“复合网址”)的模式是,“头” struct page 还包括关于背后物理学页的仅仅资讯,而月份的“颈”页仅仅还包括就是连到头页的就是磁盘。

因此为了坚称 2MiB 的大页,将有1个“头”struct page,最多 511 个“颈”struct pages。或者对于我们的 128KiB 统枚举据流来说,有 31个颈 struct pages:

即使我们只能所有这些 struct pages,以以前转化成它的标识符也才会大大较快。看到第一个请注意后,可以在一个有用的周而复始中的面转化成左边的 struct pages,而不是多次基元std。这样就提升了安仅有性!

Busy looping

我们很快就要顺利完成了,我保障!再看一下 perf 的负载:

-46 .91% 0 .38% writelibc-2.33.so[.]vmsplice-46 .84% vmsplice-43 .15% entry_SYSCALL_64_after_hwframe-do_syscall_64-41 .80% 短时 do_sys_vmsplice+ 14 .90% wait_for_space+ 8 .27% 短时 wake_up_common_lock4 .40% add_to_pipe+ 4 .24% iov_iter_get_pages+ 3 .92% 短时 mutex_lock.constprop.01 .81% iov_iter_advance+ 0 .55% import_iovec+ 0 .76% syscall_exit_to_user_mode1 .54% syscall_return_via_sysret1 .49% 短时 entry_text_start

今日大量星期花费在继续以前进燃气可写就(wait_for_space),以及唤醒继续以前进燃气凿出以下内容的读过程序来(短时wake_up_common_lock)。

为了避免这些不间断开销,如果燃气未擦除,我们可以让 vmsplice 送回,并督导有事周而复始直到擦除为止——在用 splice 擦除时花钱同样的检视:

...// SPLICE_F_NONBLOCK will cause 于大vmsplice于大 to return immediately// if we can't write to the pipe, returning EAGAINssize_tret = vmsplice(STDOUT_FILENO, Companybufvec, 1, SPLICE_F_NONBLOCK); if(ret < 0CompanyCompany errno == EAGAIN) { continue; // busy loop if not ready to write}...

通过有事周而复始,我们的安仅有性又提升了25%:

%./write 就让是write_with_vmsplice 就让是huge_page 就让是busy_loop | ./ read就让是read_with_splice 就让是busy_loop 62.5GiB/s, 256KiB buffer, 40960 iterations (10GiB piped)

阐释

通过拍照 perf 负载和 Linux CVS,我们子系统地提升了程序来安仅有性。在极高安仅有性编程母语方面,燃气和拼凑十分是其实的引起轰动,而我们这中的所牵涉到的意念是:零光盘转换、圆形统枚举据流、分页与各种各种类型磁盘、不间断数据量。

尽管我开头了一些先以以前和古怪的话题,但这篇篇名还是从没失控而来得较短了:

在仅仅标识符中的面,统枚举据流是分开分派的,通过将它们放置在多种不同的std请注意中的面来缩减std争用(FizzBuzz程序来也是这样花钱的)。 记住,当采用 get_user_pages 给与std请注意时,其 refcount 上升,而 put_page 缩减。如果我们为两个统枚举据流采用两个std请注意,而不是为两个后端器共用一个std请注意的话,那么在修改 refcount 时争用极少。 通过用taskset将./write和./read数据流COM到两个核来接入次测试。 索引中的面的标识符还包括了我没用的许多其他默认,但由于这些默认无关紧要或不够古怪,所以最后并未研讨。 索引中的面还还包括get_user_pages_fast 的 一个综合基准次测试 ,能用来精确测算在用或不用大页的主因下接入的排外应速度很慢多少。 有时候,拼凑是一个实在太可疑/ 可怕 的某种程度, 它继续困扰 着文件子系统脚本语言。

请让我发觉本文应该依赖于、古怪或不一定!

致谢

非常感谢 Alexandru Scvorţov、Max Staudt、Alex Appetiti、Alex Sayers、Stephen Lelle、Peter Cawley和Niklas Hambüchen审阅了本文的底稿。Max Staudt 还帮助我阐释了 get_user_page 的一些暧昧之处。

1. 这将在风格上典型于我的atan2f安仅有性中组部(),尽管所研讨的程序来仅仅采用自学。此外,我们将在多种不同层次上一般化标识符。调优 atan2f 是在汇编母语负载就是指导下透过的纤一般化,调优燃气程序来则牵涉到拍照 perf 政治事件,并缩减各种文件子系统数据量。

2. 本次测试在摩托罗拉 Skylake i7-8550U CPU 和 Linux 5.17 上接入。你的自然环境确实才会有所多种不同,因为在基本上几年中的面,影响本文写就到程序来的 Linux 外观上多年来在大幅巨大变化,并且在未来发行版中的面确实还才会修正。

3. “FizzBuzz”据说是一个相似的核苷酸试音主因,虽然我参与者从来并未被谈到过该主因,但我有无论如何暴发过的有效论据。

4. 尽管我们固定了统枚举据流大小不一,但即就让我们采用多种不同的统枚举据流大小不一,考虑到其他停滞的影响,(运输量)数字仅仅也十分才会有很大歧异。

5. 关于古怪的先以以前,可随时参考索引。有时候,我不才会在这中的逐字激活标识符,因为先以以前十分重要。相排外,我将贴有出备份的标识符完整版。

6. 忽略,这中的我们分析了一个以外燃气擦除和擦除的shell读过取——绑定主因下,perf record伪装成所有子数据流。

7. 分析该程序来时,我忽略到 perf 的负载被来自“Pressure Stall Information”基础核心(PSI)的资讯所污染。因此这些数字取材自一个移除PSI后编译的文件子系统。这可以通过在文件子系统构建的设计中的面增设 CONFIG_PSI=n 来借助于。在NixOS 中的面:

boot.kernelPatches = [{name = "disable-psi"; patch = null; extraConfig = ''PSI n ''; }];

此外,为了让 perf 能正确地揭示在子系统读过取中的面花费的星期,需有文件子系统调试大写就字母。如何装设大写就字母因发行版而异。在最在此之后 NixOS 发行版中的面,绑定主因下才会装设它们。

8. 假如你接入了 perf record -g,可以在 perf report 中的面用 + 透过读过取示意图。

9. 被特指 tmp_page 的一般来说“备用页”显然由 pipe_read 保存,并由pipe_write 倚重。然而由于这中的毕竟只是一个网址,我未来进行它来借助于低的安仅有性,因为在读过取 pipe_write 和 pipe_ read 时,网址倚重才会被固定数据量所抵消。

10. 从技术上懂,vmsplice 还支持在另一个方向上传输统枚举据,尽管发挥起到很小。如手册页写就到:

vmsplice显然只支持从服务器磁盘到燃气的其实拼凑。排外方向上,它显然只是将统枚举据激活到服务器空间内。

11. Tris Downs 就是宣称,该建议书确实仅仅不适当,因为网址确实才会被进一步拼凑,从而延长其生命期。这个主因也出今日以以前的 FizzBuzz 博客中的面。断言上,我十分仅仅仅仅确实不带 SPLICE_F_GIFT 的 vmsplice 应该真是不适当——vmsplic 的手册页陈述它是适当的。然而,在这种主因下,也就是说只能特别小心翼翼,以借助于零光盘燃气,同时保有适当。在次而设计来中的面,擦除前端将燃气拼凑到/dev/null 中的面,因此确实文件子系统发觉可以在不激活的主因下拼凑网址,但我即已验证这应该是仅仅暴发的主因。

12. 这中的我们呈现出了一个一般化假设,其中的面物理学磁盘是一个有用的平面线性核苷酸。真实世界主因依赖于一些,但有用假设已能陈述主因。

13. 可以通过擦除 /proc/self/pagemap 来安仅有检查分派给当以前数据流的各种各种类型网址所相同的物理学重定向,并将“网址帧号”之比网址大小不一。

14. 从 Ice Lake 开始,摩托罗拉将std延展为5级,从而将较大可多线程磁盘从256TiB 上升到 128PiB。但此功能需显式开启,因为有些程序来依赖于就是磁盘的极高 16 位不被采用。

15. std中的面的重定向需是物理学重定向,否则我们才会陷入死周而复始。

16. 忽略,极高 16 位未采用:这意味着我们每个数据流最多可以检视 248 − 1 bit,或 256TiB 的物理学磁盘。

17. struct page 确实就是连到即已分派的物理学页,这些物理学页还并未物理学重定向和其他与页说明的抽象化。它们被当成对物理学网址的仅仅仅仅抽象化的提到,但不一定是对已分派的物理学网址的提到。这一暧昧论调将在左边的旁注中的面予以陈述。

18. 显然,燃气标识符总是在 nr_pages = 16 的主因下读过取 get_user_pages_fast,必要时透过周而复始,这确实是为了采用一个小的静态统枚举据流。但这是一个借助于先以以前,拼凑网址的总数仍将是32。

19. 都有其余部分是本文不只能阐释的暧昧之处!

如果std不还包括我们要URL的请注意,get_user_pages_fast 仅仅只能送回一个 struct page。最轻纤的作法是成立正确地的std请注意,然后送回适当的 struct page。

然而,get_user_pages_fast 仅仅在被要求给与 struct page 以擦除其中的面时才才会这样花钱。否则它不才会备份std,而是送回一个 struct page,给我们一个即已分派的物理学页的提到。这正是 vmsplice 的主因,因为我们只只能转化成一个 struct page 来凿出燃气,而不只能仅仅擦除任何磁盘。

换句话说,网址分派才会被时间延迟,直到我们仅仅只能时。这花费了分派物理学页的星期,但如果网址从没通过其他模式凿出正确地,则才会避免重复读过取 get_user_pages_fast 的很慢轨迹。

因此,如果我们以以前不透过 memset,就不才会“手动”将正确地页取出std中的面,结果是不仅仅在第一次读过取 get_user_pages_fast 时才会陷入很慢轨迹,而且所有紧接著读过取都才会注意到很慢轨迹,从而避免轻纤地减速(25GiB/s而不是30GiB/s):

%./write 就让是write_with_vmsplice 就让是dont_touch_pages | ./ read就让是read_with_splice 25.0GiB/s, 256KiB buffer, 40960 iterations (10GiB piped)

此外,在采用大页时,这种暴力行为不才会展现出来:在这种主因下,get_user_pages_fast 在传入一系列各种各种类型磁盘时,大页支持将正确地地凿出正确地页。

如果这一切都很混乱,不必激怒,get_user_page 和 friends 似乎是文件子系统中的面非常头疼的配角,即使对于文件子系统脚本语言也是如此。

20. 仅仅当 CPU 有着 PDPE1GB 标志时。

21. 例如,CPU还包括专用操作子系统来缓存其余部分std,即“变换后备统枚举据流”(translation lookaside buffer,TLB)。TLB 在每次字符串连动(每次改成 CR3 的以下内容)时被刷新。大页可以显著缩减 TLB 未命中的面,因为 2MiB 页的单个请注意覆盖的磁盘是 4KiB 网址的 512 倍。

22. 如果你在想“太烂了!”早就透过各种帮助来一般化和/或一般化这种主因。不太可能的文件子系统(从5.17开始)还包括了一种在此之后各种类型,struct folio,采用显式标识头页。这缩减了接入时安仅有检查 struct page 是头页还是颈页的只能,从而提升了安仅有性。其他帮助的最大限度是仅有盘移除额外的 struct pages,尽管我不发觉怎么花钱的。

刘淑刚
牙疼有什么办法可以止痛
艾得辛治风湿效果怎么样
肩周炎怎么治疗最好
艾得辛和甲氨蝶呤有什么区别
上海9月16日新增本土无症状感染者1同上

院171同上,痊愈住院191同上,在院治疗4同上,被害0同上;泌尿道HIV749同上。原有待排查的疑似患者0同上。 截至2022年9同年16日24时,总共境内外读写开放性住院患者5285...

友情链接