[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20230820165019.GA1606648@leoy-huanghe.lan>
Date: Mon, 21 Aug 2023 00:50:19 +0800
From: Leo Yan <leo.yan@...aro.org>
To: Gang Li <gang.li@...ux.dev>
Cc: Alex Shi <alex.shi@...ux.alibaba.com>, linux-doc@...r.kernel.org,
linux-arch@...r.kernel.org, linux-mm@...ck.org,
linux-kernel@...r.kernel.org,
Alan Stern <stern@...land.harvard.edu>,
Andrea Parri <parri.andrea@...il.com>,
Will Deacon <will@...nel.org>,
Peter Zijlstra <peterz@...radead.org>,
Boqun Feng <boqun.feng@...il.com>,
Nicholas Piggin <npiggin@...il.com>,
David Howells <dhowells@...hat.com>,
Jade Alglave <j.alglave@....ac.uk>,
Luc Maranget <luc.maranget@...ia.fr>,
"Paul E. McKenney" <paulmck@...nel.org>,
Akira Yokosawa <akiyks@...il.com>,
Daniel Lustig <dlustig@...dia.com>,
Joel Fernandes <joel@...lfernandes.org>,
Jonathan Corbet <corbet@....net>, Alex Shi <alexs@...nel.org>,
Yanteng Si <siyanteng@...ngson.cn>,
Andrew Morton <akpm@...ux-foundation.org>
Subject: Re: [PATCH v1] docs/zh_CN: add zh_CN translation for
memory-barriers.txt
On Fri, Aug 11, 2023 at 02:22:55PM +0800, Gang Li wrote:
[...]
> +CPU 的保证
> +----------
> +
> +如果我们使用了编译器屏障 READ_ONCE 和
> WRITE_ONCE,那就可以避免编译器对代码进行优化,
> +此时生成的汇编指令跟代码是一样的,CPU
> 应当对这些汇编指令提供一些最基本的保证:
I prefer to translate the orginal text but you can add extra
explanation with explict mark, something like:
一个 CPU 应当提供一些最基本的保证 (译者注1):
....
译者注1: 软件使用编译器屏障READ_ONCE()和WRITE_ONCE(), 以避免在编译阶段对代码
进行内存访问的优化,在排除编译器影响之后,CPU需要提供基本保证。
> +
> + (*) 在任何给定的 CPU 上,相互依赖的内存访问应当按顺序进行,
s/按顺序进行/按程序顺序进行
> + 这意味着对于:
> +
> + Q = READ_ONCE(P); D = READ_ONCE(*Q);
> +
> + CPU 将发出以下内存操作:
> +
> + Q = LOAD P, D = LOAD *Q
> +
> + 并始终按照该顺序。然而,在 DEC Alpha 上,READ_ONCE() 还
> + 发出一个内存屏障指令,以便 DEC Alpha CPU 将发出以下内存操作:
> +
> + Q = LOAD P, MEMORY_BARRIER, D = LOAD *Q, MEMORY_BARRIER
> +
> + 无论是在 DEC Alpha 还是其他平台,READ_ONCE() 还可以防止编译器优化。
> +
> + (*) 对同一地址或重叠地址的读写操作应当是有序的:
在特定的CPU上,针对读写的重叠访问,操作应当顺序化:
> +
> + a = READ_ONCE(*X); WRITE_ONCE(*X, b);
> +
> + CPU 应当进行以下顺序的内存操作:
> +
> + a = LOAD *X, STORE *X = b
> +
> + 而对于:
> +
> + WRITE_ONCE(*X, c); d = READ_ONCE(*X);
> +
> + CPU 应当执行:
> +
> + STORE *X = c, d = LOAD *X
> +
> + (CPU 应当按照代码执行,不能自行优化) 。
This is not same with the original text. Should be:
(当访问的地址空间重叠时候,将其称之为读写的重叠访问)。
> +
> +如果不使用编译器屏障,编译器可能进行如下优化:
For "And there are a number of things that _must_ or _must_not_ be
assumed:"
下面是几个必能(must)和必不能(must not)的假设:
> +
> + (*) 没有 READ_ONCE() 和 WRITE_ONCE()
> 这两个编译器屏障,编译器可以在确保单线程安全
> + 的情况下进行各种优化,这些优化在 COMPILER BARRIER 部分有介绍。
(*) 当程序没有被 READ_ONCE() 和 WRITE_ONCE() 保护时,必不能假设编译器会
按照你所想的方式去访问内存。若没有使用这些保护,编译器可以做各种
"有创造性"的优化,这些优化在 COMPILER BARRIER 部分有介绍。
> +
> + (*) 编译器会使读写乱序:
(*) 必不能假设相互没有关联的读和写操作会按照程序顺序去执行。这意味着下
面的例子:
> +
> + X = *A; Y = *B; *D = Z;
> +
> + 我们可能会得到以下任意序列:
> +
> + X = LOAD *A, Y = LOAD *B, STORE *D = Z
> + X = LOAD *A, STORE *D = Z, Y = LOAD *B
> + Y = LOAD *B, X = LOAD *A, STORE *D = Z
> + Y = LOAD *B, STORE *D = Z, X = LOAD *A
> + STORE *D = Z, X = LOAD *A, Y = LOAD *B
> + STORE *D = Z, Y = LOAD *B, X = LOAD *A
> +
> + (*) 对相同地址的访问可能会合并或丢弃。这意味着对于:
(*) 必能假设,重叠访问是可能合并或丢弃的。这意味着对于:
> +
> + X = *A; Y = *(A + 4);
> +
> + 我们可能会得到以下任意序列:
> +
> + X = LOAD *A; Y = LOAD *(A + 4);
> + Y = LOAD *(A + 4); X = LOAD *A;
> + {X, Y} = LOAD {*A, *(A + 4) };
> +
> + 对于:
> +
> + *A = X; *(A + 4) = Y;
> +
> + 我们可能会得到以下任意序列:
> +
> + STORE *A = X; STORE *(A + 4) = Y;
> + STORE *(A + 4) = Y; STORE *A = X;
> + STORE {*A, *(A + 4) } = {X, Y};
> +
> +上述内容不适用于如下情况:
CPU 的保证不适用于如下情况:
> +
> + (*) 不适用于位域,因为编译器通常会生成使用非原子性的 读-修改-写
> 序列修改这些位域的代码。
> + 不要尝试使用位域来同步并行算法。
> +
> + (*)
> 给定位域中的所有字段必须由一个锁保护。如果给定位域中的两个字段受不同锁保护,编译器的
> + 非原子性读-修改-写序列可能会导致更新一个字段时破坏相邻字段的值。
> +
> + (*) 这些保证仅适用于正确对齐且大小正确的标量变量。"正确大小"
s/大小正确/正确大小
> 目前意味着变量的大小与
> + "char"、"short"、"int" 和 "long" 相同。"正确对齐"
> 指的是自然对齐,因此对于
> + "char" 没有约束,"short" 需要两字节对齐,"int" 需要四字节对齐,对于 32
> 位和 64
> + 位系统上的 "long" 分别需要四字节或八字节对齐。请注意,这些保证已引入 C11
> 标准,
> + 因此在使用较旧的编译器 (例如 gcc 4.6) 时要小心。包含此保证的标准部分
> + 是第 3.14 节,它将 "memory location" 定义如下:
> +
> + 内存位置
> + 是标量类型的对象,或者是所有宽度非零的相邻位域的最大序列
> +
> + 注意1:两个执行线程可以更新和访问单独的内存位置,而不会相互干扰。
s/单独的/各自的
> +
> + 注意2:位域和相邻的非位域成员位于单独的内存位置。对于两个相邻位域,如
s/单独的/各自的
> + 果其中一个位域在嵌套结构中,而另一个没有,或者两个位域之间隔着一个零
> + 长度的位域,或者他们被一个非位域成员分隔,那这两个位域也位于单独的内存
s/单独的/各自的
> + 位置。如果在两个位域之间所有的成员也都是位域,那么无论两个位域间插入
> + 多少位域,都认为是一个内存地址,同时更新这两个位域是不安全的。
如果在同一个数据结构里面,两个位域之间所有的成员也都是位域,那么无论两个位
域间插入多少位域,同时更新这两个位域是不安全的。
应该删掉“都认为是一个内存地址“,在原文中并没有这样的表达。
> +
> +
> +=========================
> +什么是内存屏障?
> +=========================
> +
> +如上所述,独立的内存操作在 CPU 外看起来是随机执行的,这对 CPU 之间的交互和 I/O
如上所述,为了高效的执行,相互独立的内存操作是可随机顺序执行的,但是也会在 CPU 之间
的交互和 I/O 访问时造成问题。
> 可能会
> +造成问题。我们需要一种方法来限制编译器和 CPU 的乱序。
s/一种/一些
> +
> +内存屏障就是这样的干预手段。它们使得屏障两侧的内存操作不能跨越屏障。
屏障强加给前后的内存操作,使其部分顺序化。
也可以考虑加上"并且在系统里能感知到顺序化".
> +
> +这种强制排序很重要,因为系统中的 CPU 和其他设备可以使用各种技巧来提高性能,
> +包括重排序、延迟执行、组合内存操作、预读、分支预测以及各种类型的缓存。内存屏
s/预读/预测读取/
预读比较容易和readahead产生歧义.
> +障用于覆盖或抑制这些技巧,使代码能够合理地控制多个 CPU
s/覆盖/规避
> 和/或设备之间的交互。
从而在CPU之间和/或设备之间, 代码能够正常地控制交互。
> +
> +
> +内存屏障的种类
> +---------------------------
> +
> +内存屏障主要有四种基本类型:
> +
> + (1) 写(store 或 write)屏障。
> +
> +
> 写内存屏障保证,在系统的其他组件看来,所有屏障前的写操作都在该屏障后的写操作执行前完成。
> +
> + 写屏障仅对写进行排序;不要求对读(LOAD)排序。
排序更多的是"sorted".
写屏障仅对写部分顺序化;不要求对读(LOAD)产生影响。
> +
> +
> 写屏障前的写操作不会被乱序到写屏障之后,写屏障之后的写操作不会被乱序到写屏障前。
"A CPU can be viewed as committing a sequence of store operations to the
memory system as time progresses. All stores _before_ a write barrier
will occur _before_ all the stores after the write barrier."
从时间推移的角度,可以看作一个CPU给内存系统提交了一序列的写操作。从
而,写屏障之前的写操作会先完成,写屏障之后的写操作会后完成。
> +
> + [!] 请注意,写屏障通常应与读屏障或地址依赖屏障配对;请参阅 "SMP 屏障配对"
> 子节。
Thanks,
Leo
Powered by blists - more mailing lists