是否有一个GCC关键字可以允许结构体重排序?

28

我知道为什么GCC默认情况下不会重新排列结构体的成员,但我很少编写依赖于结构体顺序的代码,因此有没有一种方式可以标记我的结构体以便自动重排?


4
因为我的计算机内存很紧张,所以我总是要手动计算,以确保使用最少的填充。 - Maestro
1
@Joshua,你的架构允许未对齐的内存访问吗? - cnicutar
2
@duDE:联合体和结构体是非常不同的东西。 - Michael Burr
3
我认为是这样,但将它们转换为紧凑结构并不是解决方法,因为它只会移除填充而不重新排序以获得最佳的内存对齐方式。我想要的就是手动实现的:最少填充的内存对齐。 - Maestro
1
假定你的平台使用ELF,你可以使用dwarves中的pahole来查找可能被重新排序的结构,并自动重装这些结构。 - Hasturkun
显示剩余4条评论
4个回答

21

之前的GCC版本有-fipa-struct-reorg选项,可以在-fwhole-program+ -combine模式下允许结构重排。

  • -fipa-struct-reorg

    执行结构重新组织优化,改变类C结构的布局以更好地利用空间局部性。此转换对包含结构数组的程序有效。有两种编译模式可用:基于配置文件的(使用-fprofile-generate启用)或静态的(使用内置启发式算法)。需要-fipa-type-escape来提供此转换的安全性。它仅在整个程序模式下工作,因此需要启用-fwhole-program-combine。被此转换视为“冷”的结构不受影响(请参见--param struct-reorg-cold-struct-ratio=value)。

由于以下原因,在GCC 4.8.x中已将其删除,详见发布说明

结构体重组和矩阵重组优化(命令行选项-fipa-struct-reorg-fipa-matrix-reorg)已被移除。它们并不总是能够正常工作,也不能与链接时优化(LTO)一起使用,因此只适用于由单个翻译单元组成的程序。

然而,您仍可以在GCC SVN上尝试使用struct-reorg-branchgithub镜像进行自己的风险评估,因为它仍在积极开发中。

您还可以使用clang-tools-extra中的clang-reorder-fields工具重新排序字段。

另请参见


5
作为一个旁注,Linux内核实现了一个名为randomize_layout的属性,通过使用gcc插件来引入它。其目的是在结构的定义中使用它,使编译器随机排列字段的顺序。Linux内核在安全性方面使用它,以抵御需要知道结构布局的攻击。例如,cred结构在include/linux/cred.h中定义如下:
struct cred {
    atomic_t    usage;
#ifdef CONFIG_DEBUG_CREDENTIALS
    atomic_t    subscribers;    /* number of processes subscribed */
    void        *put_addr;
[...]
    struct user_struct *user;   /* real user ID subscription */
    struct user_namespace *user_ns; /* user_ns the caps and keyrings are relative to. */
    struct group_info *group_info;  /* supplementary groups for euid/fsgid */
    /* RCU deletion */
    union {
        int non_rcu;            /* Can we skip RCU deletion? */
        struct rcu_head rcu;        /* RCU deletion hook */
    };
} __randomize_layout;

"__randomize_layout标签在Linux源代码树的include/linux/compiler-gcc.h中定义为:"
#define __randomize_layout __attribute__((randomize_layout))

2

在GCC中没有这样的选项。而且,我确定它不能以任何明智的方式引入。关于填充优化,请参考此讨论

我知道的唯一例外是热/冷结构字段拆分,在某些情况下可以实现(即使在基于配置文件指导的模式下,我也不确定GCC能否做到这一点,我知道ICC可以)。此功能不受用户控制,并在调用图上执行,其中对数据流的保守性可证明。


1
为什么不能引入它?如果我指定了__packed属性,那么我告诉GCC以不同的方式处理结构,那么使用__reorder属性会有什么问题呢?但看起来我的问题是您链接的问题的副本。 - Maestro
2
@Joshua 根据C标准,结构体成员在内存中的顺序必须与定义时的顺序相同。使用“packed”属性可以保留顺序。重新排序属性则不行。我意识到这不是关于标准合规性的讨论。 - Austin Phillips
我认为它不能被引入的原因比标准合规性更深刻。至少我们不太关心打包结构、嵌套函数等问题。但打包取决于用户。重新排序则由编译器决定如何重新排序以及是否重新排序。因此,如果引入它,将需要对字段进行可能的重新排序,精确的指针分析以重新排序所有引用,以及引用到引用等等。这可能会使编译时间增加数倍。 - Konstantin Vladimirov
2
@KonstantinVladimirov 这不仅仅是标准的遵从问题。C和C++程序通常由许多不同的源文件和库构建而成。一旦库已经编译,您就无法更改顺序或打包方式。而且您不希望在不同模块中的相同结构中具有不同的排序成员。 - Alexey Frunze
“热/冷结构字段拆分”技术真的很有趣 :) - phuclv

1

我认为在编译整个程序时(使用lto模式,使用-flto标志),重新组织/拆分结构的元素是可能的。在这种情况下,您可以获得程序的完整图片,并且对于未逃逸的符号,应该可以重新排序它们以获得更好的缓存行为等。

在gcc主干中,这正在积极开发中。这在GNU cauldron 2015中展示过。您可能想尝试gcc主干或struct-reorg-branch。

https://gcc.gnu.org/wiki/cauldron2015?action=AttachFile&do=view&target=Olga+Golovanevsky_+Memory+Layout+Optimizations+of+Structures+and+Objects.pdf


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接