2017-01-14 1 views
1

J'apprends maintenant SIMD et je pense à la façon de laisser le compilateur optimiser mon code. Maintenant, je joue avec Visual C++ 2013 x86.Comment faire pour que le compilateur VC optimise mon code avec SIMD?

J'ai un tableau, j'ai un autre tableau, et je veux calculer comme ceci:

void computeSum(float* __restrict arr, float* __restrict inp1, float* __restrict inp2, int count) 
{ 
    __declspec(align(16)) float* p1 = inp1; 
    __declspec(align(16)) float* p2 = inp2; 
    __declspec(align(16)) float* ret = arr; 

    while (count > 0) 
    { 
     ret[0] = p1[0] + p2[0]; 
     ret[1] = p1[1] + p2[1]; 
     ret[2] = p1[2] + p2[2]; 
     ret[3] = p1[3] + p2[3]; 

     p1 += 4; 
     p2 += 4; 
     ret += 4; 

     count -= 4; 
    } 
} 

Je veux dire au compilateur que les tableaux sont alignés à la limite et toute personne de 16 octets est recouvre pas sur une autre, et une boucle calculera la somme de 4 nombres de flottants continus.

Mais dans le code généré, VC préfère MOVSS/ADDSS et n'utilise pas ADDPS comme je l'espère.



Si je configure le projet d'utiliser la chaîne d'outils LLVM-vs2013, il utilise ADDPS pour calculer la somme.

Je sais comment utiliser les intrinsèques du compilateur pour écrire du code SIMD, mais ce n'est pas ce que je veux.

Y a-t-il d'autres indications que VC doit utiliser l'instruction ADDPS?



Ceci est la pleine pièce de code.

#include <stdio.h> 
#include <stdlib.h> 

void computeSum(float* __restrict arr, float* __restrict inp1, float* __restrict inp2, int count) 
{ 
    __declspec(align(16)) float* p1 = inp1; 
    __declspec(align(16)) float* p2 = inp2; 
    __declspec(align(16)) float* ret = arr; 

    while (count > 0) 
    { 
     ret[0] = p1[0] + p2[0]; 
     ret[1] = p1[1] + p2[1]; 
     ret[2] = p1[2] + p2[2]; 
     ret[3] = p1[3] + p2[3]; 

     p1 += 4; 
     p2 += 4; 
     ret += 4; 

     count -= 4; 
    } 
} 

int main() 
{ 
    float* inp1 = (float*)_aligned_malloc(sizeof(float) * 128, 16); 
    float* inp2 = (float*)_aligned_malloc(sizeof(float) * 128, 16); 
    float* result = (float*)_aligned_malloc(sizeof(float) * 128, 16); 

    for (int i = 0; i < 128; ++i) 
    { 
     inp1[i] = inp2[i] = i; 
    } 

    computeSum(result, inp1, inp2, 128); 

    for (int i = 0; i < 128; ++i) 
    { 
     printf("%f\t", result[i]); 
    } 

    return 0; 
} 
+0

Avez-vous spécifié/arch: SSE (ou similaire)? – harold

+0

@harold oui, le commutateur/arch: SSE2 est activé. – Sorayuki

+0

Avez-vous essayé avec * non * le déroulement de la boucle? –

Répondre

1

Visual C++ 2013 ou tard par défaut à utiliser /arch:SSE2 pour x86, mais vous devriez toujours vérifier les paramètres de votre projet Visual Studio pour vous assurer qu'il n'a pas explicitement été mis à autre chose. Pour x64, /arch:SSE2 est implicite. La seule fois que Visual C++ génère automatiquement des instructions multi-voies (comme ADDPS) plutôt que des voies simples (ADDSS) est due à l'auto-vectorizer. Consultez MSDN pour plus de détails et accordez une attention particulière au commutateur /Qvec-report:2 - et notez que cela ne se produira pas avec les optimisations désactivées, comme c'est souvent le cas dans les configurations de débogage.

La plupart des codages SIMD (multi-voies) sont mieux réalisés avec une utilisation intrinsèque explicite. Pour beaucoup d'exemples de ce style de codage, voir DirectXMath.

+0

Il est très important pour moi de savoir qu'il s'appelle "Auto-vectorizer" et que le commutateur de paramètre permet d'activer le rapport. Mais après quelques essais, je ne peux pas encore faire utiliser l'optimiseur addps. Pourriez-vous donner un exemple de code? Je vous remercie – Sorayuki