2009-10-09 6 views
2

Je veux économiser de la mémoire en convertissant un compteur 32 bits existant en un compteur 16 bits. Ce compteur est incrémenté/décrémenté de manière atomique. Si je fais ceci:Puis-je incrémenter de manière atomique un compteur de 16 bits sur x86/x86_64?

  1. Quelles instructions dois-je utiliser pour atomic_inc (uint16_t x) sur x86/x86_64?
  2. Est-ce fiable sur les machines x86/x86_64 multi-processeurs?
  3. Existe-t-il une pénalité de performance à payer sur l'une de ces architectures pour cela?
  4. Si oui pour (3), quelle est la pénalité de performance attendue?

Merci pour vos commentaires!

+4

A moins que vous n'ayez beaucoup de compteurs (et c'est beaucoup comme dans "mégaoctets") cela semble être un gros effort pour sauver 2 octets. Quel est le problème * réel * que vous essayez de résoudre ici? –

+1

Ouais, j'ai un * lot * de ces compteurs s'élevant en mégaoctets. Chaque compteur de ce type représente des opérations en attente sur un bloc de mémoire correspondant. Quand le compteur descend à zéro, je suis censé déclencher une autre opération. – Sudhanshu

+0

Copie possible de [Peut num ++ être atomique pour 'int num'?] (Https: // stackoverflow.com/questions/39393850/can-num-être-atomique-pour-int-num) –

Répondre

4

est ici celui qui utilise des extensions d'assemblage du CCG, comme une alternative à la réponse Delphi Steve:

uint16_t atomic_inc(uint16_t volatile* ptr) 
{ 
    uint16_t value(1); 
    __asm__("lock xadd %w0, %w1" : "+r" (value) : "m" (*ptr)); 
    return ++value; 
} 

Modifier le 1 avec -1, et le ++ avec --, pour diminuer la valeur.

+0

Merci, mais je parle vraiment d'AMD64/Pentiums ici. :) – Sudhanshu

+0

C'est cool. Je vous donne toujours une alternative C au cas où vous ne voulez pas coder avec Delphi. :-) –

+0

(Eh bien, change les initialiseurs de 'uint_t value (1)' en 'uint_t value = 1' pour C (mes habitudes en C++ m'arrivent), mais ouais. :-P) –

3

Voici une fonction Delphi qui fonctionne:

function LockedInc(var Target :WORD) :WORD; 
asm 
     mov  ecx, eax 
     mov  ax, 1 
    Lock xadd [ecx], ax 
     Inc  eax 
end; 

Je suppose que vous pouvez convertir en la langue que vous avez besoin.

+0

Pour clarifier pour les utilisateurs non Delphi/BASM, j'ajouterais que dans cette routine (32 bits), un pointeur vers la cible sera passé dans EAX, et que la valeur de retour de la fonction sera en AX. – PhiS

+0

Est-ce que vous laissez volontairement la moitié haute de la valeur de retour = la moitié haute du pointeur d'entrée, ou est-ce un bug? Vous pourriez vouloir 'mov ecx, 1' (pas seulement CX) /' verrouiller xadd [eax], cx'/'lea eax, [ecx + 1]'. (Cela a un décrochage [registre partiel sur les anciens processeurs Intel (Nehalem et avant)] (https://stackoverflow.com/questions/41573502/why-doesnt-gcc-use-partial-registers) de lire 'ecx' après écrire 'cx' .Utiliser des instructions séparées de movzx + inc pour éviter cela si nécessaire.) –

-1

Pour répondre aux trois autres questions:

  1. n'a pas trouvé un moyen de faire une liste numérotée en commençant par 2
  2. Oui, cela est fiable dans un environnement multiprocesseur
  3. Oui, il y a une pénalité de performance
  4. Le préfixe "lock" verrouille les bus, non seulement pour le processeur, mais pour tout matériel externe, qui peut vouloir accéder au bus via DMA (stockage de masse, graphiques ...). Il est donc lent, typiquement ~ 100 cycles d'horloge, mais il peut être plus coûteux. Mais si vous avez "mégaoctets" de compteurs, il y a de fortes chances que vous fassiez face à un cache manquant, et dans ce cas vous devrez attendre environ 100 horloges (le temps d'accès à la mémoire), en cas de manque de mémoire, cent, de sorte que les frais généraux de verrouillage pourraient ne pas avoir d'importance.
+0

Merci pour votre réponse. C'est le plus proche de ce que je cherchais après la réponse de Chris. – Sudhanshu

+3

Je crois que le verrouillage de bus est une chose de jadis. Les processeurs de génération actuels mettent à la place le verrou de la ligne de cache à la place: cela s'associe parfaitement avec le protocole MESI ou MESI utilisé pour la cohérence du cache. – terminus

+1

Downvoted car il n'y a pas de pénalité de performance, ou peut-être un trivial même pour un seul compteur atomique. Avec un grand nombre de compteurs atomiques, la taille d'opérande 16 bits devrait être une grande victoire. Les opérations sur les octets et les mots sont supportées nativement par le matériel de cache x86, donc je ne m'attends à aucun problème si les mots sont alignés sur 16 bits. 'lock lock mot [mem]' devrait avoir des performances fondamentalement identiques à 'lock inc dword [mem]' (ignorer les échecs de cache). –

0

La façon la plus simple d'effectuer une augmentation atomique est la suivante (ce qui est en ligne ASM):

asm 
    lock inc dword ptr Counter; 
end; 

où J est un entier. Cela augmentera directement Counter dans son emplacement mémoire.

J'ai testé cela avec force brute et cela fonctionne à 100%.

+0

L'OP veut un compteur de 16 bits, mais oui cela fonctionnera avec 'word' au lieu de' dword'. BTW, la seule façon de dire quelque chose "fonctionne à 100%" basé sur le test de force brute (sans vérifier également les manuels) est si vous l'avez testé sur tous les processeurs actuels * et futurs * x86. Tout ce dont le préfixe 'lock' n'est pas défectueux est atomique garanti à 100% (je pense, au moins pour les instructions où le préfixe' lock' est documenté pour s'appliquer). Mais par exemple 'movdqa [eax], xmm0' est atomique sur certains processeurs mais pas sur d'autres, donc tester sur Core2 ne révélerait pas les problèmes sur certains Opterons multi-socket. –

Questions connexes