Performances de mémoire lentes (et inhabituelles) inattendues et inexpliquées avec Xeon Skylake SMP

27

Nous avons testé un serveur utilisant 2 processeurs Xeon Gold 6154 avec une carte mère Supermicro X11DPH-I et 96 Go de RAM, et nous avons constaté des problèmes de performances très étranges entourant la mémoire par rapport à un seul processeur (un socket vide), double similaire CPU Haswell Xeon E5-2687Wv3 (pour cette série de tests, mais d'autres Broadwell fonctionnent de manière similaire), Broadwell-E i7s et Skylake-X i9s (à titre de comparaison).

On s'attendrait à ce que les processeurs Skylake Xeon avec une mémoire plus rapide fonctionnent plus rapidement que le Haswell en ce qui concerne diverses fonctions memcpy et même l'allocation de mémoire (non couverte dans les tests ci-dessous, car nous avons trouvé une solution de contournement), mais à la place avec les deux processeurs installés , les Skylake Xeons fonctionnent à presque la moitié de la vitesse des Haswell Xeons, et encore moins par rapport à un i7-6800k. Ce qui est encore plus étrange, c'est lors de l'utilisation de Windows VirtualAllocExNuma pour attribuer le nœud NUMA à l'allocation de mémoire, tandis que les fonctions de copie de mémoire ordinaire fonctionnent normalement sur le nœud distant par rapport au nœud local, les fonctions de copie de mémoire utilisant les registres SSE, MMX et AVX fonctionnent beaucoup plus rapide sur le nœud NUMA distant que sur le nœud local (quoi?). Comme indiqué ci-dessus, avec Skylake Xeons,

Je ne sais pas s'il s'agit d'un bogue sur la carte mère ou le processeur, ou avec UPI vs QPI, ou rien de ce qui précède, mais aucune combinaison de paramètres du BIOS ne semble en profiter. La désactivation de NUMA (non incluse dans les résultats des tests) dans le BIOS améliore les performances de toutes les fonctions de copie à l'aide des registres SSE, MMX et AVX, mais toutes les autres fonctions de copie en mémoire ordinaire subissent également de grandes pertes.

Pour notre programme de test, nous avons testé à la fois en utilisant les fonctions d'assemblage en ligne et _mmintrinsèque, nous avons utilisé Windows 10 avec Visual Studio 2017 pour tout sauf les fonctions d'assemblage, qui en tant que msvc ++ ne compileront pas asm pour x64, nous avons utilisé gcc de mingw / msys pour compiler un fichier obj en utilisant des -c -O2drapeaux, que nous avons inclus dans l'éditeur de liens msvc ++.

Si le système utilise des nœuds NUMA, nous testons les deux nouveaux opérateurs pour l'allocation de mémoire avec VirtualAllocExNuma pour chaque nœud NUMA et faisons une moyenne cumulée de 100 copies de mémoire tampon de 16 Mo chacune pour chaque fonction de copie de mémoire, et nous faisons pivoter l'allocation de mémoire sur laquelle nous nous trouvons. entre chaque série de tests.

Les 100 tampons source et 100 tampons de destination sont alignés sur 64 octets (pour la compatibilité jusqu'à AVX512 à l'aide des fonctions de streaming) et initialisés une fois pour incrémenter les données pour les tampons source et 0xff pour les tampons de destination.

Le nombre de copies en moyenne sur chaque machine avec chaque configuration variait, car il était beaucoup plus rapide sur certains, et beaucoup plus lent sur d'autres.

Les résultats sont les suivants:

Haswell Xeon E5-2687Wv3 1 CPU (1 socket vide) sur Supermicro X10DAi avec 32 Go DDR4-2400 (10c / 20t, 25 Mo de cache L3). Mais rappelez-vous, le benchmark tourne à travers 100 paires de tampons de 16 Mo, donc nous n'obtenons probablement pas de cache L3.

---------------------------------------------------------------------------
Averaging 7000 copies of 16MB of data per function for operator new
---------------------------------------------------------------------------
std::memcpy                      averaging 2264.48 microseconds
asm_memcpy (asm)                 averaging 2322.71 microseconds
sse_memcpy (intrinsic)           averaging 1569.67 microseconds
sse_memcpy (asm)                 averaging 1589.31 microseconds
sse2_memcpy (intrinsic)          averaging 1561.19 microseconds
sse2_memcpy (asm)                averaging 1664.18 microseconds
mmx_memcpy (asm)                 averaging 2497.73 microseconds
mmx2_memcpy (asm)                averaging 1626.68 microseconds
avx_memcpy (intrinsic)           averaging 1625.12 microseconds
avx_memcpy (asm)                 averaging 1592.58 microseconds
avx512_memcpy (intrinsic)        unsupported on this CPU
rep movsb (asm)                  averaging 2260.6 microseconds

Haswell Dual Xeon E5-2687Wv3 2 processeurs sur Supermicro X10DAi avec 64 Go de RAM

---------------------------------------------------------------------------
Averaging 6900 copies of 16MB of data per function for VirtualAllocExNuma to NUMA node 0(local)
---------------------------------------------------------------------------
std::memcpy                      averaging 3179.8 microseconds
asm_memcpy (asm)                 averaging 3177.15 microseconds
sse_memcpy (intrinsic)           averaging 1633.87 microseconds
sse_memcpy (asm)                 averaging 1663.8 microseconds
sse2_memcpy (intrinsic)          averaging 1620.86 microseconds
sse2_memcpy (asm)                averaging 1727.36 microseconds
mmx_memcpy (asm)                 averaging 2623.07 microseconds
mmx2_memcpy (asm)                averaging 1691.1 microseconds
avx_memcpy (intrinsic)           averaging 1704.33 microseconds
avx_memcpy (asm)                 averaging 1692.69 microseconds
avx512_memcpy (intrinsic)        unsupported on this CPU
rep movsb (asm)                  averaging 3185.84 microseconds
---------------------------------------------------------------------------
Averaging 6900 copies of 16MB of data per function for VirtualAllocExNuma to NUMA node 1
---------------------------------------------------------------------------
std::memcpy                      averaging 3992.46 microseconds
asm_memcpy (asm)                 averaging 4039.11 microseconds
sse_memcpy (intrinsic)           averaging 3174.69 microseconds
sse_memcpy (asm)                 averaging 3129.18 microseconds
sse2_memcpy (intrinsic)          averaging 3161.9 microseconds
sse2_memcpy (asm)                averaging 3141.33 microseconds
mmx_memcpy (asm)                 averaging 4010.17 microseconds
mmx2_memcpy (asm)                averaging 3211.75 microseconds
avx_memcpy (intrinsic)           averaging 3003.14 microseconds
avx_memcpy (asm)                 averaging 2980.97 microseconds
avx512_memcpy (intrinsic)        unsupported on this CPU
rep movsb (asm)                  averaging 3987.91 microseconds
---------------------------------------------------------------------------
Averaging 6900 copies of 16MB of data per function for operator new
---------------------------------------------------------------------------
std::memcpy                      averaging 3172.95 microseconds
asm_memcpy (asm)                 averaging 3173.5 microseconds
sse_memcpy (intrinsic)           averaging 1623.84 microseconds
sse_memcpy (asm)                 averaging 1657.07 microseconds
sse2_memcpy (intrinsic)          averaging 1616.95 microseconds
sse2_memcpy (asm)                averaging 1739.05 microseconds
mmx_memcpy (asm)                 averaging 2623.71 microseconds
mmx2_memcpy (asm)                averaging 1699.33 microseconds
avx_memcpy (intrinsic)           averaging 1710.09 microseconds
avx_memcpy (asm)                 averaging 1688.34 microseconds
avx512_memcpy (intrinsic)        unsupported on this CPU
rep movsb (asm)                  averaging 3175.14 microseconds

Skylake Xeon Gold 6154 1 CPU (1 socket vide) sur Supermicro X11DPH-I avec 48 Go DDR4-2666 (18c / 36t, 24,75 Mo de cache L3)

---------------------------------------------------------------------------
Averaging 5000 copies of 16MB of data per function for operator new
---------------------------------------------------------------------------
std::memcpy                      averaging 1832.42 microseconds
asm_memcpy (asm)                 averaging 1837.62 microseconds
sse_memcpy (intrinsic)           averaging 1647.84 microseconds
sse_memcpy (asm)                 averaging 1710.53 microseconds
sse2_memcpy (intrinsic)          averaging 1645.54 microseconds
sse2_memcpy (asm)                averaging 1794.36 microseconds
mmx_memcpy (asm)                 averaging 2030.51 microseconds
mmx2_memcpy (asm)                averaging 1816.82 microseconds
avx_memcpy (intrinsic)           averaging 1686.49 microseconds
avx_memcpy (asm)                 averaging 1716.15 microseconds
avx512_memcpy (intrinsic)        averaging 1761.6 microseconds
rep movsb (asm)                  averaging 1977.6 microseconds

Skylake Xeon Gold 6154 2 CPU sur Supermicro X11DPH-I avec 96 Go DDR4-2666

---------------------------------------------------------------------------
Averaging 4100 copies of 16MB of data per function for VirtualAllocExNuma to NUMA node 0(local)
---------------------------------------------------------------------------
std::memcpy                      averaging 3131.6 microseconds
asm_memcpy (asm)                 averaging 3070.57 microseconds
sse_memcpy (intrinsic)           averaging 3297.72 microseconds
sse_memcpy (asm)                 averaging 3423.38 microseconds
sse2_memcpy (intrinsic)          averaging 3274.31 microseconds
sse2_memcpy (asm)                averaging 3413.48 microseconds
mmx_memcpy (asm)                 averaging 2069.53 microseconds
mmx2_memcpy (asm)                averaging 3694.91 microseconds
avx_memcpy (intrinsic)           averaging 3118.75 microseconds
avx_memcpy (asm)                 averaging 3224.36 microseconds
avx512_memcpy (intrinsic)        averaging 3156.56 microseconds
rep movsb (asm)                  averaging 3155.36 microseconds
---------------------------------------------------------------------------
Averaging 4100 copies of 16MB of data per function for VirtualAllocExNuma to NUMA node 1
---------------------------------------------------------------------------
std::memcpy                      averaging 5309.77 microseconds
asm_memcpy (asm)                 averaging 5330.78 microseconds
sse_memcpy (intrinsic)           averaging 2350.61 microseconds
sse_memcpy (asm)                 averaging 2402.57 microseconds
sse2_memcpy (intrinsic)          averaging 2338.61 microseconds
sse2_memcpy (asm)                averaging 2475.51 microseconds
mmx_memcpy (asm)                 averaging 2883.97 microseconds
mmx2_memcpy (asm)                averaging 2517.69 microseconds
avx_memcpy (intrinsic)           averaging 2356.07 microseconds
avx_memcpy (asm)                 averaging 2415.22 microseconds
avx512_memcpy (intrinsic)        averaging 2487.01 microseconds
rep movsb (asm)                  averaging 5372.98 microseconds
---------------------------------------------------------------------------
Averaging 4100 copies of 16MB of data per function for operator new
---------------------------------------------------------------------------
std::memcpy                      averaging 3075.1 microseconds
asm_memcpy (asm)                 averaging 3061.97 microseconds
sse_memcpy (intrinsic)           averaging 3281.17 microseconds
sse_memcpy (asm)                 averaging 3421.38 microseconds
sse2_memcpy (intrinsic)          averaging 3268.79 microseconds
sse2_memcpy (asm)                averaging 3435.76 microseconds
mmx_memcpy (asm)                 averaging 2061.27 microseconds
mmx2_memcpy (asm)                averaging 3694.48 microseconds
avx_memcpy (intrinsic)           averaging 3111.16 microseconds
avx_memcpy (asm)                 averaging 3227.45 microseconds
avx512_memcpy (intrinsic)        averaging 3148.65 microseconds
rep movsb (asm)                  averaging 2967.45 microseconds

Skylake-X i9-7940X sur ASUS ROG Rampage VI Extreme avec 32 Go DDR4-4266 (14c / 28t, 19,25 Mo de cache L3) (overclocké à 3,8 GHz / 4,4 GHz turbo, DDR à 4040 MHz, fréquence AVX cible 3737 MHz, cible AVX- 512 Fréquence 3535 MHz, fréquence cible du cache 2424 MHz)

---------------------------------------------------------------------------
Averaging 6500 copies of 16MB of data per function for operator new
---------------------------------------------------------------------------
std::memcpy                      averaging 1750.87 microseconds
asm_memcpy (asm)                 averaging 1748.22 microseconds
sse_memcpy (intrinsic)           averaging 1743.39 microseconds
sse_memcpy (asm)                 averaging 3120.18 microseconds
sse2_memcpy (intrinsic)          averaging 1743.37 microseconds
sse2_memcpy (asm)                averaging 2868.52 microseconds
mmx_memcpy (asm)                 averaging 2255.17 microseconds
mmx2_memcpy (asm)                averaging 3434.58 microseconds
avx_memcpy (intrinsic)           averaging 1698.49 microseconds
avx_memcpy (asm)                 averaging 2840.65 microseconds
avx512_memcpy (intrinsic)        averaging 1670.05 microseconds
rep movsb (asm)                  averaging 1718.77 microseconds

Broadwell i7-6800k sur ASUS X99 avec 24 Go DDR4-2400 (6c / 12t, 15 Mo de cache L3)

---------------------------------------------------------------------------
Averaging 64900 copies of 16MB of data per function for operator new
---------------------------------------------------------------------------
std::memcpy                      averaging 2522.1 microseconds
asm_memcpy (asm)                 averaging 2615.92 microseconds
sse_memcpy (intrinsic)           averaging 1621.81 microseconds
sse_memcpy (asm)                 averaging 1669.39 microseconds
sse2_memcpy (intrinsic)          averaging 1617.04 microseconds
sse2_memcpy (asm)                averaging 1719.06 microseconds
mmx_memcpy (asm)                 averaging 3021.02 microseconds
mmx2_memcpy (asm)                averaging 1691.68 microseconds
avx_memcpy (intrinsic)           averaging 1654.41 microseconds
avx_memcpy (asm)                 averaging 1666.84 microseconds
avx512_memcpy (intrinsic)        unsupported on this CPU
rep movsb (asm)                  averaging 2520.13 microseconds

Les fonctions d'assemblage sont dérivées de fast_memcpy dans les xine-libs, principalement utilisées pour comparer avec l'optimiseur de msvc ++.

Le code source du test est disponible sur https://github.com/marcmicalizzi/memcpy_test (c'est un peu long à mettre dans le post)

Quelqu'un d'autre a-t-il rencontré cela ou quelqu'un a-t-il une idée de pourquoi cela pourrait se produire?


Mise à jour 2018-05-15 13: 40EST

Donc, comme suggéré par Peter Cordes, j'ai mis à jour le test pour comparer les prélecture et les non prélecture, et les magasins NT par rapport aux magasins réguliers, et réglé la prélecture effectuée dans chaque fonction ( je n'ai aucune expérience significative avec l'écriture de la prélecture, donc si Je fais des erreurs avec ça, s'il vous plaît faites le moi savoir et j'ajusterai les tests en conséquence . Ces modifications sont reflétées dans la dernière révision du lien GitHub que j'ai faite plus tôt pour tous ceux qui recherchent le code source.

J'ai aussi ajouté un memcpy SSE4.1, car avant SSE4.1 je ne trouve aucun _mm_stream_load(je spécifiquement utilisé _mm_stream_load_si128) les fonctions SSE, donc sse_memcpyet sse2_memcpyne peut pas être complètement en utilisant les magasins NT, et ainsi la avx_memcpyfonction utilise les fonctions de AVX2 pour le chargement de flux.

J'ai choisi de ne pas encore faire de test pour les modèles d'accès au magasin pur et à la charge pure, car je ne suis pas sûr que le magasin pur puisse être significatif, car sans une charge dans les registres auxquels il accède, les données seraient dénuées de sens et invérifiables.

Les résultats intéressants du nouveau test étaient que sur la configuration Xeon Skylake Dual Socket et uniquement sur cette configuration, les fonctions de stockage étaient en fait beaucoup plus rapides que les fonctions de streaming NT pour la copie de mémoire de 16 Mo. De plus, uniquement sur cette configuration (et uniquement avec LLC prefetch activé dans le BIOS), prefetchnta dans certains tests (SSE, SSE4.1) surpasse à la fois prefetcht0 et no prefetch.

Les résultats bruts de ce nouveau test sont trop longs pour être ajoutés à la publication, ils sont donc publiés sur le même référentiel git que le code source sous results-2018-05-15

Je ne comprends toujours pas pourquoi pour le streaming de magasins NT, le nœud NUMA distant est plus rapide sous la configuration Skylake SMP, bien que l'utilisation de magasins réguliers soit toujours plus rapide que celle sur le nœud NUMA local

Marc Micalizzi
la source
1
Vous n'avez pas encore eu la chance de digérer vos données, mais voir aussi Pourquoi Skylake est-il tellement meilleur que Broadwell-E pour le débit de mémoire à thread unique? (en comparant un Skylake à quatre cœurs à un Broadwell à plusieurs cœurs et en constatant l'inconvénient d'une latence mémoire / L3 plus élevée dans les systèmes à plusieurs cœurs où la bande passante à cœur unique est limitée par la concurrence maximale en mémoire dans un cœur, et non par les contrôleurs DRAM.) SKX a une latence élevée / faible bande passante par cœur à L3 / mémoire en général, selon les tests de Mysticial et d'autres résultats. Vous voyez probablement cela.
Peter Cordes
1
Certaines de vos copies utilisent-elles les magasins NT? Je viens de vérifier, et toutes vos copies sauf MMX utilisent prefetchntaet les magasins NT! C'est un énorme fait important que vous avez laissé de côté! Voir Enhanced REP MOVSB ​​pour memcpy pour plus de discussion sur ERMSB rep movsbvs magasins vectoriels NT vs magasins vectoriels réguliers. Jouer avec cela serait plus utile que MMX vs SSE. Il suffit probablement d'utiliser AVX et / ou AVX512 et d'essayer NT par rapport à regular, et / ou de supprimer la prélecture SW.
Peter Cordes
1
Avez-vous réglé la distance de prélecture pour vos machines SKX? SKX prefetchntacontourne L3 ainsi que L2 (car L3 n'est pas inclusif), il est donc plus sensible à la distance de prélecture (trop tard et les données doivent venir de la DRAM à nouveau, pas seulement L3), donc c'est plus "fragile" ( sensible au réglage de la bonne distance). Vos distances de prélecture semblent cependant assez faibles, inférieures à 500 octets si je lis correctement l'asm. Les tests de @ Mysticial sur SKX ont révélé que cela prefetchntapeut être un gros ralentissement pour cet uarch ), et il ne le recommande pas.
Peter Cordes
1
Vous avez certainement des résultats intéressants ici, mais nous devons les démêler de divers effets . Avoir des numéros avec et sans magasins NT peut nous dire quelque chose d'utile sur le comportement de NUMA. Remplir un 2ème socket force même les échecs locaux de L3 à espionner le CPU distant, au moins sur Broadwell / Haswell. Les Xeons E5 à double socket n'ont pas de filtre snoop. Je pense que l' or Xeons faire ont des filtres de Snoop, parce qu'ils sont capables de fonctionner dans plus de systèmes à deux sockets. Mais je ne suis pas sûr de sa taille, ni de ce que cela signifie vraiment: PI n'a pas effectué le réglage de la performance de la mémoire sur plusieurs sockets.
Peter Cordes
2
SKX est une interconnexion fondamentalement différente; un maillage au lieu d'un anneau. C'est un résultat intéressant, mais pas incroyable et peut ne pas être le signe d'une mauvaise configuration. IDK, j'espère que quelqu'un d'autre avec plus d'expérience avec le matériel pourra faire plus de lumière.
Peter Cordes

Réponses:

0

Votre mémoire est-elle le rang incorrect? Peut-être que votre carte a quelque chose de bizarre avec le classement de la mémoire lorsque vous ajoutez ce deuxième processeur? Je sais que lorsque vous avez des machines à quatre processeurs, elles font toutes sortes de choses étranges pour faire fonctionner la mémoire correctement et si vous avez une mémoire classée incorrecte, cela fonctionnera parfois, mais reviendra à 1/4 ou 1/2 de la vitesse. Peut-être que SuperMicro a fait quelque chose dans cette carte pour transformer le DDR4 et le double processeur en Quad Channel et il utilise des calculs similaires. Rang incorrect == 1/2 vitesse.

thelanranger
la source
Cela ne semble pas être le cas, toute la mémoire est 1R8 et correspond au rang du supermicro qvl pour la carte mère. Cela valait bien un chèque!
Marc Micalizzi
Je sais que c'est un système complètement différent, mais c'est ce à quoi je faisais référence également. qrl.dell.com/Files/en-us/Html/Manuals/R920/… Vous remarquerez que les exigences de classement changent lorsque vous augmentez la quantité de sticks / CPU.
thelanranger