Erreurs, bugs, questions - page 2505

 
Vict:

En général, je ne m'y attendais même pas :

Le code est un peu trop compliqué - j'ai essayé d'atteindre l'élément qui ne rentre pas dans la ligne de cache et de marteler directement dessus, mais cela a échoué (j'aurais probablement pu le faire si j'avais vraiment voulu, mais je me suis ennuyé), et je n'ai pas trop modifié le code. Mais de cette façon, c'est encore plus impressionnant - un seul des 16 effondrements est effectué sur un élément qui ne rentre pas dans la ligne de cache, mais le résultat est néanmoins notable.

SZY : Il est plus objectif dans ce cas de faire RIGHT_ALIGNED par l'insertion de deux courts, et non par la suppression d'un seul (nous réaliserons donc deux mises à jour de la ligne de cache pour les deux cas). L'accélération sera plus modeste, mais toujours environ 1,5 fois plus importante.

Excusez-moi, mais où est l'utilité de l'alignement ? Ce n'est pas le sujet de cet exemple.

П. С. Poster votre code sans commentaires et sous une forme grossière est un manque de respect envers vos amis.

 

Correctement noté, un alignement a été ajouté pour utiliser les objets de structure MQL dans des bibliothèques tierces, en particulier dotnet.

C'est lorsque les bibliothèques dotnet ont été ajoutées pour supporter dotnet que l'alignement a été ajouté aux champs des structures/classes de packs.

Pour faire court et simple, cela fonctionne comme suit :

Pour chaque type (char, short, int, ...), il existe un alignement par défaut (1, 2, 4 octets respectivement).
Pour le champ de la structure, le minimum de deux alignements est choisi : celui par défaut et celui défini par l'utilisateur (par le biais du pack).

Dans le même temps, la taille de l'objet emballé est définie de manière à ce que l'adressage du champ de l'objet dans le tableau soit toujours "correct" (par défaut, un octet est défini avec l'emballage).
C'est à cause de cette dernière que l'on a la fausse impression que le pack aligne la taille de la structure; ce n'est pas vrai, les adresses des champs sont alignées, ce qui entraîne l'alignement de la taille de la structure.



Par exemple

struct A pack(8)
  {
   double d;
   char   c;
  };

void OnStart()
  {
   Print(sizeof(A));
   
  }

Résultat 16, de sorte que l'adressage au premier champ d est toujours aligné sur 8 octets.

 
fxsaber:

Les essais effectués par moi-même n'ont pas montré de différence notable.

J'ai amélioré l'idée originale (dans le premier code, les adresses étaient mal comptées). Si vous le voulez bien, il sera intéressant de voir le résultat pour vous.

#define  WRONG_ALIGNED
#define  CACHE_LINE_SIZE 64

struct Data {
#ifdef  WRONG_ALIGNED
   ushort pad;
#else
   uint pad;
#endif
   uint ar[CACHE_LINE_SIZE/sizeof(int)+1];
};

#import "msvcrt.dll"
  long memcpy(uint &, uint &, long);
#import
#define  getaddr(x) memcpy(x, x, 0)

void OnStart()
{
   Data data[32768];
   ZeroMemory(data);
   
   srand(GetTickCount());
   
   ulong start_time = GetMicrosecondCount();
   
   for(unsigned i = 0; i < 10000; ++ i) {
      int rndnum = rand();
      while (++rndnum < 32768) {
         int index = int(CACHE_LINE_SIZE - getaddr(data[rndnum].ar[0]) % CACHE_LINE_SIZE) / sizeof(int);
         ++ data[rndnum].ar[index];
         ++ data[rndnum].pad;
      }
   }
      
   Alert(GetMicrosecondCount() - start_time);
   
   Print(data[100].ar[0]);
   Print(data[100].pad);
}
/*
WRONG_ALIGNED:
6206397
6185472

RIGHT_ALIGNED
4089827
4003213
*/
En substance, la même chose se produit avec/sans WRONG_ALIGNED - à chaque fois que nous écrivons dans deux lignes de cache adjacentes (l'écriture dans le pad se fait toujours à l'adresse correcte), la seule différence est qu'avec WRONG_ALIGNED il y a des cas (pas toujours) où l'une des entrées dans ar se trouve dans uint, qui ne sera pas complètement dans la ligne de cache, j'ai une différence stable d'environ 1,5 fois.
 
