常量表达式的数学函数是否在编译时预先计算?

发布于 2024-08-17 23:03:17 字数 170 浏览 6 评论 0原文

为了方便和连贯,我倾向于使用常量表达式的数学函数(即 log(x)/log(2) 而不是 log(x)/0.3...) 。由于这些函数实际上并不是语言本身的一部分,它们也没有在 math.h 中定义(仅声明),常量函数是否会在编译时预先计算,或者是否会被浪费地计算在运行时?

I tend to use math functions of constant expressions for convinience and coherence (i.e log(x)/log(2) instead of log(x)/0.3...). Since these functions aren't actually a part of the language itself, neither are they defined in math.h (only declared), will the constant ones get precalculated at compile time, or will they be wastefully calculated at runtime?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

单身情人 2024-08-24 23:03:17

这取决于编译器和优化标志。正如 @AndrewyT 指出的,GCC 能够通过属性指定哪些函数是常量和纯函数,在这种情况下答案是肯定的,它将内联结果,正如您可以轻松检查的那样:

$ cat constant_call_opt.c 
#include <math.h>

float foo() {
        return log(2);
}

$ gcc -c constant_call_opt.c -S

$ cat constant_call_opt.s 
        .file   "constant_call_opt.c"
        .text
.globl foo
        .type   foo, @function
foo:
        pushl   %ebp
        movl    %esp, %ebp
        subl    $4, %esp
        movl    $0x3f317218, %eax
        movl    %eax, -4(%ebp)
        flds    -4(%ebp)
        leave
        ret
        .size   foo, .-foo
        .ident  "GCC: (Ubuntu 4.3.3-5ubuntu4) 4.3.3"
        .section        .note.GNU-stack,"",@progbits

那里没有函数调用,只是加载一个constant (0x3f317218 == 0.69314718246459961 == log(2))

虽然我现在手头没有任何其他编译器可以尝试,但我认为您可以期望在所有主要的 C 编译器中都有相同的行为,因为这是一个微不足道的优化。

It depends on the compiler and the optimization flags. As @AndrewyT points out, GCC has the ability to specify which functions are constant and pure via attributes, and in this case the answer is positive, it will inline the result, as you can easily check:

$ cat constant_call_opt.c 
#include <math.h>

float foo() {
        return log(2);
}

$ gcc -c constant_call_opt.c -S

$ cat constant_call_opt.s 
        .file   "constant_call_opt.c"
        .text
.globl foo
        .type   foo, @function
foo:
        pushl   %ebp
        movl    %esp, %ebp
        subl    $4, %esp
        movl    $0x3f317218, %eax
        movl    %eax, -4(%ebp)
        flds    -4(%ebp)
        leave
        ret
        .size   foo, .-foo
        .ident  "GCC: (Ubuntu 4.3.3-5ubuntu4) 4.3.3"
        .section        .note.GNU-stack,"",@progbits

no function call there, just loads a constant (0x3f317218 == 0.69314718246459961 == log(2))

Althought I have not any other compiler at hand to try now, I think you could expect the same behaviour in all the major C compilers out there, as it's a trivial optimization.

冷…雨湿花 2024-08-24 23:03:17

有些编译器会在编译时评估它们,但不能保证这种行为(并且这样做也可能会导致问题)。您需要检查您的编译器并查看它的作用。

请注意,在许多系统上,log(2) 可从 中的宏 M_LN2 获得。

Some compilers will evaluate them at compile time, but this behavior is not guaranteed (and doing so may also cause problems). You'll need to check your compiler and see what it does.

Note that on many systems, log(2) is available from the macro M_LN2 in <math.h>.

素衣风尘叹 2024-08-24 23:03:17

这取决于。如果编译器可以像运行时一样进行数学计算,并且执行链接时优化,并且库以某种中间形式保存,那么是的,可以完成。

大多数编译器不会执行所有这些操作。

It depends. If the compiler can do the math exactly as it would be done at runtime, and if link time optimizations are performed, and if the library is kept in some sort of intermediate form, then yes it could be done.

Most compilers don't do all of that.

以为你会在 2024-08-24 23:03:17

对于库函数,某些编译器可能将这些函数实现为内在函数,这意味着编译器对这些函数有足够的了解,可以在编译时用常量替换调用。是否会这样做取决于编译器。在实践中,我经常注意到一些编译器不愿意在编译时预先计算浮点表达式,即使它们不涉及任何函数调用。

在一般情况下,通常它们不会也无法在编译时计算,假设编译器对这些函数的了解不够,无法在编译时计算它们。也许它们有一些显着的运行时副作用?

一些编译器可能具有非标准的编译器相关扩展,允许用户向编译器提供有关函数的附加信息,以便编译器可以更好地优化函数调用,甚至确定是否可以用编译器替换给定的调用。时间预先计算。例如,GCC编译器支持这样的函数属性(GCC 特定扩展)为 constpure。如果参数在编译时已知,则理论上可以用编译时预计算来替换对具有 const 属性的函数的调用。虽然我不能说GCC是否真的能做到这一点。

在 C++(即使您的问题被标记为 C)语言中,计划的新功能是 constexpr 声明说明符,它具有类似的目的,并且应该具有您所描述的效果。

In case of library functions, some compiler might implement these functions as intrinsics, meaning that the compiler knows enough about the functions to replace the call with a constant at compile time. Whether it will do this depends on the compiler. In practice, I often notice that some compilers are reluctant to pre-calculate floating-point expressions at compile time, even if they don't involve any function calls.

In general case, normally they won't and can't get calculated at compile time, assuming the compiler simply doesn't know enough about these functions to be able to calculate them at compile time. Maybe they have some prominent run-time side-effects?

Some compilers might have non-standard compiler-dependent extensions that allow user to provide the additional information about the functions to the compiler, so that the compiler can optimize function calls better and even figure out whether a given call can be replaced with a compile-time pre-calculation. For example, GCC compiler supports such function attributes (GCC-specific extension) as const and pure. If the arguments are known at compile time, a call to function with the const attribute can be theoretically replaced with compile-time pre-calculation. Although I can't say whether GCC can actually do this.

In C++ (even though your question is tagged C) language a planned new feature is a constexpr declaration specifier that serves a similar purpose and supposed to have the effect you describe.

巷子口的你 2024-08-24 23:03:17

它们通常会在运行时进行计算(有关如何内联它们的信息,请参阅其他答案),尽管我不一定会使用“浪费”这个词,除非它们有很多和/或代码被执行了很多次。

不要只输入常量值,而是创建一个 #defineconst 变量来表达该值的含义(PILOG_2 等)并使用它来代替。

例如:

#define LOG_2 0.30102999566

这不会执行任何计算,您可以将值指定为您想要的任何精度,或者可以根据您的环境进行管理(32 位 v 64 位)。

They'll usually get calculated at runtime (see other answers for how to inline them), though I wouldn't necessarily use the word "wastefully" unless there were a lot of them and/or the code was executed lots of times.

Rather than just put in the constant value, create a #define or const variable that expresses what the value means (PI, LOG_2, etc.) and use that instead.

For example:

#define LOG_2 0.30102999566

This doesn't do any calculation and you can specify the value to what ever precision you desire or can manage given your environment (32 bit v 64 bit).

当梦初醒 2024-08-24 23:03:17

这种情况发生在运行时,因为该函数的代码仅在链接步骤期间可用(在编译步骤之后发生)。

要优化该步骤,请使用在使用之前初始化的全局变量。

This happens at runtime since the code for the function only becomes available during the linking step (which happens after the compile step).

To optimize the step, use a global variable which you initialize before you use it.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文