Desempenho de memória lenta inesperado e inexplicável (e incomum) com o Xeon Skylake SMP

27

Testamos um servidor usando 2x CPUs Xeon Gold 6154 com uma placa-mãe Supermicro X11DPH-I e 96 GB de RAM, e encontramos alguns problemas de desempenho muito estranhos em torno da memória quando comparados à execução com apenas 1 CPU (um soquete vazio), dois similares CPU Haswell Xeon E5-2687Wv3 (para esta série de testes, mas outras Broadwells executam da mesma forma), Broadwell-E i7s e Skylake-X i9s (para comparação).

É de se esperar que os processadores Skylake Xeon com memória mais rápida tenham desempenho mais rápido que o Haswell quando se trata de várias funções de memória e até alocação de memória (não abordadas nos testes abaixo, pois encontramos uma solução alternativa), mas com as duas CPUs instaladas , o Skylake Xeons tem quase a metade da velocidade do Haswell Xeons e menos ainda quando comparado ao i7-6800k. O que é ainda mais estranho é que, ao usar o Windows VirtualAllocExNuma para atribuir o nó NUMA à alocação de memória, enquanto as funções de cópia de memória simples têm desempenho pior no nó remoto versus o nó local, as funções de cópia de memória que utilizam os registros SSE, MMX e AVX executam muito mais rápido no nó NUMA remoto do que no nó local (o quê?). Como observado acima, com o Skylake Xeons,

Não tenho certeza se isso é um bug na placa-mãe ou na CPU, ou com UPI vs QPI, ou nenhuma das opções acima, mas nenhuma combinação das configurações do BIOS parece valer isso. Desabilitar o NUMA (não incluído nos resultados do teste) na BIOS melhora o desempenho de todas as funções de cópia usando os registros SSE, MMX e AVX, mas todas as outras funções de cópia de memória simples sofrem grandes perdas também.

Para o nosso programa de teste, testamos usando funções de montagem em linha e _mmintrínsecos, usamos o Windows 10 com Visual Studio 2017 para tudo, exceto as funções de montagem, que como o msvc ++ não compila o asm para x64, usamos o gcc de mingw / msys para compile um arquivo obj usando -c -O2sinalizadores, que incluímos no vinculador do msvc ++.

Se o sistema estiver usando nós NUMA, testamos os dois operadores novos para alocação de memória com VirtualAllocExNuma para cada nó NUMA e fazemos uma média cumulativa de 100 cópias de buffer de memória de 16 MB cada para cada função de cópia de memória e alternamos a alocação de memória em que estamos. entre cada conjunto de testes.

Todos os 100 buffers de origem e 100 de destino são alinhados em 64 bytes (para compatibilidade até o AVX512 usando funções de streaming) e inicializados uma vez para dados incrementais para os buffers de origem e 0xff para os buffers de destino.

O número de cópias calculadas em média em cada máquina com cada configuração variava, pois era muito mais rápido em algumas e mais lento em outras.

Os resultados foram os seguintes:

