gpt4 book ai didi

assembly - AMD64 缓存优化策略 - 堆栈、符号、变量和字符串表

转载 作者:行者123 更新时间:2023-12-04 03:24:35 25 4
gpt4 key购买 nike

介绍

我将在 GNU 汇编程序 (GAS) 中为 Linux x86-64(特别是我 table 上的 AMD Ryzen 9 3900X)编写自己的 FORTH“引擎”。

(如果成功,我可能会用类似的想法为复古6502和类似的自制电脑制作固件)

我想添加一些有趣的调试功能,例如将编译代码的注释保存在带有附加字符串的“N​​OP words”中,这在运行时不会做任何事情,但是当反汇编/打印出已经定义的单词时,它会打印那些评论也一样,所以它不会丢失所有标题(a b - c)和评论(这里有这个特殊的小技巧)我将能够尝试用文档定义新词,然后以某种不错的方式打印所有定义并从中创建新的图书馆,我认为这很好。 (并且已经切换到忽略“生产发布”的评论)

我在这里阅读了太多关于优化的内容,但我无法在几周内理解所有这些内容,所以我将推出微优化,直到它遇到性能问题,然后我将开始分析。

但我想至少从体面的架构决策开始。

我目前的理解:

  • 如果程序主要从 CPU 缓存而不是内存运行,那就太好了
  • 缓存以某种方式“自动”填充,但相关数据/代码尽可能紧凑且尽可能接近可能会有很大帮助
  • 我确定了一些适合缓存的区域,还有一些不太适合缓存的区域 - 我按重要性排序:
    • 汇编代码——引擎和像“+”这样的基本词——一直被使用(固定大小,.text 部分)
    • 两个堆栈 - 也一直在使用(动态的,我可能会使用 rsp 作为数据堆栈并独立实现返回堆栈 - 还不确定,哪个是“本地的”,哪个是“模拟的”)
    • 第四个字节码 - 定义和编译的词 - 在运行时使用,当速度很重要时(仍在增加大小)
    • 变量、常量、字符串、其他内存分配(在运行时使用)
    • 单词名称(“DUP”、“DROP”——仅在编译阶段定义新单词时使用)
    • 评论(每天使用一次左右)

问题:

因为有很多“堆”在增长(好吧,没有使用“免费”,所以它也可能是堆栈,或者堆栈在增长)(还有两个堆栈在向下增长)我不确定如何实现它,因此 CPU 缓存将以某种方式适本地覆盖它。

我的想法是使用一个“大堆”(并在需要时用 brk() 增加它),然后在其上分配大块的对齐内存,在每个 block 中实现“小堆”并将它们扩展到另一个大堆当旧的填满时分块。

我希望,缓存会自动获得最常用的 block ,首先在大部分时间保留它,而较少使用的 block 将大部分被缓存忽略(相应地,它只会占据一小部分并被读取并全部踢出时间),但也许我没做对。

但也许对此有更好的策略?

最佳答案

进一步阅读的第一站应该是:

so I will put out microoptimalisation until it will suffer performance problems and then I will start with profiling.

是的,开始尝试可能很好,这样您就可以使用 HW 性能计数器进行分析,这样您就可以将您正在阅读的有关性能的内容与实际发生的事情相关联。因此,在您过度优化整体设计理念之前,您会获得一些您尚未想到的可能细节的想法。您可以从一些非常小规模的东西开始学习很多关于 asm 微优化的知识,比如某个地方没有任何复杂分支的单个循环。


由于现代 CPU 使用分离的 L1i 和 L1d 缓存以及一级 TLB,因此将代码和数据放在一起并不是一个好主意。 (尤其不是读写数据;自修改代码由 flushing the whole pipeline on any store too near any code that's in-flight anywhere in the pipeline 处理。)

相关:Why do Compilers put data inside .text(code) section of the PE and ELF files and how does the CPU distinguish between data and code? - 他们没有,只有经过混淆的 x86 程序才会这样做。 (ARM 代码有时会混合代码/数据,因为 PC 相关负载在 ARM 上的范围有限。)


是的,确保您所有的数据分配都在附近应该有利于 TLB 局部性。硬件通常使用伪 LRU 分配/逐出算法,该算法通常可以很好地将热数据保存在缓存中,通常不值得尝试手动 clflushopt 任何东西来帮助它。软件预取也很少有用,尤其是在数组的线性遍历中。如果您知道稍后要访问的位置,那么这样做有时是值得的,但 CPU 无法轻易预测。

AMD 的 L3 缓存可能会使用 adaptive replacement喜欢Intel does , 试图保留更多被重用的行,而不是让它们被那些往往不会被重用的行轻易驱逐。但是 Zen2 的 512kiB L2 以 Forth 标准比较大;您可能不会有大量的二级缓存未命中。 (并且无序执行可以做很多事情来隐藏 L1 未命中/L2 命中。甚至隐藏 L3 命中的一些延迟。)当代 Intel CPU 通常使用 256k L2 缓存;如果您是通用现代 x86 的缓存阻塞,128kiB 是一个不错的 block 大小选择,假设您可以写入然后在获得 L2 命中时再次循环。

在像 Zen2 ( https://en.wikichip.org/wiki/amd/microarchitectures/zen_2#Architecture ) 或 Skylake 这样的现代 x86 上,L1i 和 L1d 缓存(每个 32k),甚至 uop 缓存(高达 4096 微指令,每条指令大约 1 或 2),与第四实现;可能大部分时间所有内容都会命中 L1 缓存,当然还有 L2。是的,代码局部性通常很好,但是 L2 缓存比典型 6502 的整个内存还多,您真的不必担心太多 :P


解释器更关心的是分支预测,但幸运的是 Zen2(以及自 Haswell 以来的英特尔)有 TAGE 预测器,即使只有一个“大中央调度”分支,它也能很好地学习间接分支的模式:Branch Prediction and the Performance of Interpreters -Don’t Trust Folklore

关于assembly - AMD64 缓存优化策略 - 堆栈、符号、变量和字符串表,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/67841704/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com