Bonsoir,

Je rencontre actuellement des résultats étranges quant à l'utilisation des instructions SSE, via les fonctions intrinsèques de gcc.

En gros je teste les performances d'une addition vectorielle des trois manières suivantes :
- boucle classique, addition élément par élément
- addition via instructions SSE avec données alignées
- addition via instructions SSE avec données non alignées

Je mesure les perfs pour différentes tailles de vecteur.

Et là surprise : autant pour les vecteurs de float les instructions SSE sont plus rapides (avec données alignées), autant pour les vecteurs de double c'est l'inverse.

J'ai obtenu les mêmes conclusions avec les deux processeurs suivants :
Athlon 3500+
Intel CoreDuo2

D'où ma question : est-ce que le problème vient de mon test où est-ce que les instructions intrinsèques n'apportent rien quand il s'agit de double ? Quelqu'un a-t-il déjà rencontré le même problème ?


Voici le code et les résultats que j'obtiens sur l'Athlon :

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
218
219
220
221
222
 
#include <iostream>
 
#include <emmintrin.h>
#include <boost/date_time/posix_time/posix_time_types.hpp>
 
using namespace boost::posix_time;
 
namespace bench_sse
{
 
template <class T>
    void resize_array(T*& t, size_t size)
    {
        t = reinterpret_cast<T*>(_mm_malloc(sizeof(T)*size,16));
    }
 
template <class T>
    void build_array(T*& t, size_t size)
    {
        for(size_t i = 0; i < size; ++i)
        {
            t[i] = i/4.0;
        }
    }
 
template <class T>
    struct packet_traits;
 
template <>
    struct packet_traits<float>
    {
        typedef __m128 type;
        static const size_t size = 4;
    };
 
template <>
    struct packet_traits<double>
    {
        typedef __m128d type;
        static const size_t size = 2;
    };
 
template <class T>
    struct unpacket_traits;
 
template <>
    struct unpacket_traits<__m128>
    {
        typedef float type;
        static const size_t size = 4;
    };
 
template <>
    struct unpacket_traits<__m128d>
    {
        typedef double type;
        static const size_t size = 2;
    };
 
inline __m128 nx_add_p(const __m128& a, const __m128& b) { return _mm_add_ps(a,b); }
inline __m128d nx_add_p(const __m128d& a, const __m128d& b) { return _mm_add_pd(a,b); }
 
inline __m128 nx_load_p(const float* a) { return _mm_load_ps(a); }
inline __m128d nx_load_p(const double* a) { return _mm_load_pd(a); }
 
inline void nx_store_p(float* to, const __m128& from) { _mm_store_ps(to,from); }
inline void nx_store_p(double* to, const __m128d& from) { _mm_store_pd(to,from); }
 
inline __m128 nx_loadu_p(const float* a) { return _mm_loadu_ps(a); }
inline __m128d nx_loadu_p(const double* a) { return _mm_loadu_pd(a); }
 
inline void nx_storeu_p(float* to, const __m128& from) { _mm_storeu_ps(to,from); }
inline void nx_storeu_p(double* to, const __m128d& from) { _mm_storeu_pd(to,from); }
 
template <int N>
    struct sse_bench;
 
template <>
    struct sse_bench<0>
    {
        static const size_t size = 8;
        static const size_t number = 4000000;
    };
 
template <>
    struct sse_bench<1>
    {
        static const size_t size = 100;
        static const size_t number = 1000000;
    };
 
template <>
    struct sse_bench<2>
    {
        static const size_t size = 1000;
        static const size_t number = 100000;
    };
 
template <>
    struct sse_bench<3>
    {
        static const size_t size = 10000;
        static const size_t number = 10000;
    };
 
 
template <class T>
    long time_add(T*& t1, T*& t2, T*& res, size_t size, size_t number)
    {
        build_array(t1,size);
        build_array(t2,size);
        ptime pt1(microsec_clock::local_time());
        for(size_t j = 0; j < number; ++j)
        {
            for(size_t i = 0; i < size; ++i)
                res[i] = t1[i] + t2[i];
        }
        ptime pt2(microsec_clock::local_time());
        time_duration d = pt2-pt1;
        return d.total_milliseconds();
    }
 
template <class T>
    long time_add_sse(T*& t1, T*& t2, T*& res, size_t size, size_t number)
    {
        build_array(t1,size);
        build_array(t2,size);
        ptime pt1(microsec_clock::local_time());
        size_t inc = packet_traits<T>::size;
        for(size_t j = 0; j < number; ++j)
        {
            for(size_t i = 0; i < size; i += inc)
                nx_store_p(res+i, nx_add_p(nx_load_p(t1+i),
                                           nx_load_p(t2+i)
                                           )
                           );
        }
        ptime pt2(microsec_clock::local_time());
        time_duration d = pt2-pt1;
        return d.total_milliseconds();
    }
 
template <class T>
    long time_add_sse_u(T*& t1, T*& t2, T*& res,size_t size, size_t number)
    {
        build_array(t1,size);
        build_array(t2,size);
        ptime pt1(microsec_clock::local_time());
        size_t inc = packet_traits<T>::size;
        for(size_t j = 0; j < number; ++j)
        {
            for(size_t i = 0; i < size; i += inc)
                nx_storeu_p(res+i, nx_add_p(nx_loadu_p(t1+i),
                                            nx_loadu_p(t2+i)
                                           )
                           );
        }
        ptime pt2(microsec_clock::local_time());
        time_duration d = pt2-pt1;
        return d.total_milliseconds();
    }
 
 
    template <class T, int N>
        void single_bench_add()
        {
            sse_bench<N> b;
 
            T* t1, *t2, *tres;
            resize_array(t1,b.size);
            resize_array(t2,b.size);
            resize_array(tres,b.size);
 
            T* ts1, *ts2, *tsres;
            resize_array(ts1,b.size);
            resize_array(ts2,b.size);
            resize_array(tsres,b.size);
 
            T *tu1, *tu2, *tures;
            tu1 = new T[b.size];
            tu2 = new T[b.size];
            tures = new T[b.size];
 
            std::cout << "Size = " << b.size << " - Iteration = " << b.number << std::endl;
            std::cout << "Single    add : " << time_add(t1,t2,tres,b.size,b.number) << std::endl;
            std::cout << "Parallel  add : " << time_add_sse(ts1,ts2,tsres,b.size,b.number) << std::endl;
            std::cout << "Unaligned add : " << time_add_sse_u(tu1,tu2,tures,b.size,b.number) << std::endl;
            std::cout << "Single    add : " << time_add(t1,t2,tres,b.size,b.number) << std::endl;
            std::cout << "Parallel  add : " << time_add_sse(ts1,ts2,tsres,b.size,b.number) << std::endl;
            std::cout << "Unaligned add : " << time_add_sse_u(tu1,tu2,tures,b.size,b.number) << std::endl;
            std::cout << std::endl;
 
            _mm_free(t1);
            _mm_free(t2);
            _mm_free(tres);
            _mm_free(ts1);
            _mm_free(ts2);
            _mm_free(tsres);
            delete[] tu1;
            delete[] tu2,
            delete[] tures;
        }
 
    template <class T>
        void bench_add()
        {
            single_bench_add<T,0>();
            single_bench_add<T,1>();
            single_bench_add<T,2>();
            single_bench_add<T,3>();
        }
}
 
int main(int agc, char* argv[])
{
    std::cout << "FLOAT" << std::endl << std::endl;
    bench_sse::bench_add<float>();
    std::cout << "DOUBLE" << std::endl << std::endl;
    bench_sse::bench_add<double>();
    return 0;
}
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
 
FLOAT
 
Size = 8 - Iteration = 4000000
Single    add : 53
Parallel  add : 20
Unaligned add : 60
Single    add : 47
Parallel  add : 25
Unaligned add : 62
 
Size = 100 - Iteration = 1000000
Single    add : 149
Parallel  add : 58
Unaligned add : 196
Single    add : 149
Parallel  add : 57
Unaligned add : 197
 
Size = 1000 - Iteration = 100000
Single    add : 147
Parallel  add : 47
Unaligned add : 189
Single    add : 138
Parallel  add : 50
Unaligned add : 188
 
Size = 10000 - Iteration = 10000
Single    add : 199
Parallel  add : 129
Unaligned add : 218
Single    add : 198
Parallel  add : 130
Unaligned add : 213
 
DOUBLE
 
Size = 8 - Iteration = 4000000
Single    add : 48
Parallel  add : 69
Unaligned add : 117
Single    add : 52
Parallel  add : 69
Unaligned add : 118
 
Size = 100 - Iteration = 1000000
Single    add : 150
Parallel  add : 225
Unaligned add : 385
Single    add : 179
Parallel  add : 226
Unaligned add : 386
 
Size = 1000 - Iteration = 100000
Single    add : 141
Parallel  add : 217
Unaligned add : 378
Single    add : 139
Parallel  add : 215
Unaligned add : 379
 
Size = 10000 - Iteration = 10000
Single    add : 267
Parallel  add : 317
Unaligned add : 442
Single    add : 262
Parallel  add : 319
Unaligned add : 448