2009-08-20 88 views
5

所以我通过展开一些循环来优化一些代码(是的,我知道我应该依靠我的编译器为我做这件事,但我没有选择编译器)和我想稍微优雅一些​​,以防万一我的数据大小因未来的一些编辑而发生变化,代码将会优雅地降级。宏的等效C sizeof

喜欢的东西:

typedef struct { 
    uint32_t alpha; 
    uint32_t two; 
    uint32_t iii; 
} Entry; 

/*...*/ 

uint8_t * bytes = (uint8_t *) entry; 
#define PROCESS_ENTRY(i) bytes[i] ^= 1; /*...etc, etc, */ 
#if (sizeof(Entry) == 12) 
    PROCESS_ENTRY(0);PROCESS_ENTRY(1);PROCESS_ENTRY(2); 
    PROCESS_ENTRY(3);PROCESS_ENTRY(4);PROCESS_ENTRY(5); 
    PROCESS_ENTRY(6);PROCESS_ENTRY(7);PROCESS_ENTRY(8); 
    PROCESS_ENTRY(9);PROCESS_ENTRY(10);PROCESS_ENTRY(11); 
#else 
# warning Using non-optimized code 
    size_t i; 
    for (i = 0; i < sizeof(Entry); i++) 
    { 
     PROCESS_ENTRY(i); 
    } 
#endif 
#undef PROCESS_ENTRY 

这不工作,当然,因为sizeof是不提供给预处理器(至少,这是this answer似乎什么来表示)。

有没有一种简单的解决方法,我可以用它来获得用于C宏的数据结构sizeof,还是我只是SOL?

+0

那么,sizeof()**是一个宏。一个内置的宏,至少。 – Havenard 2009-08-20 21:01:30

+5

sizeof是不是一个宏,在任何形状或形式 – 2009-08-20 21:08:29

+2

sizeof是不是一个宏,虽然offsetof是。 sizeof更像是一个运营商。 – 2009-08-20 21:09:16

回答

17

你不能在预处理器中执行,但不需要。刚生成宏平原if

#define PROCESS_ENTRY(i) bytes[i] ^= 1; /*...etc, etc, */ 
if (sizeof(Entry) == 12) { 
    PROCESS_ENTRY(0);PROCESS_ENTRY(1);PROCESS_ENTRY(2); 
    PROCESS_ENTRY(3);PROCESS_ENTRY(4);PROCESS_ENTRY(5); 
    PROCESS_ENTRY(6);PROCESS_ENTRY(7);PROCESS_ENTRY(8); 
    PROCESS_ENTRY(9);PROCESS_ENTRY(10);PROCESS_ENTRY(11); 
} else { 
    size_t i; 
    for (i = 0; i < sizeof(Entry); i++) { 
     PROCESS_ENTRY(i); 
    } 
} 

sizeof是一个常量表达式,并比较与常一个常数也不变。任何理智的C编译器都会优化掉编译时始终为false的分支 - 常量折叠是最基本的优化之一。不过,你失去了#warning

+0

从稍微不同的角度来看待问题总是很好的。 +1和荣誉! – qrdl 2009-08-21 06:16:01

9

如果您使用的是autoconf或其他构建配置系统,则可以在配置时检查数据结构的大小并写出标题(如#define SIZEOF_Entry 12)。当然,这在交叉编译等情况下变得更加复杂,但我假设你的构建和目标体系结构是相同的。

否则是的,你运气不好。

-1

如果您希望结构体的最小可能大小(或将其与4字节边界对齐,或其他),则可以使用打包或对齐的属性。

在Visual C++中,你可以使用#pragma pack,并且在GCC可以使用__attribute __((包装))和__attribute __((对齐(NUM字节))

+0

这不会给大小。此外,虽然打包将节省空间,但这可能会花费时间,而这正是他想要获得的。 – 2009-08-20 21:12:15

+0

@David Thornley:它不会给出尺寸,但它会做他想在这个代码块中做的事情。通过对结构进行打包,他肯定会知道填充不会妨碍,所以结构*只有12个字节(或每个项目的大小总和),因此不需要使用预处理器宏找到结构的大小。 PROCESS_ENTRY在未填充的结构上使用字节级访问,因此打包结构可以使用此宏而不必担心填充。 – 2009-08-20 21:37:06

+0

我应该补充说,通常最好不要干涉,只是让优化器做它的工作(特别是因为它比大多数程序员好得多)。 – 2009-08-20 22:56:02

5

你的运气了 - 预处理器甚至不知道结构是什么,更不用说用任何方法来计算它的大小了。更不用说用任何方式来计算它的大小了。更不用说用任何方法来计算它的大小了。

在这样的情况下,你可以仅仅定义一个常量来知道结构的大小,静态地断言它实际上等于使用负尺寸阵列技巧的尺寸。

你也可以试试if (sizeof(Entry) == 12),并查看您的编译器是否能够在编译时评估分支条件并删除死代码。这不是一个很大的问题。

+0

+1为'看优化器是否优化'建议。 – 2009-08-20 21:30:33

+0

使用assert()而不是#warning的想法也很好。你真的想要一个优雅的降级,还是你想清楚地知道这个问题,所以你可以修复它? – 2009-08-21 18:24:05

+0

有两个原因,它不能是12.一个是有一些意想不到的填充(在这种情况下,你可能想要一个错误,所以你可以使用编译器特定的编译指令打包结构),一个是你已经添加一个字段(在这种情况下,您希望更新#define以便它代表新的大小,然后还决定是否更新展开的循环以处理新的大小)。所以实际上,我会在结构体的定义点处声明断言,并且还*展开展开时的警告。 – 2009-08-21 19:47:29

1

这可能不会帮助,但如果你在C这样做是为了分派到相应的循环在编译时的能力++,你可以使用模板来使编译器:

template <std::size_t SizeOfEntry> 
void process_entry_loop(...) 
{ 
    // ... the nonoptimized version of the loop 
} 

template <> 
void process_entry_loop<12>(...) 
{ 
    // ... the optimized version of the loop 
} 

// ... 

process_entry_loop<sizeof(Entry)>(...); 
+2

好主意,但是将sizeof(Entry)作为模板参数会更清晰,并且专门用于12. – 2009-08-21 18:21:40

+0

提示:我喜欢这个! – fbrereto 2009-08-21 18:33:38

1

另外两个方法可以让人想起 - 或者编写一个小应用程序来编写展开的循环,或者使用Duff's device上的变体以及结构的预期大小。

+1

任何现代编译器生成的代码都比Duff的设备慢,这不是一个很好的编译器。 – 2009-08-21 05:48:29

+0

这不取决于它是如何偏执的代码大小?为了好玩,编译器不一定会将代码的大小乘以12。没有分析器输入,优化器无法知道哪些循环值得付费代码大小以获得速度。展开所有内容都会产生大量代码,大量的icache遗漏以及速度减慢。另一方面,您可以智能地选择展开哪些循环(或者更智能地使用可以使用探查器数据优化的编译器)。除非你的意思是Duff's Device现在已经过时了,因为我不知道更新,更好的技巧。 – 2009-08-21 23:28:14