我有几个简单的功能,比如
#define JacobiLog(x1,x2) ((x1>x2)?x1:x2)+log(1+exp(-fabs(x1-x2)))
什么是更好的实现(代码,编译,内存......) - 如上所述定义或编写一些简单的功能
double JacobiLog(double x1,double x2)
{
return ((x1>x2) ? x1 : x2) + log(1+exp(-fabs(x1-x2)));
}
我有几个简单的功能,比如
#define JacobiLog(x1,x2) ((x1>x2)?x1:x2)+log(1+exp(-fabs(x1-x2)))
什么是更好的实现(代码,编译,内存......) - 如上所述定义或编写一些简单的功能
double JacobiLog(double x1,double x2)
{
return ((x1>x2) ? x1 : x2) + log(1+exp(-fabs(x1-x2)));
}
编译器可能会自动将您的函数设置为inline。您应该使用它而不是定义。
在您使用定义的情况下,它还将避免意外的行为
double num = JacobiLog(x++, y++);
我让你想象一下代码替换的问题......
即使“定义”更快(因为它阻止了函数调用),编译器也可以优化和内联您的函数,并使其尽可能快。
如果您在 c++ 环境中,则应始终使用模板和函数。它将使您的程序更具可读性并防止类型错误。
在 C 中,宏可能很有用,因为没有指定类型(参见下面的示例):
/* Will work with int, long, double, short, etc. */
#HIGHER(VAL1, VAL2) ((VAL1) > (VAL2) ? (VAL1) : (VAL2))
define
可能会快一点,但很可能编译器无论如何都会内联函数(或者你可以标记为内联)并且它们是相同的。但是函数更好,因为它更具可读性和更易于调试。
假设一个好的编译器,这个功能更好。
对于函数,代码是否内联由编译器决定(假设每个使用它的人都可以访问函数的定义,例如,如果它是inline
在 C++ 的头文件中声明的函数,或者只是一个其所有用户在同一个翻译单元中的普通函数)。使用宏,它总是内联的,这不一定更快,因为它可能导致代码膨胀,从而导致更多的缓存未命中和页面错误。
更不用说宏难以阅读,更糟糕的是,难以调试。
首先(对于完整的答案),我们必须承认使用宏可能会产生意想不到的副作用,并且函数可确保您知道传入的类型,并且您知道每个参数只计算一次。
通常,使用宏的这些影响是问题的根源。
一般来说,编译器会在适当的时候内联函数,如果它正确地完成了它的工作,那么它应该几乎具有宏的所有优点,但没有很少的副作用。
但是,有时您实际上可以获得内联编译器可能无法识别的一些好处。例如,您的宏将暂时推迟将参数转换double
为“int
或” long
,并在整数算术中执行更多操作(这可能具有性能或精度优势)。您可能还会得到整数溢出和不正确的结果。
由于您在“更好”因素列表中包含“内存”,因此很容易说该函数更小(假设您将编译器配置为优化大小),但这不一定是真的。
显然,作为一个函数,您在内存中只需要一个副本,所有调用者都可以使用相同的代码,而每次使用时内联或扩展都会复制代码。您的编译器不太可能隔离宏并将其转换为从代码中许多不同位置调用的函数。
一个从不内联的函数不能变小的地方就是它阻碍了简化。我能想到三种常见的情况:
这是一个微优化。除非您正在进行嵌入式编程并且每条指令都很重要,否则请使用该功能。更不用说这log
可能比调用函数的开销慢大约 100 倍。所以如果你的程序主要是调用这个函数,你只能节省大约 1%。[1]一旦你的程序开始做其他重要的事情,这种节省将减少到基本上不明显。
编译器可以尽可能自由地内联函数,这将使两者相同。但是,您不能强制编译器这样做。C++中有一个inline
关键字,但这只是一个提示,编译器可以随意忽略它。
请参阅this了解两者之间的一些差异(这涵盖了内联函数和非内联函数,但如上所述,内联函数与#define
's 基本相同)。该链接的基本结论是“取决于”。
另请注意,在行为上,a#define
和 a 函数不是 100% 等效的。
[1]:数字大部分是虚构的。如果您想要准确的结果,请进行基准测试。