Передача аргументов в функции с параметрами const: это быстрее?

Рассмотрим, например:

int sum(int a, int b)
{
    return a + b;
}

против.

int sum(const int a, const int b)
{
    return a + b;
}

Является ли второй подход в целом быстрее?

Функциональные параметры в C копируются и отправляются в функцию, поэтому изменения внутри функции не влияют на исходные значения. Мое рассуждение состоит в том, что во втором sum выше компилятор точно знает, что a и b не изменяются внутри функции, поэтому он может просто передать исходные значения, не скопировав их в первую очередь. Вот почему я думаю, что второй sum быстрее первого. Но я действительно не знаю. В конкретном простом примере sum выше различия, если они есть, должны быть минимальными.

Изменить: Пример sum предназначен только для иллюстрации моей точки. Я не ожидаю, что в этом конкретном примере должны быть большие различия. Но мне интересно, может ли в более сложных ситуациях модификатор const внутри параметра функции может быть использован компилятором, чтобы сделать функцию быстрее. Я сомневаюсь, что компилятор всегда может определить, изменяется ли параметр внутри функции (следовательно, мой второй вопрос ниже); поэтому я ожидаю, что когда он найдет модификатор const, он сделает что-то другое, чем когда нет модификатора const.

Вопрос: В общем случае функция будет быстрее, если ее аргументы const, чем когда они не являются?

Вопрос 2: В целом, может ли компилятор C (теоретически) всегда определять, изменяется ли параметр функции внутри функции?

Ответы

Ответ 1

Короткий ответ: Нет

Длинный ответ, нет, с доказательством.

Я проверил этот тест несколько раз и не видел разницы в реальном времени, на моем MacBook pro, скомпилированном с clang:

int add(int a, int b)
{
    return a + b;
}

const int cadd(const int a, const int b)
{
    return a + b;
}

int main (int argc, char * argv[])
{
#define ITERS 1000000000

    clock_t start = clock();
    int j = 0;
    for (int i = 0; i < ITERS; i++)
    {
        j += add(i, i + 1);
    }

    printf("add took %li ticks\n", clock() - start);

    start = clock();
    j = 0;
    for (int i = 0; i < ITERS; i++)
    {
        j += cadd(i, i + 1);
    }

    printf("cadd took %li ticks\n", clock() - start);

    return 0;
}

Выход

add took 4875711 ticks
cadd took 4885519 ticks

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

Итак, вот сборка, сгенерированная:

_add:
    .cfi_startproc
    pushq   %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset %rbp, -16
    movq    %rsp, %rbp
    .cfi_def_cfa_register %rbp
    movl    %edi, -4(%rbp)
    movl    %esi, -8(%rbp)
    movl    -4(%rbp), %esi
    addl    -8(%rbp), %esi
    movl    %esi, %eax
    popq    %rbp
    ret

_cadd:                                 
    .cfi_startproc    
    pushq   %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset %rbp, -16
    movq    %rsp, %rbp
    .cfi_def_cfa_register %rbp
    movl    %edi, -4(%rbp)
    movl    %esi, -8(%rbp)
    movl    -4(%rbp), %esi
    addl    -8(%rbp), %esi
    movl    %esi, %eax
    popq    %rb

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

Ответ 2

Ответ, вероятно, зависит от вашего компилятора, уровня оптимизации и того, решает ли компилятор встроить функцию. Если вам интересно об этих вещах, легко просто взглянуть на фактическую сборку, созданную вашим компилятором, и узнать.

Ответ 3

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

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

Ответ 4

Поздно, компилятор мог помещать переменные, определенные как const, в сегмент/блок памяти только для чтения, так что если была сделана попытка записать на адрес, через некоторый топик указателя, запись в память приведет к возникновению исключения во время выполнения.

- Jamey