web-dev-qa-db-fra.com

Passer des arguments à des fonctions avec des paramètres const: est-ce plus rapide?

Considérons, par exemple:

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

vs.

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

La seconde approche en général est-elle plus rapide?

Les paramètres de fonction en C sont copiés et envoyés à la fonction, de sorte que les modifications apportées à la fonction n'affectent pas les valeurs d'origine. Mon raisonnement est que dans la seconde sum ci-dessus, le compilateur sait avec certitude que a et b ne sont pas modifiés dans la fonction, de sorte qu'il peut simplement passer les valeurs d'origine sans les copier au préalable. C'est pourquoi je pense que la seconde sum est plus rapide que la première. Mais je ne sais pas vraiment. Dans l'exemple simple particulier de sum ci-dessus, les différences, le cas échéant, devraient être minimes.

Edit: L'exemple sum illustre simplement mon propos. Je ne m'attends pas à ce que dans cet exemple particulier, il y ait de grandes différences. Mais je me demande si, dans des situations plus complexes, le modificateur const dans un paramètre de fonction peut être exploité par le compilateur pour rendre la fonction plus rapide. Je doute que le compilateur puisse toujours déterminer si un paramètre est modifié dans une fonction (d'où ma deuxième question ci-dessous); par conséquent, je m'attendrais à ce que lorsqu'il trouve un modificateur const, il fasse autre chose que lorsqu'il n'y a pas de modificateur const.

Question: En général, une fonction sera plus rapide lorsque ses arguments sont const, que lorsqu'ils ne le sont pas?

Question 2: En général, un compilateur C peut-il (théoriquement) toujours déterminer si un paramètre de fonction est modifié à l'intérieur de la fonction?

17
becko

Réponse courte: Non

Réponse longue, non, avec preuve.

J'ai effectué ce test plusieurs fois et je n'ai constaté aucune différence en temps réel sur mon MacBook Pro compilé avec 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;
}

Sortie

 add a pris 4875711 tiques 
 cadd a pris 4885519 tiques 

Ces temps doivent vraiment être pris avec un grain de sel, cependant, comme clock n'est pas la plus précise des fonctions de chronométrage, et peut être influencé par d'autres programmes en cours d'exécution.

Donc, voici l’Assemblée comparée générée:

_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

Donc, comme vous pouvez le constater, il n'y a Pas de différence entre les deux. Passer un argument en tant que const n'est qu'un indice pour l'appelant, l'argument ne sera pas modifié et, dans un scénario simple comme celui décrit ci-dessus, aucun assemblage différent ne sera compilé.

15

La réponse dépend probablement de votre compilateur, du niveau d'optimisation et de la décision du compilateur d'intégrer la fonction. Si vous êtes curieux de ces choses, il est facile de simplement regarder l’Assembly réel produit par votre compilateur et de le découvrir.

8
David Grayson

Bien que tardif pour le parti, un compilateur pourrait placer les variables définies comme const dans un segment/bloc de mémoire en lecture seule. exception à l'exécution.

- Jamey

0
Jamey Kirby

Non, les deux devraient avoir la même vitesse. pour votre raison, supposons qu'il passe les valeurs d'origine dans la fonction sum, que diriez-vous du code de la fonction sum qui modifie la valeur d'origine, par exemple un autre thread.

En général, le const n'a aucun impact sur la performance des arguments. cela a un impact sur les performances si const est une variable locale/globale, car certains calculs peuvent être déplacés au moment de la compilation comme s'il s'agissait d'un const.

0
RolandXu