Initialisation avant ou dans la boucle for

J’ai testé parce que ça m’intriguait quand même de pas avoir trouvé sur internet quelqu’un qui prouvait qu’il n’y avait aucune réelle différence

Alors j’ai utilisé ce code pour faire mes tests :

#include <stdio.h>
#include <time.h>

#define rep 10000

// On utilise le mot-clé `volatile` pour empêcher que le compilateur saute la
// boucle vide

void decl_for(void) {
    volatile int i;
    for (i = 0; i < rep; ++i) {
    }
}

void decl_fn(void) {
    for (volatile int i = 0; i < rep; ++i) {
    }
}

int main(void) {
    clock_t ta, td;

    for (int i = 0; i < 10; ++i) {
        printf("n°%d\t", i + 1);

        td = clock();
        for (int j = 0; j < rep; ++j) {
            decl_for();
        }
        ta = clock();
        long delta1 = ta - td;
        printf("for : %ld\t", delta1);

        td = clock();
        for (int j = 0; j < rep; ++j) {
            decl_fn();
        }
        ta = clock();
        long delta2 = ta - td;
        printf("fn : %ld\t", delta2);

        printf("ecart : %ld\n", (delta1 - delta2) / CLOCKS_PER_SEC);
    }

    return 0;
}

Résultats

Ce qui me donne ces résultats :

n°1     for : 144047    fn : 135476     ecart : 8571
n°2     for : 144516    fn : 147876     ecart : -3360
n°3     for : 150797    fn : 135666     ecart : 15131
n°4     for : 153779    fn : 144821     ecart : 8958
n°5     for : 163571    fn : 155577     ecart : 7994
n°6     for : 141100    fn : 136226     ecart : 4874
n°7     for : 141100    fn : 135872     ecart : 5228
n°8     for : 140568    fn : 134942     ecart : 5626
n°9     for : 143267    fn : 135312     ecart : 7955
n°10    for : 143175    fn : 136072     ecart : 7103

On voit que déclarer dans la fonction et pas dans le for, c’est plus rapide, sauf que j’ai compilé avec une tonne de truc de débogage : -Wall -Wextra -Wshadow -Wcast-align -Wstrict-prototypes -fanalyzer -fsanitize=undefined -g

Voilà ce que ça donne sans argument donné au compilateur :

n°1     for : 147757    fn : 149087     ecart : -1330
n°2     for : 147442    fn : 146889     ecart : 553
n°3     for : 149774    fn : 142949     ecart : 6825
n°4     for : 150022    fn : 142218     ecart : 7804
n°5     for : 151124    fn : 144103     ecart : 7021
n°6     for : 148284    fn : 142159     ecart : 6125
n°7     for : 149724    fn : 145557     ecart : 4167
n°8     for : 162975    fn : 146997     ecart : 15978
n°9     for : 150409    fn : 142262     ecart : 8147
n°10    for : 150698    fn : 141019     ecart : 9679

Le résultat est le même, maintenant, avec l’option d’optimisation O3 :

n°1     for : 157722    fn : 162595     ecart : -4873
n°2     for : 162482    fn : 160296     ecart : 2186
n°3     for : 165359    fn : 161122     ecart : 4237
n°4     for : 177995    fn : 177640     ecart : 355
n°5     for : 165326    fn : 167349     ecart : -2023
n°6     for : 172356    fn : 160947     ecart : 11409
n°7     for : 163722    fn : 156314     ecart : 7408
n°8     for : 164267    fn : 158028     ecart : 6239
n°9     for : 164323    fn : 158746     ecart : 5577
n°10    for : 162714    fn : 156948     ecart : 5766

Toujours pareil, même en demandant à GCC d’optimiser.

Alors, il y a donc bien une différence entre les deux méthodes ?

Sur ma machine, avec ma version de gcc, oui. Mais peut-être que sur votre machine non.

Les résultats que j’ai présentés ci-dessus proviennent de mon PC portable. Quand je lance les mêmes tests, mais depuis mon PC fixe, voici ce que j’obtiens :

Avec les options de débogage :

n°1     for : 147380    fn : 174594     ecart : -27214
n°2     for : 154869    fn : 176800     ecart : -21931
n°3     for : 151584    fn : 175797     ecart : -24213
n°4     for : 150282    fn : 171719     ecart : -21437
n°5     for : 147962    fn : 172099     ecart : -24137
n°6     for : 148461    fn : 172433     ecart : -23972
n°7     for : 149758    fn : 172066     ecart : -22308
n°8     for : 149648    fn : 171500     ecart : -21852
n°9     for : 147577    fn : 173268     ecart : -25691
n°10    for : 149809    fn : 172117     ecart : -22308

Sans aucune option :

n°1     for : 193780    fn : 194501     ecart : -721
n°2     for : 194958    fn : 195128     ecart : -170
n°3     for : 194917    fn : 194266     ecart : 651
n°4     for : 194126    fn : 194726     ecart : -600
n°5     for : 194265    fn : 194346     ecart : -81
n°6     for : 193568    fn : 194979     ecart : -1411
n°7     for : 193909    fn : 194284     ecart : -375
n°8     for : 194475    fn : 194152     ecart : 323
n°9     for : 195083    fn : 195664     ecart : -581
n°10    for : 195224    fn : 194832     ecart : 392

Avec l’optimisation O3 :

n°1     for : 48481     fn : 194637     ecart : -146156
n°2     for : 48991     fn : 195717     ecart : -146726
n°3     for : 49006     fn : 195501     ecart : -146495
n°4     for : 49389     fn : 195469     ecart : -146080
n°5     for : 48953     fn : 194869     ecart : -145916
n°6     for : 48721     fn : 194756     ecart : -146035
n°7     for : 49230     fn : 196268     ecart : -147038
n°8     for : 49039     fn : 195210     ecart : -146171
n°9     for : 48959     fn : 195485     ecart : -146526
n°10    for : 48750     fn : 195087     ecart : -146337

Les résultats sont… plus que différent, ils sont carrément opposés ! L’écart est énormément réduit lorsque l’on compile sans aucun argument et cette fois-ci, il est préférable d’initialiser dans le for plutôt que dans la fonction.

Conclusion

Faites ce que vous voulez, le résultat dépendra de toute façon de votre machine. Et l’écart de performance est plus que négligeable.