Математические функции константных выражений предварительно рассчитываются во время компиляции? - PullRequest
12 голосов
/ 12 января 2010

Я склонен использовать математические функции постоянных выражений для удобства и согласованности (т. Е. log(x)/log(2) вместо log(x)/0.3...). Поскольку эти функции на самом деле не являются частью самого языка, они также не определены в math.h (только объявлены), не будут ли константы предварительно рассчитаны во время компиляции, или они будут расточительно вычисляться во время выполнения?

Ответы [ 6 ]

17 голосов
/ 12 января 2010

Это зависит от компилятора и флагов оптимизации. Как указывает @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

там нет вызова функции, просто загружается константа (0x3f317218 == 0.69314718246459961 == log(2))

Хотя у меня сейчас нет другого компилятора, который можно попробовать, я думаю, вы можете ожидать такого же поведения во всех основных компиляторах Си, поскольку это тривиальная оптимизация.

11 голосов
/ 12 января 2010

Некоторые компиляторы будут оценивать их во время компиляции, но такое поведение не гарантируется (и это может также вызвать проблемы). Вам нужно проверить ваш компилятор и посмотреть, что он делает.

Обратите внимание, что во многих системах log(2) доступно из макроса M_LN2 в <math.h>.

3 голосов
/ 12 января 2010

В случае библиотечных функций некоторые компиляторы могут реализовывать эти функции как встроенные, что означает, что компилятор знает достаточно о функциях, чтобы заменить вызов константой во время компиляции. Будет ли это сделано, зависит от компилятора. На практике я часто замечаю, что некоторые компиляторы не хотят предварительно вычислять выражения с плавающей точкой во время компиляции, даже если они не связаны с вызовами функций.

В общем случае, как правило, они не рассчитываются и не могут быть вычислены во время компиляции, при условии, что компилятор просто недостаточно знает об этих функциях, чтобы иметь возможность вычислять их во время компиляции. Может быть, у них есть заметные побочные эффекты во время выполнения?

Некоторые компиляторы могут иметь нестандартные зависящие от компилятора расширения, которые позволяют пользователю предоставлять дополнительную информацию о функциях компилятору, чтобы компилятор мог лучше оптимизировать вызовы функций и даже выяснить, можно ли заменить данный вызов на предварительный расчет времени компиляции. Например, компилятор GCC поддерживает такие функции атрибуты (расширение, специфичное для GCC), как const и pure. Если аргументы известны во время компиляции, вызов функции с атрибутом const теоретически может быть заменен предварительным вычислением во время компиляции. Хотя я не могу сказать, может ли GCC действительно сделать это.

В языке C ++ (даже если ваш вопрос помечен тегом C) запланированной новой функцией является спецификатор объявления constexpr, который служит для аналогичной цели и должен иметь тот эффект, который вы описываете.

3 голосов
/ 12 января 2010

Это зависит. Если компилятор может выполнять математику точно так, как это было бы сделано во время выполнения, и если выполнялась оптимизация времени соединения, и если библиотека содержалась в некоторой промежуточной форме, то да, это могло бы быть сделано.

Большинство компиляторов не делают всего этого.

3 голосов
/ 12 января 2010

Они обычно рассчитываются во время выполнения (см. Другие ответы о том, как их встроить), хотя я не обязательно буду использовать слово «расточительно», если их не было много и / или код не был выполнен много раз.

Вместо того, чтобы просто ввести постоянное значение, создайте переменную #define или const, которая выражает значение этого значения (PI, LOG_2 и т. Д.) И используйте ее вместо этого.

Например:

#define LOG_2 0.30102999566

Это не выполняет никаких вычислений, и вы можете указать значение, с какой бы точностью вы ни пожелали или могли управлять в вашей среде (32-битная или 64-битная).

0 голосов
/ 12 января 2010

Это происходит во время выполнения, поскольку код функции становится доступным только на этапе компоновки (что происходит после этапа компиляции).

Чтобы оптимизировать шаг, используйте глобальную переменную, которую вы инициализируете перед ее использованием.

...