40 votes

Performances en boucle C 64 bits sur x86

J'ai besoin d'un Internet fonction de la somme de contrôle (complément de la somme de contrôle) pour certains IPv4 ICMP code de traitement en utilisant les sockets raw et j'ai trébuché sur le comportement, je ne peux pas l'expliquer sur un 64 bits processeur Intel (à l'aide de gcc 4.8.2). Je me demandais si quelqu'un pourrait jeter de la lumière sur elle.

J'ai mis en place une première somme de contrôle de la fonction en utilisant une version 32 bits de l'accumulateur et de l'exécution de 16 bits sommes. Ensuite, j'ai mis en œuvre la même aide d'une version 64 bits de l'accumulateur 32 bits et sommes, en pensant que les fonds seraient le résultat d'une exécution plus rapide. Le résultat est que la première mise en œuvre s'exécute deux fois plus rapide que le second (O3 optimisation). Je ne peux tout simplement pas comprendre pourquoi...

Le code ci-dessous n'est pas réellement mener des sommes de contrôle (j'ai simplifié) mais illustre bien le problème. À la fois compilé en 64 bits en cours d'exécution sur 64 bits natif de la plateforme (LP64: court de 16 bits, int de 32 bits, long de 64 bits, les pointeurs, 64 bits).

  1. 32 bits de l'accumulateur 16 bits et le sommes

    unsigned short
    cksum_16_le(unsigned char* data, size_t size)
    {
        unsigned short word;
        unsigned int sum = 0;
        unsigned int i;
    
        for(i = 0; i < size - 1; i += 2)
            sum += *((unsigned short*) (data + i));
    
        sum = (sum & 0xffff) + (sum >> 16);
        sum = (sum & 0xffff) + (sum >> 16);
    
        return ~sum;
    }
    

De 50 000 appels de fonction au cours de la même 10k de données: ~1.1 secondes.

  1. 64-bits de l'accumulateur 32 bits et sommes

    unsigned short
    cksum_32_le(unsigned char* data, size_t size)
    {
        unsigned long word;
        unsigned long sum = 0;
        unsigned int i;
    
        for(i = 0; i < size - 3; i += 4)
            sum += *((unsigned int*) (data + i));
    
        sum = (sum & 0xffffffff) + (sum >> 32);
        sum = (sum & 0xffffffff) + (sum >> 32);
        sum = (sum & 0xffff) + (sum >> 16);
        sum = (sum & 0xffff) + (sum >> 16);
    
        return ~sum;
    }
    

De 50 000 appels de fonction au cours de la même 10k de données: ~2,2 secondes.

Mise à jour:

Il semble que le problème est lié au matériel. Mémoire diags a révélé occasionnel de bus erreurs de parité (pas sûr de savoir pourquoi cela aurait une incidence sur la version 32 bits de plus que la version 16 bits, mais là vous allez). Le code s'exécute comme prévu sur d'autres serveurs. Sera la suppression de la question dans les prochaines heures (étant liés au matériel, il n'est pas particulièrement plus utile).

Dernière mise à jour:

Il est intéressant de noter, en remplacement de l' for boucles while boucles et de la compilation avec O3 optimisation (voir ci-dessous pour les 64 bits de l'accumulateur cas) est à la fois 32 bits et 64 bits de l'accumulateur cas pour fonctionner à des vitesses identiques. C'est parce que le compilateur effectue quelques déroulement de la boucle (bizarrement, il ne se déroule pas de la for boucle) et effectue la synthèse à l'aide de mmx registres...

uint64_t sum = 0;
const uint32_t* dptr = (const uint32_t*) data;

while (size > 3)
{
    sum += (uint32_t) *dptr++;
    size -= 4;
}

1voto

Richard Grant Points 5

J'ai eu un problème similaire comme ça avant, je ne pouvais pas trouver toutes les questions de notre code. MAIS ce qui a fonctionné pour moi a été de changer le compilateur.

Ma conjecture est que GCC est l'écriture s'est déprécié de l'Assemblée.

Si vous pouvez décompiler votre demande, nous pourrions jeter plus de lumière sur la question, mais il n'est tout simplement pas assez d'informations ici.

Quand j'ai Décompilé mon code que j'ai trouvé c'est que c'était la réécriture de l'ensemble de la méthode plusieurs fois. mais c'est peut-être juste pour moi.

Espérons que cela vous a aidé, il y a peu ou pas d'information à propos de ce n'importe où.

  • si je devais deviner, je suis d'accord avec l'Apprenant, je suis assez certain que le code décompilé serait point à la boucle for. Je suis très intéressé par cette question, de sorte s'il vous plaît commentaire en arrière.

0voto

Learner Points 183

Réponse Probable: "i < taille - 1" état, qui peut être compilé et exécuté de manière plus efficace que "i < taille - 3". Première, il suffit de décrémenter l'instruction à la place de l'autre qui nécessite une constante de 3 à également être chargé de quelque part. Ce calcul est exécuté à chaque itération. Vous devez stocker le résultat de ce calcul ailleurs.

Cela n'a rien à voir avec la boucle while. Lorsque vous réécrit la boucle while vous avez changé l'itération condition trop et éliminé la cause ci-dessus.

Je préfère aussi faire de la conversion de type en dehors de la boucle, mais qui révèle aussi une restriction de vos données doit

-1voto

Joël V. Points 1

Je pense qu'il ne peut pas dérouler la boucle "for" à cause de la conversion de char * en unsigned int *. Les polices de caractères empêchent souvent le compilateur d'optimiser le code, car une analyse d'alias parfaite ne peut pas être effectuée dans ce cas. Si vous déclarez tout d'abord un pointeur local supplémentaire pour convertir votre pointeur "données" avant la boucle, de sorte qu'il n'y ait pas de transfert dans la boucle, le compilateur devrait pouvoir optimiser la boucle "pour".

-3voto

EpiGen Points 28
sum += *((unsigned int*) (data + i)); 

Je n'aime pas un tel casting dans

64-bits de l'accumulateur 32 bits et sommes

depuis que vous avez écrit :

À la fois compilé en 64 bits en cours d'exécution sur 64 bits natif de la plateforme (LP64: court de 16 bits, int de 32 bits, long >64 bits, les pointeurs, 64 bits).

Je suggère d'utiliser (unsigned long*). Certaines personnes conseillé de vérifier dans le code désassemblé ce qui se passe dans la réalité. Je pense que c'est à cause de votre int* cast avec long de l'accumulateur.

Ce sujet sans O2<>O3 drapeau? Pourriez-vous montrer ce qu'est la vitesse normale en mode de compilation?

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X