Apple:使用 -O0 与 -O2(内核)编译 clang 帧大小
Apple: Compile clang frame size with -O0 vs -O2 (kernel)
我有一个现有项目,我们为开发人员编译 DEBUG(和 -O0 所以 lldb 有意义)。但是我有一个特别的功能,当使用 -O0 时,它的大小会膨胀:
-O2 -Wframe-larger-than=100
warning: stack frame size of 168 bytes in function 'dsl_scan_visitbp'
-O0 -Wframe-larger-than=100
warning: stack frame size of 1160 bytes in function 'dsl_scan_visitbp'
并且通过一些递归,堆栈可能会非常垃圾(内核中有 16K 个堆栈)。
首先要检查的是任何局部变量,但我相信只有两个:
dsl_pool_t *dp = scn->scn_dp;
blkptr_t *bp_toread = NULL;
如果您想查看整个函数:
https://github.com/openzfs/zfs/blob/master/module/zfs/dsl_scan.c#L1908
(Linux 来源,但处理 Apple clang 端口)
那个sourcefile里有一堆alwaysinline
,可能也来玩一下。
但是我很好奇为什么它随着-O0 变得这么大?
然后怎么办,我看不到任何 Apple-clang #pragmas 可以在源文件中为一个函数或一个文件打开 "on" 优化(仅关闭优化)。如果我知道原因是什么,也许我可以用不同的 pragma 来控制那个特定的问题。
我现在看到的唯一解决方案是 dsl_scan.c
在 Makefile 中进行不同的处理,以便只有该文件始终获得 -O2。但这有点乏味。
我不熟悉代码库,所以我没有看到任何明显的变量会占用大量堆栈 space。但是,我注意到函数(包括 always_inline
d)很长。通常,在调试版本中,every 变量和临时表达式结果在堆栈帧中被分配一个 unique space,无论范围如何。因此,即使 2 个变量的生命周期不重叠(例如,一个在 if
块中声明,另一个在 else
块中声明)它们将在内存中分配单独的 space。所以即使有很多小的短期变量和临时值,这也可以加起来。
您最好在调试版本中禁用此函数调用的所有函数中的 always_inline
属性,因为这可以避免为所有可能的执行分支预分配内存,即使它们从未被采用,或者如果它们在不参与递归的函数中声明。
我有一个现有项目,我们为开发人员编译 DEBUG(和 -O0 所以 lldb 有意义)。但是我有一个特别的功能,当使用 -O0 时,它的大小会膨胀:
-O2 -Wframe-larger-than=100
warning: stack frame size of 168 bytes in function 'dsl_scan_visitbp'
-O0 -Wframe-larger-than=100
warning: stack frame size of 1160 bytes in function 'dsl_scan_visitbp'
并且通过一些递归,堆栈可能会非常垃圾(内核中有 16K 个堆栈)。
首先要检查的是任何局部变量,但我相信只有两个:
dsl_pool_t *dp = scn->scn_dp;
blkptr_t *bp_toread = NULL;
如果您想查看整个函数: https://github.com/openzfs/zfs/blob/master/module/zfs/dsl_scan.c#L1908 (Linux 来源,但处理 Apple clang 端口)
那个sourcefile里有一堆alwaysinline
,可能也来玩一下。
但是我很好奇为什么它随着-O0 变得这么大?
然后怎么办,我看不到任何 Apple-clang #pragmas 可以在源文件中为一个函数或一个文件打开 "on" 优化(仅关闭优化)。如果我知道原因是什么,也许我可以用不同的 pragma 来控制那个特定的问题。
我现在看到的唯一解决方案是 dsl_scan.c
在 Makefile 中进行不同的处理,以便只有该文件始终获得 -O2。但这有点乏味。
我不熟悉代码库,所以我没有看到任何明显的变量会占用大量堆栈 space。但是,我注意到函数(包括 always_inline
d)很长。通常,在调试版本中,every 变量和临时表达式结果在堆栈帧中被分配一个 unique space,无论范围如何。因此,即使 2 个变量的生命周期不重叠(例如,一个在 if
块中声明,另一个在 else
块中声明)它们将在内存中分配单独的 space。所以即使有很多小的短期变量和临时值,这也可以加起来。
您最好在调试版本中禁用此函数调用的所有函数中的 always_inline
属性,因为这可以避免为所有可能的执行分支预分配内存,即使它们从未被采用,或者如果它们在不参与递归的函数中声明。