Vict:

J'ai mis au point l'idée originale (dans le premier code, je n'ai pas compté les adresses correctement). Si vous le voulez bien, il sera intéressant de voir le résultat dans votre cas.

En gros, la même chose se passe avec/sans WRONG_ALIGNED - à chaque fois que nous écrivons dans deux lignes de cache adjacentes (l'entrée pad est toujours à l'adresse correcte), la seule différence est qu'avec WRONG_ALIGNED il y a des cas (pas toujours) où l'une des entrées dans ar se trouve dans uint, ce qui ne touchera pas la ligne de cache entière, j'ai une différence stable d'environ 1,5 fois.

Expliquez-moi, qu'essayez-vous d'obtenir avec cette phrase ? Dans l'exemple précédent, il s'agissait d'un code de mauvaise qualité.

int index = int(CACHE_LINE_SIZE - getaddr(data[rndnum].ar[0]) % CACHE_LINE_SIZE) / sizeof(int);
 
Francuz:

Veuillez expliquer ce que vous essayez d'obtenir avec cette ligne ? Dans l'exemple précédent, il s'agissait d'un code de mauvaise qualité.

Trouver notre position dans la ligne de cache actuelle (celle où se trouve le tampon) et prendre l'index pour ar[], que l'élément avec lui est dans la ligne de cache suivante (peut-être l'élément est dans deux lignes de cache avec WRONG_ALIGNED)

 
Vict:

Trouver notre position dans la ligne de cache actuelle (celle où se trouve pad) et prendre un index pour ar[] tel que l'élément qui le contient soit dans la ligne de cache suivante (peut-être que l'élément est dans deux lignes de cache à WRONG_ALIGNED)

Maintenant, nous parlons d'un décalage. Mais ce que vous montrez est un exemple purement synthétique, qui ne se produira jamais dans la vie réelle. Et sur des exemples réels, le gain de vitesse sera d'environ 1% au mieux. Vous ne devriez pas en faire tout un plat pour une accélération aussi dérisoire.

П. С. De plus, vous avez mal calculé la taille du registre.
 
Francuz:

Maintenant, nous parlons de déplacement. Mais ce que vous montrez est un exemple purement synthétique qui ne sera jamais vu dans la vie réelle. Dans des exemples concrets, le gain de vitesse est d'environ 1% au mieux. Tu ne devrais pas faire une grosse affaire d'une accélération aussi dérisoire.

Non, c'est un exemple tout à fait réel. Seuls 25% des écritures se produisent dans des endroits "problématiques" à l'interface des lignes de cache. C'est combien ? Par exemple, si vous avez un long tableau de doubles, une ligne de cache ne contient que 4 valeurs et si vous ne vous préoccupez pas de l'alignement (et que le compilateur ne le fait pas pour vous), vous aurez 25% de doubles places problématiques - comme c'est le cas dans mon exemple. Il y a beaucoup d'autres nuances qui parlent d'alignement, mais je n'y reviendrai pas - je ne suis pas très versé dans le domaine.

Eh bien, le maître de maison.

P.S. Aussi, vous avez mal calculé la taille du registre.
Je ne l'ai pas vraiment compté du tout ;))
 
Vict:

Non, c'est un exemple parfaitement réaliste. Dans celui-ci, seulement 25 % des entrées se produisent à des emplacements "problématiques" à la jonction de la ligne de cache. C'est beaucoup ? Par exemple, si vous avez un long tableau de doubles, une ligne de cache ne contient que 4 valeurs, et si vous ne vous souciez pas de l'alignement (et que le compilateur ne le fait pas pour vous), alors vous obtenez 25% du double problématique - comme dans mon exemple. Il y a beaucoup plus de nuances, qui parlent d'alignement, mais je n'en parlerai pas - je ne suis pas assez versé dans la question.

Eh bien, vous êtes le patron.

Une fois de plus, je dis que la taille du registre vous perturbe.

 
Francuz:

Encore une fois, vous confondez la taille du registre.

Justifier
 
Vict:
Justifier

Les registres sont mesurés en bits, et non en octets. Cette ligne est donc utilisée de manière incorrecte dans le reste du code :

#define  CACHE_LINE_SIZE 64