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).
-
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.
-
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;
}