1

我想按照以下方式定义一个宏:

#define MYCheckedCall(stmnt) do {         \
    status_t status = (stmnt);            \
    if (MYFail(status)) {                 \
        MYLog("Statement failed!");       \
    }                                     \
} while (0)

每隔几次调用就在整个应用程序中使用它。与到处重复代码相比,它的性能特征是什么?(即只定义 status_t 一次,没有额外的 {} 范围)

需要注意的几点:

  • 这会在堆栈中添加一个框架(以存储status_t变量),因此我对 perf 感到好奇。
  • status_t 是typedef int status_t或类似的东西。
  • 这将用于整个库的一半代码。

我知道这有点过早的优化,但只是想知道它,因为它似乎应该是一个微不足道的决定?


代码现在看起来的示例(没有宏):

status_t status = 0;
status = call1();
if (MYFail(status)) MYLog("call1 failed!");

status = call2();
if (MYFail(status)) MYLog("call2 failed!");

我希望使用宏的示例:

MYCheckedCall( call1() );
MYCheckedCall( call2() );

如果我只是 C 菜鸟并且有更好的模式/方式来实现这种功能,我也很想知道它。

我在 Mac OS X 10.7 上使用 clang/llvm-gcc。

4

3 回答 3

6

假设status_t是整数类型,我希望开销为零status每次存储在寄存器中或最坏情况下存储在同一个堆栈槽中的开销为零;编译器不会费心调整堆栈,因为它可以保留函数入口所需的最大堆栈空间并重用相同的堆栈槽。

与往常一样,如果您有兴趣,您应该检查汇编器输出以确认这是编译器所做的。

于 2012-07-10T12:16:01.723 回答
6

过早的优化不应该是一个担心。此外,代码大小通常也不是性能的可靠指标(认为重复一百万次的三行循环将比具有一千条指令的程序花费大约一千倍的时间)。所以,以后再考虑优化。

在您的情况下,我认为没有区别,因为预处理器将您的所有宏调用替换为您为宏创建的代码。所以,基本上你在谈论相同的代码(检查这个定义)。

于 2012-07-10T12:17:19.623 回答
1

我根本不会担心这个特定结构的性能。如果稍后分析证明这是一个瓶颈,那么您可以对其进行优化。而不是宏,我会使用一个函数。一旦您想扩展宏,它很快就会成为维护的噩梦。

void MYCheckedCall (status_t status, const char *fail_message)
{
    if (MYFail(status))
    {
        MYLog(fail_message);
    }
}

MYCheckedCall(call1(), "call1 failed!");
MYCheckedCall(call2(), "call2 failed!");

甚至可能将其重命名为 MYStatusCheck,因为这是函数/宏实际所做的。

于 2012-07-10T12:49:13.940 回答