首页 > 解决方案 > 将“falign-functions”编译器标志显式设置为某个值的动机是什么?

问题描述

我正在为嵌入式系统开发 SW,并试图了解早期开发人员设置的一些低级细节。目标平台是定制的OpenRISC 1200处理器,在 FPGA 中合成。该软件是使用基于 GCC 的交叉编译器构建的。

在编译器标志中,我找到了这个:-falign-functions=16. 构建配置中有一条评论说:

在 Open RISC 1200 上,函数对齐需要在缓存边界(16 字节)上。否则,性能将受到严重影响。

我意识到我对高速缓存的理解有点肤浅,我可能应该阅读以下内容:每个程序员应该知道的关于内存的知识。我还没有,但我会的。话虽如此,我有一些问题:

  1. 我知道这是为了最大限度地减少指令缓存中的缓存未命中,但是为什么通过将函数对齐设置为指令缓存行大小(即 16 字节)来实现呢?
  2. 如果这是最节省内存的方式,您不希望这是交叉编译器中函数对齐的默认设置吗?我的意思是,对于像 x86、amd64 或 ARM 这样的更常见的平台,您不需要关心函数对齐(或者我错了吗?)。

标签: cgccembeddedcross-compiling

解决方案


大多数体系结构都有内存访问和指令的方面,这些方面可能取决于对齐方式。

但是为什么通过将函数对齐设置为指令缓存行大小来实现

CPU 将从内存中获取完整的高速缓存行(好像内存被划分为这些更大的块而不是字节)。因此,如果您需要的所有数据都适合一个缓存行,那么只有一次提取,但如果您甚至只有 2 个字节的数据,但一个字节是缓存行的结尾,另一个字节是下一个缓存行的开始,现在它必须加载两个完整的缓存行。这浪费了小型 CPU 缓存中的空间,并进行了更多的内存传输。

快速搜索表明 OpenRISC 1200 使用 16 字节缓存行,因此当专门针对该缓存行时,对齐您在这 16 字节倍数上拥有的任何数据的开头有助于避免在一个函数/数据块中跨越两条线。

如果这是最节省内存的方式,您不希望这是交叉编译器中函数对齐的默认设置吗?

它可能不止于此。首先,这种对齐是通过浪费“填充”内存来实现的。如果您使用 1 个字节的高速缓存行调用函数,那么会浪费另外 15 个字节来达到 16 字节的边界。

同样在函数调用的情况下,内存很有可能无论如何都会在缓存中,并且向前跳转可能会离开缓存的内存,从而导致不需要的负载。

所以这留下了一个折衷,使用很少堆栈空间并快速返回的函数可能不会从额外的对齐中受益很多,但是运行时间更长并使用更多堆栈空间的函数可能会受益于不“浪费”缓存空间上的“以前的功能”。


通常需要对齐的另一个原因是在处理需要完全对齐的指令(在未对齐的地址上失败),或者速度慢得多(加载/存储被分成几部分),或者可能是其他一些影响(如加载/如果未正确对齐,则存储不是原子的)。

通过快速搜索,我相信 OR1200 的一般对齐要求似乎是 4 个字节,即使对于 8 个字节类型也是如此。所以在这方面,至少 4 的对齐似乎是可取的,而 8 或 16 可能只在前面提到的某些情况下提供好处。

我不熟悉 Open RISC,但在某些平台上稍后添加的指令(例如 16 字节 / 128 位 SSE 指令)需要或受益于比默认值更大的对齐方式(我相信 AMD64 将默认对齐方式提高到 16,但后来 AVX 想要 32 字节对齐)。


推荐阅读