我花了几分钟手动重新排序结构中的字段以减少填充效果[1],感觉就像几分钟太多了。我的直觉是说我的时间可能更好地花在编写Perl脚本上,或者为我做这种优化。
我的问题是这是否也是多余的;是否已经有一些我不知道的工具,或者我应该能够启用[2]打包结构的一些编译器功能?
这个问题更加复杂,因为需要在几个不同的架构中对其进行一致优化,因此无论使用何种工具都需要能够考虑不同的结构对齐和指针大小。
编辑:快速澄清 - 我想要做的是重新排序源代码中的字段以避免填充,而不是在没有填充的情况下“编译”结构。编辑#2:另一个复杂因素:根据配置,某些数据类型的大小也可能会发生变化。显而易见的是针对不同体系结构的指针和指针差异,但也有浮点类型(16,32或64位,取决于'精确性'),校验和(8位或16位取决于“速度”)和一些其他不明显的东西。
[1]有问题的结构在嵌入式设备上被实例化了数千次,因此结构的每个4字节减少可能意味着 go 和 no-go之间的差异这个项目。
[2]可用的编译器是GCC 3. *和4. *,Visual Studio,TCC,ARM ADS 1.2,RVCT 3. *以及其他一些更加模糊的编译器。
答案 0 :(得分:6)
如果您可以挤出存储的每个单词都很关键,那么我必须建议手动优化结构。一个工具可以为你最佳地安排成员,但是它不知道,例如,这个以16位存储的值实际上永远不会超过1024,所以你可以窃取的高6位此值超过此处 ...
因此,人类几乎肯定会在这份工作中击败机器人。
[编辑]但似乎你真的不想为每个架构手动优化你的结构。也许你真的有很多架构需要支持?
我认为这个问题不适用于一般解决方案,但您可能能够将您的领域知识编码为自定义Perl / Python / something脚本,为每个架构生成结构定义。
此外,如果所有成员的大小都是2的幂,那么只需按大小排序成员(最大的第一个)就可以得到最佳的打包。在这种情况下,你可以使用好的老式的基于宏的结构建筑 - 像这样:
#define MYSTRUCT_POINTERS \
Something* m_pSomeThing; \
OtherThing* m_pOtherThing;
#define MYSTRUCT_FLOATS \
FLOAT m_aFloat; \
FLOAT m_bFloat;
#if 64_BIT_POINTERS && 64_BIT_FLOATS
#define MYSTRUCT_64_BIT_MEMBERS MYSTRUCT_POINTERS MYSTRUCT_FLOATS
#else if 64_BIT_POINTERS
#define MYSTRUCT_64_BIT_MEMBERS MYSTRUCT_POINTERS
#else if 64_BIT_FLOATS
#define MYSTRUCT_64_BIT_MEMBERS MYSTRUCT_FLOATS
#else
#define MYSTRUCT_64_BIT_MEMBERS
#endif
// blah blah blah
struct MyStruct
{
MYSTRUCT_64_BIT_MEMBERS
MYSTRUCT_32_BIT_MEMBERS
MYSTRUCT_16_BIT_MEMBERS
MYSTRUCT_8_BIT_MEMBERS
};
答案 1 :(得分:6)
有一个名为pstruct的Perl脚本,通常包含在Perl安装中。该脚本将转储结构成员偏移量和大小。您可以修改pstruct或使用其输出作为制作实用程序的起点,该实用程序按您希望的方式打包您的结构。
$ cat foo.h
struct foo {
int x;
char y;
int b[5];
char c;
};
$ pstruct foo.h
struct foo {
int foo.x 0 4
char foo.y 4 1
foo.b 8 20
char foo.c 28 1
}
答案 2 :(得分:2)
大多数C编译器都不会这样做,因为你可以做一些奇怪的事情(比如在结构中获取一个元素的地址,然后使用指针魔法来访问其余部分,绕过编译器)。一个着名的例子是AmigaOS中的双链表,它使用守护节点作为列表的头尾(这使得在遍历列表时可以避免ifs)。监护人头节点始终具有pred == null
,尾节点将具有next == null
,开发人员将两个节点转换为单个三指针结构head_next null tail_pred
。通过使用head_next
或null
的地址作为头节点和尾节点的地址,它们保存了四个字节和一个内存分配(因为它们只需要整个结构一次)。
所以你最好的选择可能是将结构编写为伪代码,然后编写一个预处理器脚本,从中创建真实的结构。
答案 3 :(得分:0)
看看#pragma pack。这会更改编译器如何对齐结构中的元素。您可以使用它来强制它们紧密包装在一起,没有空格。
答案 4 :(得分:0)
它也取决于平台/编译器。如上所述,大多数编译器将所有内容填充到4字节对齐(或更糟!),因此假设一个结构有2个短路和一个长:
short
long
short
将占用12个字节(填充2 * 2个字节)。
将其重新排序为
short
short
long
仍将占用12个字节,因为编译器会将其填充以更快地访问数据(这是大多数桌面的默认设置,因为他们更喜欢快速访问内存使用情况)。您的嵌入式系统有不同的需求,因此无论如何都必须使用#pragma pack。
至于重新排序的工具,我只需(手动)重新组织您的结构布局,以便将不同的类型放在一起。首先把所有的短裤放进去,然后把所有的短裤放进去等等。如果你要完成包装,这就是工具无论如何都会做的。在类型之间的转换点,中间可能有2个字节的填充,但我不认为值得担心。
答案 5 :(得分:0)
编译器可能不会通过自己的头重新排序结构中的字段。标准要求字段应按其定义的顺序排列。做其他事可能会以微妙的方式破坏代码。
当你写作时,当然完全有可能制作某种代码生成器,以有效的方式在字段周围进行混乱。但我更喜欢手动这样做。
答案 6 :(得分:0)
考虑如何制作这样的工具...我想我会从调试信息开始。
从源头获取每个结构的大小是一件痛苦的事。它重叠了编译器已经完成的许多工作。我对ELF不太熟悉,无法准确地说明如何从调试二进制文件中提取结构大小信息,但我知道信息存在是因为调试器可以显示它。或许obudump或binutils包中的其他内容可以为您轻松获取(至少对于使用ELF的平台)。
获得信息后,其余部分非常简单。将成员从大到小排序,尽可能保持原始结构的排序。使用perl或python,甚至可以很容易地将其与源的其余部分交叉引用,甚至可以保留注释或#ifdef,具体取决于它们的使用方式。最大的痛苦是在整个代码库中更改结构的所有初始化。让人惊讶。
这就是事情。这听起来真的很好,但我不知道有任何这样的现有的工具可以做到这一点,当你自己写的时候......我想你已经能够手动重新排序大多数你节目中的结构。
答案 7 :(得分:0)
我有同样的问题。正如另一个答案中所建议的那样,pstruct可能有所帮但是,它确实提供了我们所需要的。实际上pstruct使用gcc提供的调试信息。我根据同样的想法写了另一个脚本。
您必须使用STUBS调试信息(-gstubs
)生成程序集文件。 (有可能从矮人那里获得相同的信息,但我使用的方法与pstruct相同)。如果不修改编译过程,这是一个很好的方法是将"-gstubs -save-temps=obj"
添加到编译选项中。
以下脚本读取程序集文件,并检测在结构中添加额外字节的时间:
#!/usr/bin/perl -n
if (/.stabs[\t ]*"([^:]*):T[()0-9,]*=s([0-9]*)(.*),128,0,0,0/) {
my $struct_name = $1;
my $struct_size = $2;
my $desc = $3;
# Remove unused information from input
$desc =~ s/=ar\([0-9,]*\);[0-9]*;[-0-9]*;\([-0-9,]*\)//g;
$desc =~ s/=[a-zA-Z_0-9]+://g;
$desc =~ s/=[\*f]?\([0-9,]*\)//g;
$desc =~ s/:\([0-9,]*\)*//g;
my @members = split /;/, $desc;
my ($prev_size, $prev_offset, $prev_name) = (0, 0, "");
for $i (@members) {
my ($name, $offset, $size) = split /,/, $i;
my $correct_offset = $prev_offset + $prev_size;
if ($correct_offset < $offset) {
my $diff = ($offset - $correct_offset) / 8;
print "$struct_name.$name looks misplaced: $prev_offset + $prev_size = $correct_offset < $offset (diff = $diff bytes)\n";
}
# Skip static members
if ($offset != 0 || $size != 0) {
($prev_name, $prev_offset, $prev_size) = ($name, $offset, $size);
}
}
}
调用它的好方法:
find . -name *.s | xargs ./detectPaddedStructs.pl | sort | un