Математические функции константных выражений становятся предрасчетными во время компиляции?

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

12
задан cvb 12 January 2010 в 14:01
поделиться

6 ответов

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

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

11
ответ дан 2 December 2019 в 04:02
поделиться

Это зависит от компилятора и флагов оптимизации. Как отмечает @andrelwyt, 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.69314718246459718246459918246459718246459961 == log (2) )

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

17
ответ дан 2 December 2019 в 04:02
поделиться

ФАКТИЧЕСКИ все, что вы делаете, это изменяете ссылку на локальную переменную toUpdate , указывая на переданный аргумент product .

Позволяет сделать шаг назад, когда вы делаете:

var toUpdate = productToUpdate.First<ProductInfo>();

у вас есть ссылка на предмет из вашей коллекции ( ProductSeureResults ). Теперь вы можете с радостью обновить его свойства, ala:

toUpdate.ProductName = product.ProductName;
toUpdate.Price = product.Price;
//etc..

, однако вы не можете обновить элемент в коллекции, чтобы указать на другой/новый предмет так, как вы пытались. Вы можете удалить этот элемент из коллекции и добавить новый, если это действительно то, что вы требуете:

public void UpdateProductInfo(ProductInfo product)
    {
        var productToUpdate = this.ProductSearchResults.Where(p => p.ID == product.ID).;

        if (productUpdate.Count() > 0)
        {
            var toUpdate = productToUpdate.First<ProductInfo>();

            this.ProductSearchResults.Remove(toUpdate);
            this.ProductSearchResults.Add(product);
        }
    }

Надежда, что поможет.

-121--3823380-

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

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

-121--2760447-

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

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

3
ответ дан 2 December 2019 в 04:02
поделиться

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

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

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

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

3
ответ дан 2 December 2019 в 04:02
поделиться

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

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

Например:

#define LOG_2 0.30102999566

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

3
ответ дан 2 December 2019 в 04:02
поделиться

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

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

0
ответ дан 2 December 2019 в 04:02
поделиться
Другие вопросы по тегам:

Похожие вопросы: