将 "falign-functions" 编译器标志显式设置为特定值的动机是什么?
What is the motivation to explicitly set the "falign-functions" compiler flag to a certain value?
我正在为嵌入式系统开发软件,并试图了解早期开发人员设置的一些底层细节。目标平台是定制的 OpenRISC 1200 处理器,在 FPGA 中合成。该软件是使用基于 GCC 的交叉编译器构建的。
在编译器标志中,我找到了这个:-falign-functions=16
。构建配置中有一条评论说:
On Open RISC 1200, function alignment needs to be on a cache boundary (16 bytes). If not, performance suffer severely.
我意识到我对高速缓存的理解有点肤浅,我可能应该阅读类似的内容:What Every Programmer Should Know About Memory。我还没有,但我会的。话虽如此,我有一些问题:
- 我知道这是为了最大限度地减少指令缓存中的缓存未命中,但为什么要通过将函数对齐设置为指令缓存行大小(即 16 字节)来实现?
- 如果这是内存效率最高的方式,您难道不希望这是交叉编译器中函数对齐的默认设置吗?我的意思是,对于像 x86、amd64 或 ARM 这样更常见的平台,您不需要关心函数对齐(或者我错了吗?)。
大多数体系结构的内存访问和指令方面都可能依赖于对齐。
but why is that achieved by setting the function alignment to the instruction cache line size
CPU 将从内存中获取完整的缓存行(就好像内存被分成这些更大的块而不是字节)。因此,如果您需要的所有数据都适合一个缓存行,则只有一次提取,但如果您只有 2 个字节的数据,但一个字节是缓存行的结尾,另一个字节是下一个缓存行的开始,那么现在它必须加载两个完整的缓存行。这会浪费 space 小的 CPU 缓存和更多的内存传输。
A quick search 表示 OpenRISC 1200 使用 16 字节缓存行,因此当专门针对它时,将您拥有的任何数据的开头对齐那些 16 字节倍数有助于避免在一个函数中跨越两行/ 条数据。
If this is the most memory efficient way, wouldn't you expect this to be the default setting for function alignment in the cross-compiler?
可能不止于此。首先,这种对齐是通过浪费 "padding" 内存来实现的。如果您使用了缓存行的 1 个字节来调用函数,那么另外 15 个字节将被浪费以达到 16 字节的边界。
同样在函数调用的情况下,内存无论如何都会在缓存中,向前跳转可能会离开缓存的内存,从而导致本来不需要的负载。
所以这留下了一个权衡,函数使用很少的堆栈 space 和 return 很快,可能不会从额外的对齐中获益太多,但是运行时间更长并使用更多堆栈的函数space 可能受益于不 "wasting" 在 "previous function" 上缓存 space。
通常需要对齐的另一个原因是在处理完全需要它(在未对齐地址上失败)或速度慢得多(loads/stores 被分成几部分)或一些指令时其他效果(如 load/store 如果未正确对齐则不是原子的)。
通过快速搜索,我相信 OR1200 的一般对齐要求似乎是 4 个字节,即使对于 8 个字节的类型也是如此。因此,在这方面,至少 4 个对齐似乎是可取的,而 8 个或 16 个可能只在前面提到的某些情况下提供好处。
我对 Open RISC 并不特别熟悉,但在后来添加的某些平台上的指令(例如 16 字节/128 位 SSE 指令)需要或受益于比默认值更大的对齐(我相信 AMD64 提高了默认对齐为 16,但后来 AVX 需要 32 字节对齐)。
我正在为嵌入式系统开发软件,并试图了解早期开发人员设置的一些底层细节。目标平台是定制的 OpenRISC 1200 处理器,在 FPGA 中合成。该软件是使用基于 GCC 的交叉编译器构建的。
在编译器标志中,我找到了这个:-falign-functions=16
。构建配置中有一条评论说:
On Open RISC 1200, function alignment needs to be on a cache boundary (16 bytes). If not, performance suffer severely.
我意识到我对高速缓存的理解有点肤浅,我可能应该阅读类似的内容:What Every Programmer Should Know About Memory。我还没有,但我会的。话虽如此,我有一些问题:
- 我知道这是为了最大限度地减少指令缓存中的缓存未命中,但为什么要通过将函数对齐设置为指令缓存行大小(即 16 字节)来实现?
- 如果这是内存效率最高的方式,您难道不希望这是交叉编译器中函数对齐的默认设置吗?我的意思是,对于像 x86、amd64 或 ARM 这样更常见的平台,您不需要关心函数对齐(或者我错了吗?)。
大多数体系结构的内存访问和指令方面都可能依赖于对齐。
but why is that achieved by setting the function alignment to the instruction cache line size
CPU 将从内存中获取完整的缓存行(就好像内存被分成这些更大的块而不是字节)。因此,如果您需要的所有数据都适合一个缓存行,则只有一次提取,但如果您只有 2 个字节的数据,但一个字节是缓存行的结尾,另一个字节是下一个缓存行的开始,那么现在它必须加载两个完整的缓存行。这会浪费 space 小的 CPU 缓存和更多的内存传输。
A quick search 表示 OpenRISC 1200 使用 16 字节缓存行,因此当专门针对它时,将您拥有的任何数据的开头对齐那些 16 字节倍数有助于避免在一个函数中跨越两行/ 条数据。
If this is the most memory efficient way, wouldn't you expect this to be the default setting for function alignment in the cross-compiler?
可能不止于此。首先,这种对齐是通过浪费 "padding" 内存来实现的。如果您使用了缓存行的 1 个字节来调用函数,那么另外 15 个字节将被浪费以达到 16 字节的边界。
同样在函数调用的情况下,内存无论如何都会在缓存中,向前跳转可能会离开缓存的内存,从而导致本来不需要的负载。
所以这留下了一个权衡,函数使用很少的堆栈 space 和 return 很快,可能不会从额外的对齐中获益太多,但是运行时间更长并使用更多堆栈的函数space 可能受益于不 "wasting" 在 "previous function" 上缓存 space。
通常需要对齐的另一个原因是在处理完全需要它(在未对齐地址上失败)或速度慢得多(loads/stores 被分成几部分)或一些指令时其他效果(如 load/store 如果未正确对齐则不是原子的)。
通过快速搜索,我相信 OR1200 的一般对齐要求似乎是 4 个字节,即使对于 8 个字节的类型也是如此。因此,在这方面,至少 4 个对齐似乎是可取的,而 8 个或 16 个可能只在前面提到的某些情况下提供好处。
我对 Open RISC 并不特别熟悉,但在后来添加的某些平台上的指令(例如 16 字节/128 位 SSE 指令)需要或受益于比默认值更大的对齐(我相信 AMD64 提高了默认对齐为 16,但后来 AVX 需要 32 字节对齐)。