Мы тестировали сервер с использованием двух процессоров Xeon Gold 6154 с материнской платой Supermicro X11DPH-I и 96 ГБ ОЗУ и обнаружили некоторые очень странные проблемы с производительностью, связанные с памятью, по сравнению с работой только с 1 ЦП (один сокет пуст), аналогично двойному CPU Haswell Xeon E5-2687Wv3 (для этой серии тестов, но другие Broadwells работают аналогично), Broadwell-E i7s и Skylake-X i9s (для сравнения).
Можно ожидать, что процессоры Skylake Xeon с более быстрой памятью будут работать быстрее, чем Haswell, когда речь идет о различных функциях memcpy и даже распределении памяти (не охваченных тестами ниже, как мы нашли обходной путь), но вместо этого с обоими установленными процессорами Xeons Skylake работают почти вдвое быстрее, чем Xeons Haswell, и даже меньше по сравнению с i7-6800k. Что еще более странно, это то, что при использовании Windows VirtualAllocExNuma для назначения узла NUMA для выделения памяти, в то время как функции копирования простой памяти ожидаемо хуже работают на удаленном узле по сравнению с локальным узлом, функции копирования памяти, использующие регистры SSE, MMX и AVX, выполняют много быстрее на удаленном узле NUMA, чем на локальном (что?). Как отмечалось выше, с Skylake Xeons,
Я не уверен, является ли это ошибкой на материнской плате или процессоре, или с UPI против QPI, или ни с одним из вышеперечисленных, но никакая комбинация настроек BIOS, похоже, не поможет. Отключение NUMA (не включенного в результаты теста) в BIOS действительно повышает производительность всех функций копирования, использующих регистры SSE, MMX и AVX, но все другие функции копирования в обычную память также несут большие потери.
Для нашей тестовой программы мы тестировали как встроенные функции сборки, так и _mm
встроенные, мы использовали Windows 10 с Visual Studio 2017 для всего, кроме функций сборки, которые как msvc ++ не будет компилировать asm для x64, мы использовали gcc из mingw / msys для скомпилируйте файл obj, используя -c -O2
флаги, которые мы включили в компоновщик msvc ++.
Если система использует узлы NUMA, мы тестируем оба оператора, новые для распределения памяти, с VirtualAllocExNuma для каждого узла NUMA и делаем совокупное среднее из 100 копий буфера памяти по 16 МБ каждая для каждой функции копирования памяти, и мы проверяем, на каком выделении памяти мы находимся между каждым набором тестов.
Все 100 исходных и 100 целевых буферов выровнены на 64 байта (для совместимости до AVX512 с использованием потоковых функций) и инициализированы один раз для приращения данных для исходных буферов и 0xff для целевых буферов.
Число копий, усредняемых на каждой машине с каждой конфигурацией, варьировалось, поскольку на одних это было намного быстрее, а на других - намного медленнее.
Результаты были следующими:
Haswell Xeon E5-2687Wv3 1 процессор (1 пустая розетка) на Supermicro X10DAi с 32 ГБ памяти DDR4-2400 (10 c / 20 т, 25 МБ кэш-памяти третьего уровня). Но помните, что эталонный тест вращается через 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 (18 с / 36 т, 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 ( 14 к / 28 т, 19,25 МБ кэш-памяти L3) (разогнан до 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 (6 с / 12 т, 15 МБ кэш-памяти третьего уровня)
---------------------------------------------------------------------------
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, которую я сделал ранее для тех, кто ищет исходный код.
Я также добавил memcpy в SSE4.1, так как до 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
Я до сих пор не понимаю, почему для потоковой передачи хранилищ NT удаленный узел NUMA быстрее при настройке Skylake SMP, хотя использование обычных хранилищ по-прежнему быстрее, чем на локальном узле NUMA
prefetchnta
и магазины NT! Это огромный важный факт, который вы оставили вне своего вопроса! Посмотрите Enhanced REP MOVSB для memcpy для более детального обсуждения ERMSB rep movsb
против хранилищ векторов NT и обычных хранилищ векторов. Возиться с этим было бы полезнее, чем MMX против SSE. Возможно, просто используйте AVX и / или AVX512 и попробуйте NT по сравнению с обычным, и / или пропуская предварительную выборку SW.
prefetchnta
обходит L3 так же, как и L2 (потому что L3 не включительно), поэтому он более чувствителен к расстоянию предварительной выборки (слишком поздно, и данные должны приходить снова из DRAM, а не только к L3), поэтому он более «хрупкий» ( чувствителен к настройке нужного расстояния). Тем не менее, ваши предвыборочные расстояния выглядят довольно низкими, менее 500 байт, если я правильно читаю asm. Тестирование @ Mysticial на SKX показало, что это prefetchnta
может сильно замедлить работу этого уарха ), и он не рекомендует этого.