Haswell Xeon E5-2687Wv3 1 CPU (1 soquete vazio) no Supermicro X10DAi com 32GB DDR4-2400 (10c / 20t, 25 MB de cache L3). Mas lembre-se, o benchmark gira em torno de 100 pares de buffers de 16 MB, portanto, provavelmente não estamos recebendo hits 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 cpu em Supermicro X10DAi com 64GB 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 soquete vazio) no Supermicro X11DPH-I com DDR4-2666 de 48 GB (18c / 36t, 24,75 MB 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 no Supermicro X11DPH-I com 96GB 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 no ASUS ROG Rampage VI Extreme com 32GB DDR4-4266 (14c / 28t, 19,25 MB de cache L3) (com overclock para 3.8GHz / 4.4GHz turbo, DDR a 4040MHz, Target AVX Frequency 3737MHz, Target AVX- 512 Frequência 3535MHz, frequência de cache alvo 2424MHz)

---------------------------------------------------------------------------
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 no ASUS X99 com 24GB DDR4-2400 (6c / 12t, 15 MB 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

As funções de montagem são derivadas de fast_memcpy em xine-libs, usadas principalmente para comparar com o otimizador do msvc ++.

O código-fonte do teste está disponível em https://github.com/marcmicalizzi/memcpy_test (é um pouco longo para colocar no post)

Alguém mais se deparou com isso ou alguém tem alguma ideia de por que isso pode estar acontecendo?


Atualização 2018-05-15 13: 40EST

Assim, como sugerido por Peter Cordes, atualizei o teste para comparar pré-buscados versus não pré-buscados e lojas NT versus lojas regulares e ajustamos a pré-busca realizada em cada função ( não tenho nenhuma experiência significativa com a pré-busca de escrita, por isso, se Estou cometendo algum erro com isso, informe-me e ajustarei os testes de acordo. A pré-busca tem um impacto, portanto, pelo menos, está fazendo alguma coisa ). Essas alterações são refletidas na revisão mais recente do link do GitHub que fiz anteriormente para quem procura o código-fonte.

Eu também acrescentou um memcpy SSE4.1, desde antes da SSE4.1 não consigo encontrar nenhum _mm_stream_load(eu usei especificamente _mm_stream_load_si128) funções SSE, por isso sse_memcpye sse2_memcpynão pode ser completamente usando lojas NT, e assim a avx_memcpyfunção usa funções AVX2 para carregamento de stream.

Optei por não fazer ainda um teste para padrões de acesso de armazenamento puro e carga pura, pois não tenho certeza se o armazenamento puro pode ser significativo, pois sem uma carga nos registros que está acessando, os dados seriam sem sentido e não verificáveis.

Os resultados interessantes com o novo teste foram que, na configuração do Xeon Skylake Dual Socket, e somente nessa configuração, as funções de armazenamento eram realmente significativamente mais rápidas que as funções de streaming do NT para cópia de 16 MB de memória. Assim como somente nessa configuração (e apenas com a pré-busca LLC ativada no BIOS), a pré-busca em alguns testes (SSE, SSE4.1) supera tanto a pré-busca como a pré-busca.

Os resultados brutos desse novo teste são muito longos para serem adicionados à postagem; portanto, eles são postados no mesmo repositório git que o código-fonte em results-2018-05-15

Ainda não entendo por que, para o streaming de repositórios NT, o nó NUMA remoto é mais rápido na configuração do Skylake SMP, embora o uso de repositórios regulares seja ainda mais rápido do que no nó NUMA local

Marc Micalizzi
fonte
11
Ainda não foi possível digerir seus dados, mas veja também Por que o Skylake é muito melhor que o Broadwell-E para taxa de transferência de memória de thread único? (comparando um Skylake de quatro núcleos com um Broadwell de vários núcleos e vendo a desvantagem de maior latência de memória / L3 em sistemas com muitos núcleos em que a largura de banda de núcleo único é limitada pela simultaneidade máxima de memória em um núcleo, não pelos controladores DRAM.) O SKX possui alta latência / baixa largura de banda por núcleo para L3 / memória em geral, de acordo com os testes da Mysticial e outros resultados. Você provavelmente está vendo isso.
Peter Cordes
11
Alguma de suas cópias está usando lojas NT? Acabei de verificar e todas as suas cópias, exceto a MMX, estão usando as prefetchntalojas NT e NT! Esse é um fato muito importante que você deixou de fora da sua pergunta! Consulte REP MOVSB ​​aprimorado para memcpy para obter mais discussões sobre rep movsbarmazenamentos de vetores ERMSB vs. NT vs. armazenamentos vetoriais regulares. Brincar com isso seria mais útil que o MMX vs. SSE. Provavelmente, basta usar o AVX e / ou o AVX512 e tentar NT vs. regular e / ou deixar de fora a pré-busca do SW.
Peter Cordes
11
Você ajustou a distância de pré-busca para suas máquinas SKX? O SKX prefetchntaignora L3 e L2 (porque L3 não inclui), por isso é mais sensível à distância de pré-busca (tarde demais e os dados precisam vir da DRAM novamente, não apenas L3), por isso é mais "quebradiço" ( sensível à sintonia da distância correta). Suas distâncias de pré-busca parecem bastante baixas, porém, abaixo de 500 bytes, se eu estiver lendo o asm corretamente. O teste de @ Mysticial no SKX descobriu que prefetchntapode ser uma grande desaceleração nesse uarch ), e ele não o recomenda.
Peter Cordes
11
Você definitivamente tem alguns resultados interessantes aqui, mas precisamos desembaraçá-los de vários efeitos . Ter números com e sem repositórios NT pode nos dizer algo útil sobre o comportamento da NUMA. Preencher um segundo soquete força até as falhas L3 locais a bisbilhotar a CPU remota, pelo menos em Broadwell / Haswell. Os E5 Xeons de soquete duplo não têm um filtro bisbilhoteiro. Acho ouro Xeons fazer têm filtros snoop, porque eles são capazes de operar em mais de sistemas dual-socket. Mas não tenho certeza de quão grande é, ou o que isso realmente significa: o PI não fez o ajuste de desempenho da memória em vários soquetes.
Peter Cordes
2
SKX é uma interconexão fundamentalmente diferente; uma malha em vez de um anel. É um resultado interessante, mas não inacreditável e pode não ser um sinal de configuração incorreta. IDK, espero que alguém com mais experiência com o hardware possa lançar mais luz.
Peter Cordes

Respostas:

0

Sua memória está na posição incorreta? Talvez sua placa tenha alguma coisa estranha com o ranking de memória quando você adiciona a segunda CPU? Eu sei que quando você tem máquinas com CPU Quad, eles fazem todos os tipos de coisas estranhas para que a memória funcione corretamente e, se você tiver uma memória classificada incorreta, às vezes ela funcionará, mas voltará para 1/4 ou 1/2 da velocidade. Talvez o SuperMicro tenha feito algo nessa placa para transformar o DDR4 e o CPU duplo em um canal quádruplo e esteja usando matemática semelhante. Posição incorreta == 1/2 velocidade.

thelanranger
fonte
Não parece ser o caso, toda a memória é 1R8 e corresponde à classificação do supermicro qvl da placa-mãe. Vale a pena conferir!
Marc Micalizzi
Eu sei que este é um sistema completamente diferente, mas é isso que eu estava me referindo também. qrl.dell.com/Files/pt-BR/Html/Manuals/R920/… Você notará que os requisitos de classificação mudam quando você aumenta a quantidade de sticks / CPUs.
thelanranger