Мы тестировали сервер, использующий 2 процессора Xeon Gold 6154 с материнской платой Supermicro X11DPH-I и 96 ГБ ОЗУ, и обнаружили очень странные проблемы с производительностью, связанные с памятью по сравнению с работой только с 1 процессором (один разъем пуст), аналогичным двойным CPU Haswell Xeon E5-2687Wv3 (для этой серии тестов, но другие Broadwell работают аналогично), Broadwell-E i7s и Skylake-X i9s (для сравнения).
Можно было бы ожидать, что процессоры Skylake Xeon с более быстрой памятью будут работать быстрее, чем Haswell, когда дело доходит до различных функций memcpy и даже выделения памяти (не рассматривается в приведенных ниже тестах, поскольку мы нашли обходной путь), но вместо этого с двумя установленными процессорами Skylake Xeon работает почти вдвое медленнее, чем Haswell Xeon, и даже меньше по сравнению с i7-6800k. Что еще более странно, так это то, что при использовании Windows VirtualAllocExNuma для назначения узла NUMA для распределения памяти, в то время как функции простого копирования памяти ожидаемо работают хуже на удаленном узле по сравнению с локальным узлом, функции копирования памяти, использующие регистры SSE, MMX и AVX, работают намного хуже. быстрее на удаленном узле NUMA, чем на локальном (что?). Как отмечалось выше, со Skylake Xeons, если мы потянем 1 процессор, он будет работать более или менее так, как ожидалось (все еще немного медленнее, чем Haswell, но не намного).
Я не уверен, является ли это ошибкой на материнской плате или процессоре, или с UPI против QPI, или ничего из вышеперечисленного, но никакая комбинация настроек BIOS, похоже, не помогает. Отключение NUMA (не включенного в результаты тестирования) в BIOS действительно улучшает производительность всех функций копирования с использованием регистров SSE, MMX и AVX, но все другие функции копирования простой памяти также несут большие потери.
Для нашей тестовой программы мы тестировали как встроенные функции сборки, так и _mm
intrinsic, мы использовали Windows 10 с Visual Studio 2017 для всего, кроме функций сборки, которые, поскольку msvc ++ не компилирует asm для x64, мы использовали gcc из mingw / msys для компиляции файла obj с использованием -c -O2
flags, которые мы включили в компоновщик msvc ++.
Если система использует узлы NUMA, мы тестируем оба оператора new для выделения памяти с помощью VirtualAllocExNuma для каждого узла NUMA и делаем совокупное среднее значение 100 копий буфера памяти по 16 МБ каждая для каждой функции копирования памяти, и мы меняем распределение памяти, в котором мы находимся. между каждым набором тестов.
Все 100 исходных буферов и 100 буферов назначения выровнены по 64 байта (для совместимости до AVX512 с использованием потоковых функций) и инициализируются один раз для инкрементных данных для исходных буферов и 0xff для буферов назначения.
Среднее количество копий на каждой машине с каждой конфигурацией варьировалось, так как на одних это было намного быстрее, а на других - намного медленнее.
Haswell Xeon E5-2687Wv3 1 процессор (1 пустой сокет) на Supermicro X10DAi с 32 ГБ DDR4-2400 (10c / 20t, 25 МБ кеш-памяти L3). Но помните, что тест вращается через 100 пар буферов по 16 МБ, поэтому мы, вероятно, не получаем попаданий в кэш 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 процессора на Supermicro X10DAi с оперативной памятью 64 ГБ
---------------------------------------------------------------------------
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 процессор (1 пустой сокет) на Supermicro X11DPH-I с 48 ГБ DDR4-2666 (18c / 36t, 24,75 МБ кэш-памяти третьего уровня)
---------------------------------------------------------------------------
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 процессора на Supermicro X11DPH-I с 96 ГБ 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 на ASUS ROG Rampage VI Extreme с 32 ГБ DDR4-4266 (14c / 28t, 19,25 МБ кэш-памяти третьего уровня) (разогнан до 3,8 ГГц / 4,4 ГГц в режиме турбо, DDR на 4040 МГц, целевая частота AVX 3737 МГц, целевая частота AVX-512 3535 МГц, целевая частота кэш-памяти 2424 МГц)
---------------------------------------------------------------------------
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 на ASUS X99 с 24 ГБ DDR4-2400 (6c / 12t, 15 МБ кэш-памяти 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
Функции сборки являются производными от fast_memcpy в xine-libs, в основном используются только для сравнения с оптимизатором msvc ++.
Исходный код теста доступен по адресу https://github.com/marcmicalizzi/memcpy_test (это длинновато для публикации)
Кто-нибудь еще сталкивался с этим, или кто-нибудь знает, почему это может происходить?
Обновление 2018-05-15 13: 40EST
Поэтому, как было предложено Питером Кордесом, я обновил тест, чтобы сравнить предварительно выбранные и не предварительно загруженные, а также хранилища NT и обычные хранилища, и настроил предварительную выборку, выполняемую в каждой функции (У меня нет значимого опыта написания предварительной выборки, поэтому, если я сделаю какие-либо ошибки с этим, сообщите мне, и я соответствующим образом настрою тесты. Предварительная выборка оказывает влияние, поэтому, по крайней мере, она что-то делает). Эти изменения отражены в последней редакции ссылки GitHub, которую я сделал ранее для всех, кто ищет исходный код.
Я также добавил SSE4.1 memcpy, поскольку до SSE4.1 я не могу найти ни одного _mm_stream_load
(Я специально использовал _mm_stream_load_si128
) SSE, поэтому sse_memcpy
и sse2_memcpy
не может полностью использовать хранилища NT, а также avx_memcpy
функция использует функции AVX2 для загрузки потока.
Я решил пока не проводить тест для чистых шаблонов доступа к хранилищу и чистой нагрузки, так как я не уверен, может ли чистое хранилище иметь смысл, поскольку без загрузки регистров, к которым он обращается, данные были бы бессмысленными и непроверяемыми.
Интересные результаты нового теста были получены на установке Xeon Skylake Dual Socket и только при такой настройке функции сохранения были значительно быстрее, чем функции потоковой передачи NT при копировании памяти размером 16 МБ. Также только и в этой настройке (и только при включенной предварительной выборке LLC в BIOS) prefetchnta в некоторых тестах (SSE, SSE4.1) превосходит как prefetcht0, так и отсутствие предварительной выборки.
Необработанные результаты этого нового теста слишком длинные для добавления в сообщение, поэтому они размещаются в том же репозитории git, что и исходный код в разделе results-2018-05-15
Ваша память неверный ранг? Возможно, у вашей платы есть какая-то странность с рейтингом памяти, когда вы добавляете второй процессор? Я знаю, что когда у вас есть машины с четырехъядерным процессором, они делают всевозможные странные вещи, чтобы заставить память работать должным образом, и если у вас неправильная ранжированная память, иногда она будет работать, но тактовая частота возвращается к 1/4 или 1/2 скорости. Возможно, SuperMicro сделал что-то на этой плате, чтобы превратить DDR4 и Dual CPU в Quad Channel, и он использует аналогичную математику. Неправильный ранг == 1/2 скорости.