IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

C++ Discussion :

aider l'optimiseur pour floor_log_2(n)


Sujet :

C++

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre très actif

    Profil pro
    Étudiant
    Inscrit en
    Décembre 2004
    Messages
    499
    Détails du profil
    Informations personnelles :
    Âge : 39
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Décembre 2004
    Messages : 499
    Par défaut aider l'optimiseur pour floor_log_2(n)
    bonjour,

    avez-vous une solution pour aider l'optimiseur à faire du "loop unwinding" ?

    J'ai code qui avec un "while" fait une recherche dichotomique du nombre de bit qu'il faut pour stocker un certain entier (donc qui calcule floor(log2(n)) ),

    et j'aimerais que l'optimiseur me produise un code assembleur dans ce style :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    private static int floorLog2(int i)    //do you see the binary tree? 5 comparisions
        {
            return
            i < 1 << 15 ? i < 1 << 07 ? i < 1 << 03 ? i < 1 << 01 ? i < 1 << 00 ? -1 : 00 :
                                                                    i < 1 << 02 ? 01 : 02 :
                                                      i < 1 << 05 ? i < 1 << 04 ? 03 : 04 :
                                                                    i < 1 << 06 ? 05 : 06 :
                                        i < 1 << 11 ? i < 1 << 09 ? i < 1 << 08 ? 07 : 08 :
                                                                    i < 1 << 10 ? 09 : 10 :
                                                      i < 1 << 13 ? i < 1 << 12 ? 11 : 12 :
                                                                    i < 1 << 14 ? 13 : 14 :
                          i < 1 << 23 ? i < 1 << 19 ? i < 1 << 17 ? i < 1 << 16 ? 15 : 16 :
                                                                    i < 1 << 18 ? 17 : 18 :
                                                      i < 1 << 21 ? i < 1 << 20 ? 19 : 20 :
                                                                    i < 1 << 22 ? 21 : 22 :
                                        i < 1 << 27 ? i < 1 << 25 ? i < 1 << 24 ? 23 : 24 :
                                                                    i < 1 << 26 ? 25 : 26 :
                                                      i < 1 << 29 ? i < 1 << 28 ? 27 : 28 :
                                                                    i < 1 << 30 ? 29 : 30;
    }
    mon code :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
     
    #include <iostream>
    template <typename T> T floor_log_2(T n) {
    	T nb_bit_for_type = sizeof(T) * 8;
    	T M = nb_bit_for_type/4;
    	T d = nb_bit_for_type/2;
    	T res = 0;
     
     
    	while(M) {
    		T tmp = 1 << d;
     
                    if (n < tmp) {
    			d -= M;
    		} else {
    		        d += M;
                    }
    	}
     
    	T tmp = 1 << d;
    	if (n >= tmp) {
    		d += 1;
    	}
     
    	return d ;
    }
     
     
    int main() {
    	int res;
    	res = floor_log_2<unsigned char>(8); // 4
    	res = floor_log_2<unsigned char>(3); // 2
    	res = floor_log_2<unsigned char>(128); // 8
    	res = floor_log_2<unsigned char>(127); // 7
    	res = floor_log_2<unsigned char>(17); // 5
    	res = floor_log_2<unsigned char>(24); // 5
    	res = floor_log_2<unsigned char>(60); // 6
     
    	res = floor_log_2<unsigned char>(1); // 1
    	res = floor_log_2<unsigned char>(0); // 1 (oui)
    	res = floor_log_2<unsigned char>(-1); // 8
     
    	return 0;
    }
    en fait je ne comprends pas bien pourquoi visual 2013 il n'arrive pas à le faire direct (je n'ai pas la dernière version de gcc si quelqu'un voudrait bien regarder ce que ça donne ?)

    et avec un template récursif (M et d comme paramètres template ?) ça peut l'aider ?

  2. #2
    Membre très actif

    Profil pro
    Étudiant
    Inscrit en
    Décembre 2004
    Messages
    499
    Détails du profil
    Informations personnelles :
    Âge : 39
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Décembre 2004
    Messages : 499
    Par défaut
    ok avec des templates ça marche nickel il a bien "unwindé" les appels récursifs, mais bon ça serait plus cool qu'il arrive à le faire sans qu'on lui prémâche le travail.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    #include <iostream>
     
     
    template <typename T, T d, T M> __inline T floor_log_2_bisT(T n) {
    	T tmp = (1 << d);
    	// int _d = d; int  _res = M; // pour voir les params
    	if (M != 0) {
    		if (n < tmp) {
    			return floor_log_2_bisT<T,d - M, M / 2>(n);
    		} else {
    			return floor_log_2_bisT<T, d + M, M / 2>(n);
    		}
    	} else {
    		if (n < tmp) {
    			return d;
    		} else {
    			return d + 1;
    		}
    	}
    }
     
     
    template <typename T> T _floor_log_2(T n) {
    	T res = floor_log_2_bisT<T,sizeof(T)* 4, sizeof(T)* 2>(n);
    	return res;
    }
    int global;
    unsigned int floor_log_2(unsigned int n) {
            global++; // pour mettre un breakpoint ici en release
    	return _floor_log_2<unsigned int>(n);
    }
     
    int main()
    {
    	int res;
     
    	res = floor_log_2(127); std::cout << res << "\n";
    	res = floor_log_2(128); std::cout << res << "\n";
    	res = floor_log_2(-1); std::cout << res << "\n";
    	res = floor_log_2((1 << 13) + (1 << 15) + 27); std::cout << res << "\n";
    	res = floor_log_2(3); std::cout << res << "\n";
    	res = floor_log_2(1); std::cout << res << "\n";
    	res = floor_log_2(0); std::cout << res << "\n";
    	return 0;
    }

  3. #3
    Membre Expert

    Inscrit en
    Mai 2008
    Messages
    1 014
    Détails du profil
    Informations forums :
    Inscription : Mai 2008
    Messages : 1 014
    Par défaut
    Bonjour,

    Citation Envoyé par acx01b
    en fait je ne comprends pas bien pourquoi visual 2013 il n'arrive pas à le faire direct"
    C'est quand même loin d'être trivial comme optimisation... l'utilisation des template pour forcer la génération complète de l'arbre binaire me parait être une très bonne idée.

    Cependant si tu recherches des perf maximales je pense que le mieux reste quand même d'utiliser l'intrinsèque _BitScanReverse qui renvoie justement la position du premier bit à 1 (pour gcc voir __builtin_ctz pour une fonction assez similaire), avec ça tu auras l'assurance que la recherche du bit le plus significatif se fera en une seule instruction, "bsr" sur x86.

    J'ai fait un programme de bench :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
     
    #include <chrono>
    #include <random>
    #include <iostream>
    #include <iomanip>
    #include <vector>
    #include <string>
    #include <intrin.h>
    #pragma intrinsic(_BitScanReverse)
     
    int prevent_optimization = 0;
     
    template <typename T, T d, T M> __forceinline T floor_log_2_bisT(T n)
    {
        T tmp = (1 << d);
        // int _d = d; int  _res = M; // pour voir les params
        if (M != 0) {
            if (n < tmp) {
                return floor_log_2_bisT<T, d - M, M / 2>(n);
            }
            else {
                return floor_log_2_bisT<T, d + M, M / 2>(n);
            }
        }
        else {
            if (n < tmp) {
                return d;
            }
            else {
                return d + 1;
            }
        }
    }
     
     
    template <typename T> T _floor_log_2(T n) {
        T res = floor_log_2_bisT<T, sizeof(T)* 4, sizeof(T)* 2>(n);
        return res;
    }
     
     
    unsigned int floor_log_2_binary_unroll(unsigned int n) {
        return _floor_log_2<unsigned int>(n);
    }
     
     
    int floor_log_2_binary_naive(unsigned int n) {
        int nb_bit_for_type = sizeof(int)* 8;
        int M = nb_bit_for_type / 4;
        int d = nb_bit_for_type / 2;
        int res = 0;
     
     
        while (M) {
            int tmp = 1 << d;
     
            if (n < tmp) {
                d -= M;
            }
            else {
                d += M;
            }
            M = M / 2;
        }
     
        int tmp = 1 << d;
        if (n >= tmp) {
            d += 1;
        }
     
        return d;
    }
     
    int floor_log_2_naive(unsigned int t)
    {
        int r = 1;
        while (t >>= 1)
        {
            r++;
        }
        return r;
    }
     
     
    int floor_log_2_intrinsic(unsigned int t)
    {
        if (t == 0)
            return 1;
     
        unsigned long r;
        _BitScanReverse(&r, t);
        return r + 1;
    }
     
     
    int floor_log_2_intrinsic_noz(unsigned int t)
    {
        // give random result if t == 0
        unsigned long r = 0;
        _BitScanReverse(&r, t);
        return r + 1;
    }
     
     
    template <class Rep, class Period>
    int to_ms(const std::chrono::duration<Rep, Period>& d)
    {
        return std::chrono::duration_cast<std::chrono::milliseconds>(d).count();
    }
     
    template <typename Func>
    void Do(const std::string& desc, const std::vector<int>& v, Func f)
    {
        std::chrono::system_clock clock;
        int res = 0;
     
        auto start = clock.now();
        for (int i : v)
        {
            res += f(i);
        }
        auto end = clock.now();
        prevent_optimization += res;
        std::cout << std::left << std::setw(25) << desc;
        std::cout << " : " << to_ms(end - start) << " ms" << std::endl;
    }
     
    int pick_a_number(int min, int max)
    {
        static std::default_random_engine rng;
        std::uniform_int_distribution<> d(min, max);
        return d(rng);
    }
     
    void test(int nbint, int min, int max)
    {
        std::cout << min << " " << max << std::endl;
        std::vector<int> v;
     
        for (int i = 0; i < nbint; i++)
        {
            v.push_back(pick_a_number(min, max));
        }
     
        Do("floor_log_2_naive", v, &floor_log_2_naive);
        Do("floor_log_2_binary_naive", v, &floor_log_2_binary_naive);
        Do("floor_log_2_binary_unroll", v, &floor_log_2_binary_unroll);
        Do("floor_log_2_intrinsic", v, &floor_log_2_intrinsic);
        Do("floor_log_2_intrinsic_noz", v, &floor_log_2_intrinsic_noz);
        std::cout << std::endl;
    }
     
    int main()
    {
        int nbitem = 100000000;
     
        for (int i = 0; i < 31; i++)
            test(nbitem, 0, 1 << i);
     
        std::cout << prevent_optimization;
     
        return 0;
    }
    Et voici les résultats :
    Il est intéressant de constater que la méthode dichotomique, même avec l'unroll, n'apporte pas un gain si puissant que ça. Au maximum, sur les grands nombres on gagne x4, par contre sur les petits nombres c'est du x1, pas de gain.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
     
    0 1
    floor_log_2_naive         : 77 ms
    floor_log_2_binary_naive  : 667 ms
    floor_log_2_binary_unroll : 244 ms
    floor_log_2_intrinsic     : 417 ms
    floor_log_2_intrinsic_noz : 96 ms
     
    0 2
    floor_log_2_naive         : 334 ms
    floor_log_2_binary_naive  : 918 ms
    floor_log_2_binary_unroll : 244 ms
    floor_log_2_intrinsic     : 344 ms
    floor_log_2_intrinsic_noz : 95 ms
     
    0 4
    floor_log_2_naive         : 497 ms
    floor_log_2_binary_naive  : 1062 ms
    floor_log_2_binary_unroll : 369 ms
    floor_log_2_intrinsic     : 255 ms
    floor_log_2_intrinsic_noz : 96 ms
     
    0 8
    floor_log_2_naive         : 488 ms
    floor_log_2_binary_naive  : 1158 ms
    floor_log_2_binary_unroll : 550 ms
    floor_log_2_intrinsic     : 192 ms
    floor_log_2_intrinsic_noz : 96 ms
     
    0 16
    floor_log_2_naive         : 507 ms
    floor_log_2_binary_naive  : 1086 ms
    floor_log_2_binary_unroll : 479 ms
    floor_log_2_intrinsic     : 152 ms
    floor_log_2_intrinsic_noz : 95 ms
     
    0 32
    floor_log_2_naive         : 541 ms
    floor_log_2_binary_naive  : 1164 ms
    floor_log_2_binary_unroll : 618 ms
    floor_log_2_intrinsic     : 125 ms
    floor_log_2_intrinsic_noz : 95 ms
     
    0 64
    floor_log_2_naive         : 580 ms
    floor_log_2_binary_naive  : 1101 ms
    floor_log_2_binary_unroll : 478 ms
    floor_log_2_intrinsic     : 110 ms
    floor_log_2_intrinsic_noz : 95 ms
     
    0 128
    floor_log_2_naive         : 626 ms
    floor_log_2_binary_naive  : 1118 ms
    floor_log_2_binary_unroll : 600 ms
    floor_log_2_intrinsic     : 101 ms
    floor_log_2_intrinsic_noz : 97 ms
     
    0 256
    floor_log_2_naive         : 679 ms
    floor_log_2_binary_naive  : 1049 ms
    floor_log_2_binary_unroll : 486 ms
    floor_log_2_intrinsic     : 97 ms
    floor_log_2_intrinsic_noz : 95 ms
     
    0 512
    floor_log_2_naive         : 729 ms
    floor_log_2_binary_naive  : 1145 ms
    floor_log_2_binary_unroll : 632 ms
    floor_log_2_intrinsic     : 94 ms
    floor_log_2_intrinsic_noz : 95 ms
     
    0 1024
    floor_log_2_naive         : 818 ms
    floor_log_2_binary_naive  : 1105 ms
    floor_log_2_binary_unroll : 490 ms
    floor_log_2_intrinsic     : 93 ms
    floor_log_2_intrinsic_noz : 96 ms
     
    0 2048
    floor_log_2_naive         : 877 ms
    floor_log_2_binary_naive  : 1122 ms
    floor_log_2_binary_unroll : 652 ms
    floor_log_2_intrinsic     : 93 ms
    floor_log_2_intrinsic_noz : 96 ms
     
    0 4096
    floor_log_2_naive         : 937 ms
    floor_log_2_binary_naive  : 1067 ms
    floor_log_2_binary_unroll : 534 ms
    floor_log_2_intrinsic     : 91 ms
    floor_log_2_intrinsic_noz : 96 ms
     
    0 8192
    floor_log_2_naive         : 1000 ms
    floor_log_2_binary_naive  : 1121 ms
    floor_log_2_binary_unroll : 674 ms
    floor_log_2_intrinsic     : 93 ms
    floor_log_2_intrinsic_noz : 96 ms
     
    0 16384
    floor_log_2_naive         : 1110 ms
    floor_log_2_binary_naive  : 1093 ms
    floor_log_2_binary_unroll : 539 ms
    floor_log_2_intrinsic     : 92 ms
    floor_log_2_intrinsic_noz : 95 ms
     
    0 32768
    floor_log_2_naive         : 1114 ms
    floor_log_2_binary_naive  : 1093 ms
    floor_log_2_binary_unroll : 691 ms
    floor_log_2_intrinsic     : 92 ms
    floor_log_2_intrinsic_noz : 96 ms
     
    0 65536
    floor_log_2_naive         : 1190 ms
    floor_log_2_binary_naive  : 1047 ms
    floor_log_2_binary_unroll : 584 ms
    floor_log_2_intrinsic     : 93 ms
    floor_log_2_intrinsic_noz : 96 ms
     
    0 131072
    floor_log_2_naive         : 1217 ms
    floor_log_2_binary_naive  : 1166 ms
    floor_log_2_binary_unroll : 691 ms
    floor_log_2_intrinsic     : 94 ms
    floor_log_2_intrinsic_noz : 95 ms
     
    0 262144
    floor_log_2_naive         : 1281 ms
    floor_log_2_binary_naive  : 1100 ms
    floor_log_2_binary_unroll : 498 ms
    floor_log_2_intrinsic     : 93 ms
    floor_log_2_intrinsic_noz : 95 ms
     
    0 524288
    floor_log_2_naive         : 1352 ms
    floor_log_2_binary_naive  : 1104 ms
    floor_log_2_binary_unroll : 626 ms
    floor_log_2_intrinsic     : 93 ms
    floor_log_2_intrinsic_noz : 95 ms
     
    0 1048576
    floor_log_2_naive         : 1383 ms
    floor_log_2_binary_naive  : 1055 ms
    floor_log_2_binary_unroll : 492 ms
    floor_log_2_intrinsic     : 94 ms
    floor_log_2_intrinsic_noz : 96 ms
     
    0 2097152
    floor_log_2_naive         : 1440 ms
    floor_log_2_binary_naive  : 1135 ms
    floor_log_2_binary_unroll : 631 ms
    floor_log_2_intrinsic     : 94 ms
    floor_log_2_intrinsic_noz : 95 ms
     
    0 4194304
    floor_log_2_naive         : 1522 ms
    floor_log_2_binary_naive  : 1075 ms
    floor_log_2_binary_unroll : 490 ms
    floor_log_2_intrinsic     : 94 ms
    floor_log_2_intrinsic_noz : 95 ms
     
    0 8388608
    floor_log_2_naive         : 1568 ms
    floor_log_2_binary_naive  : 1072 ms
    floor_log_2_binary_unroll : 621 ms
    floor_log_2_intrinsic     : 94 ms
    floor_log_2_intrinsic_noz : 95 ms
     
    0 16777216
    floor_log_2_naive         : 1615 ms
    floor_log_2_binary_naive  : 1025 ms
    floor_log_2_binary_unroll : 508 ms
    floor_log_2_intrinsic     : 94 ms
    floor_log_2_intrinsic_noz : 96 ms
     
    0 33554432
    floor_log_2_naive         : 1651 ms
    floor_log_2_binary_naive  : 1139 ms
    floor_log_2_binary_unroll : 637 ms
    floor_log_2_intrinsic     : 94 ms
    floor_log_2_intrinsic_noz : 96 ms
     
    0 67108864
    floor_log_2_naive         : 1705 ms
    floor_log_2_binary_naive  : 1097 ms
    floor_log_2_binary_unroll : 494 ms
    floor_log_2_intrinsic     : 95 ms
    floor_log_2_intrinsic_noz : 95 ms
     
    0 134217728
    floor_log_2_naive         : 1777 ms
    floor_log_2_binary_naive  : 1092 ms
    floor_log_2_binary_unroll : 651 ms
    floor_log_2_intrinsic     : 94 ms
    floor_log_2_intrinsic_noz : 96 ms
     
    0 268435456
    floor_log_2_naive         : 1858 ms
    floor_log_2_binary_naive  : 1070 ms
    floor_log_2_binary_unroll : 535 ms
    floor_log_2_intrinsic     : 94 ms
    floor_log_2_intrinsic_noz : 96 ms
     
    0 536870912
    floor_log_2_naive         : 1913 ms
    floor_log_2_binary_naive  : 1116 ms
    floor_log_2_binary_unroll : 673 ms
    floor_log_2_intrinsic     : 93 ms
    floor_log_2_intrinsic_noz : 96 ms
     
    0 1073741824
    floor_log_2_naive         : 1989 ms
    floor_log_2_binary_naive  : 1072 ms
    floor_log_2_binary_unroll : 536 ms
    floor_log_2_intrinsic     : 94 ms
    floor_log_2_intrinsic_noz : 96 ms

  4. #4
    Membre très actif

    Profil pro
    Étudiant
    Inscrit en
    Décembre 2004
    Messages
    499
    Détails du profil
    Informations personnelles :
    Âge : 39
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Décembre 2004
    Messages : 499
    Par défaut
    salut,

    Merci ! Je ne connaissais pas l'existence de ces built-in dans visual (et gcc) !

    http://msdn.microsoft.com/fr-fr/libr...(v=vs.90).aspx

    J'ai regardé _BitScanReverse qui effectivement ne fait que l'instruction assembleur bsr.
    Pourtant partout dans la doc de bsr il disent que le résultat est 2^(floor(log_2(n))+1) et non pas floor(log_2(n))+1 (quand on trouve un bit à 1 dans src, le bit correspondant est mis à 1 dans dst).
    La doc de _BitScanReverse elle est correcte.

    Sinon je ne sais pas trop pourquoi ce serait une optimisation non-triviale : à voir.

  5. #5
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 644
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par défaut
    Salut,

    Par habitude, je me méfies comme de la peste des instructions qui commencent par _ ou par __ : ce sont, typiquement, des instructions propres à une implémentation donnée (microsoft, ou gcc en l'occurrence), qui risquent soit de rendre le code non portable, soit de nous obliger à jouer avec un maximume de directives préprocesseurs
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
     #ifdef WIN32 #elif defined(GCC)
    (ou similaires, bien sur
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  6. #6
    Membre très actif

    Profil pro
    Étudiant
    Inscrit en
    Décembre 2004
    Messages
    499
    Détails du profil
    Informations personnelles :
    Âge : 39
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Décembre 2004
    Messages : 499
    Par défaut
    à mon avis ce type de boucle est triviale à déplier pour l'optimiseur :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    int uneFonction(int n) {
    	const int M = une constante;
            int res = 0;
    	while(M) {
                    k opérations simples sur (n,M,res) où k est une constante
    		M /= 2;
    	}
    	return res;
    }
    c'est équivalent avec M /= cst où cst > 1 à la place de M/=2 (par contre si cst = 1.0001 la taille du code déplié peut devenir beaucoup trop importante et être plus lent à cause du cache donc c'est à l'optimiseur d'estimer ce qui est acceptable en taille et le plus rapide).

    Je vais faire quelques tests pour voir si visual et gcc trouvent ça trivial ou non.

  7. #7
    Membre Expert
    Homme Profil pro
    Étudiant
    Inscrit en
    Juin 2012
    Messages
    1 711
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Juin 2012
    Messages : 1 711
    Par défaut
    Citation Envoyé par Arzar Voir le message
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    0 1
    floor_log_2_intrinsic     : 417 ms
    floor_log_2_intrinsic_noz : 96 ms
    Ça coûte tellement cher les branch mispredict :/

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    int floor_log_2_intrinsic(unsigned int t)
    {
        unsigned long r;
        _BitScanReverse(&r, t);
        if(t == 0) {
            r = 0; // cmovz normalement ici
        }
        return r + 1;
    }
    Peut être qu'avec ça le compilo virera la branche pour mettre un cmovz.

  8. #8
    Membre très actif

    Profil pro
    Étudiant
    Inscrit en
    Décembre 2004
    Messages
    499
    Détails du profil
    Informations personnelles :
    Âge : 39
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Décembre 2004
    Messages : 499
    Par défaut
    salut, comment tu expliques ces perfs juste pour faire un x4 pour t = 0 ou 1
    x2.5 pour t <= 4
    x1 pour t <= 2^16 (le if n'est plus perceptible au niveau perf)


    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    int floor_log_2_intrinsic(unsigned int t) { // 0 <= t < 4
        /** si on décommente ça change les perfs dans certain cas
        if (t == 0) {
            return 1;
        } **/
     
        unsigned long r;
        _BitScanReverse(&r, t);
        return r + 1;
    }
    une hypothèse "plausible" :

    tu ne crois pas que c'est juste que pour t très petit le proc arrive à faire 5 instructions en même temps dont le _BitScanReverse,
    alors que pour t très grand il n'arrive pas à gérer le _BitScanReverse en parallèle et fait les instructions séquentiellement ?

    donc si on met le if c'est négligeable sauf dans le cas où il a un résultat précablé pour _BitScanReverse (genre t < 8 ?) qui lui permet d'utiliser ses autres circuits pour faire le prologue/épilogue de la fonction tant qu'il n'y a pas de if au milieu ?

  9. #9
    Membre Expert
    Homme Profil pro
    Étudiant
    Inscrit en
    Juin 2012
    Messages
    1 711
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Juin 2012
    Messages : 1 711
    Par défaut
    Citation Envoyé par acx01b Voir le message
    salut, comment tu expliques ces perfs juste pour faire un
    C'est les branch mispredicts : mauvaise prédiction = besoin de vider le pipeline et de recharger les bonnes instructions, et ça, c'est très coûteux.
    Les nombres sont aléatoires, donc les branches impossibles à prédire. Pour t < 2, t == 0 sera vrai une fois sur 2 (mais sans suivre de pattern reconnaissable), et globalement le cpu se trompera en permanence, d'où le surcoût.

    Pour t grand, on à t == 0 (quasiment) toujours faux, c'est facile à prédire et le cpu ne se trompera que très rarement.

Discussions similaires

  1. Aider moi svp pour mon pfe
    Par sillimi18 dans le forum Servlets/JSP
    Réponses: 0
    Dernier message: 19/05/2013, 17h15
  2. Quel optimiseur pour ce problème ?
    Par Ceubex dans le forum EDI et Outils pour Java
    Réponses: 1
    Dernier message: 30/05/2011, 18h26
  3. Optimiseurs pour modélisation
    Par Ceubex dans le forum API standards et tierces
    Réponses: 0
    Dernier message: 05/06/2010, 16h54
  4. Réponses: 6
    Dernier message: 27/06/2006, 18h54
  5. Aider moi pour sql server et delphi
    Par aqs dans le forum Bases de données
    Réponses: 6
    Dernier message: 11/06/2005, 21h16

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo