4

我对我的代码的性能有疑问。假设我在 C 中有一个结构:

typedef struct _CPoint
{
    float x, y;
} CPoint;

和一个我使用结构的函数。

float distance(CPoint p1, CPoint p2)
{
    return sqrt(pow((p2.x-p1.x),2)+pow((p2.y-p1.y),2));
}

我想知道用#define替换这个函数是否是一个聪明的主意,

#define distance(p1, p2)(sqrt(pow((p2.x-p1.x),2)+pow((p2.y-p1.y),2)));

我认为它会更快,因为不会有函数开销,我想知道是否应该对程序中的所有其他函数使用这种方法来提高性能。所以我的问题是:

我应该用#define 替换所有函数以提高代码的性能吗?

4

5 回答 5

8

不,您永远不应该根据感知到的性能差异在宏和函数之间做出决定。您应该根据函数相对于宏的优点来单独评估它。一般选择功能。

宏有很多隐藏的缺点可以咬你。举个例子,您在这里对宏的翻译是不正确的(或者至少没有保留原始函数的语义)。宏的参数distance每次被评估 2 次。想象一下我打了以下电话

distance(GetPointA(), GetPointB());

在宏版本中,这实际上导致了 4 个函数调用,因为每个参数都被计算了两次。作为一个函数,它只会distance导致 3 个函数调用(距离和每个参数)。注意:我忽略了sqrtpow在上述计算中的影响,因为它们在两个版本中是相同的。

于 2011-10-13T16:25:16.920 回答
3

有三件事:

  • distance像你上面的正常功能
  • 内联函数
  • 预处理器宏

虽然函数保证了某种类型的安全性,但由于每次函数调用都需要使用堆栈帧,它们也会导致性能损失。内联函数中的代码会在调用站点复制,因此不会支付罚金——但是,您的代码大小会增加。宏不提供类型安全,还涉及文本替换。

从所有三个中进行选择,我通常会使用内联函数。仅当宏非常短且以这种形式非常有用时(例如hlist_for_each来自 Linux 内核)

于 2011-10-13T16:27:17.557 回答
3

我建议使用inline函数而不是宏。它将为您提供宏的任何可能的性能优势,而不会产生丑陋。(宏有一些陷阱,使得它们作为函数的一般替代品非常不确定。特别是,宏参数在每次使用时都会被评估,而函数参数在“调用”之前每次都会被评估一次。)

inline float distance(CPoint p1, CPoint p2)
{
    float dx = p2.x - p1.x;
    float dy = p2.y - p1.y;
    return sqrt(dx*dx + dy*dy);
}

(注意我也替换pow(dx, 2)dx * dx。两者是等价的,并且乘法更有可能是有效的。一些编译器可能会尝试优化对pow... 的调用,但猜猜他们用什么替换它。)

于 2011-10-13T16:28:34.367 回答
3

Jared 是对的,在这种特定情况下,pow调用和sqrt调用所花费的周期将比调用所花费的周期多 2 个数量级distance

有时人们认为小代码等于小时间。不是这样。

于 2011-10-13T16:29:14.180 回答
1

如果使用相当成熟的编译器,如果开启优化,它将在汇编级别为您执行此操作。

对于 gcc,-O3 或(对于“小”功能)甚至 -O2 选项都可以执行此操作。

有关这方面的详细信息,您可以考虑在此处阅读http://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html以获取“-finline*”选项。

于 2011-10-13T16:28:54.850 回答