2017-03-28 1 views
3

Ceci est mon implémentation naïve du produit scalaire:Pourquoi std :: inner_product est-il plus lent que l'implémentation naïve?

float simple_dot(int N, float *A, float *B) { 
    float dot = 0; 
    for(int i = 0; i < N; ++i) { 
    dot += A[i] * B[i]; 
    } 

    return dot; 
} 

Et cela est d'utiliser la bibliothèque C++:

float library_dot(int N, float *A, float *B) { 
    return std::inner_product(A, A+N, B, 0); 
} 

J'ai couru une référence (code est ici https://github.com/ijklr/sse), et la version de la bibliothèque est un beaucoup plus lent. Mon drapeau de compilateur est -Ofast -march=native

+4

Utiliser '0.0f' comme valeur initiale. –

+0

Que se passe-t-il si vous modifiez 0 à 0f dans l'appel à inner_product? – NathanOliver

+0

Vous devriez être capable de regarder l'implémentation de la bibliothèque comme un modèle. –

Répondre

7

Vos deux fonctions ne font pas la même chose. L'algorithme utilise un accumulateur dont le type est déduit de la valeur initiale, qui dans votre cas (0) est int. Accumuler des flotteurs dans un int ne prend pas simplement plus de temps que de s'accumuler dans un flotteur, mais produit également un résultat différent. L'équivalent de votre code de boucle brute est d'utiliser la valeur initiale 0.0f, ou de manière équivalente float{}.

(Notez que std::accumulate est très similaire à cet égard.)

+0

[Démo pour x86-64] (https: // godbolt.org/g/C9MmeI) –

+0

Merci. CA aide! – ijklr

+0

GCC ne déroule pas la boucle et '-funroll-loops' ne casse pas la chaîne de dépendances. Clang déroule 4 fois ce qui est génial. Donc, au moins avec GCC 'std :: inner_product' n'est pas optimal, c'est-à-dire que vous devez optimiser à la main de toute façon avec des réductions. –