Во время сравнения нового серверного чипа Centriq от Qualcomm с имеющимися в наличии Intel Xeon поколения Skylake мною была замечена странная штука: производительность шифра ChaCha20-Poly1305 плохо масштабируется при добавлении ядер. Один поток работал на скорости примерно 2,89 Гбайт/с, а на 24 ядрах и при 48 потоках сумарная производительность составила всего лишь 35 Гбайт/с.
Неплохо, конечно, но я ожидал увидеть что-то вроде 69 Гбайт/с. 35 Гбайт/с это всего лишь 1,46 Гбайт/с на ядро, или около 50 % от производительности одного ядра. AES-GCM масштабируется в тех же условиях гораздо лучше, до примерно 80 % производительности одного ядра, что объясняется способностью процессора повышать частоту при нагрузке на одно ядро.

Почему же ChaCha20-Poly1305 масштабируется так плохо? Познакомьтесь с набором инструкций AVX-512, который расширяет большинство существующих SIMD-инструкций до 512 бит и добавляет несколько новых. Проблема в том, что они потребляют значительное количество энергии, ведь за один заход требуется выполнить от 8 до 64 обычных операций.

Для сохранения энергопотребления на приемлемом уровне Intel в семействе процессоров Haswell 3 года назад ввёл динамическое изменение частоты. Этот механизм уменьшает базовую частоту процессора в случае исполнения AVX2 или AVX-512 инструкций. Если исполняется только код AVX-512, всё в порядке. Частота ниже, но общее количество операций, выполняемых в единицу времени, больше.
OpenSSL 1.1.1dev содержит несколько вариантов реализации ChaCha20-Poly1305, включая использование AVX2 и AVX-512. BoringSSL реализует алгоритм несколько другим образом и только с использованием AVX2, поэтому его производительность на одном ядре всего 1,6 Гбайт/с вместо 2,89 Гбайт/с у OpenSSL.
Какое влияние это оказывает в ситуации, когда нагрузка представляет из себя смесь обычных вычислений и небольшой доли вычислений с использованием AVX-512? Мы используем Xeon Silver 4116 с базовой частотой 2,1 ГГц в двухсокетной конфигурации. Из таблицы, найденной на wikichip, видно, что выполнение инструкций AVX-512 даже на одном ядре уменьшит базовую частоту до 1,8 ГГц, а выполнение инструкций на всех ядрах уменьшит базовую частоту до 1,4 ГГц.
Представим, что у нас есть вебсервер (Apache или NGINX), на котором вдобавок выполняются приложения. Возникает вопрос: что будет, если начать шифровать трафик алгоритмом ChaCha-Poly1305, реализованным с помощью инструкций AVX-512?
Я собрал две версии NGINX: одну с OpenSSL 1.1.1dev, другую с BoringSSL — и установил их на сервер с двумя Xeon Silver 4116, получив в сумме 24 ядра. Вебсервер сконфигурирован на обработку и отдачу HTML-страницы среднего размера. Использовался LuaJIT для удаления переводов строк и лишних пробельных символов, а также сжатие страницы алгоритмом brotli. Затем я измерил количество запросов, которые сервер был способен обработать при полной нагрузке.

При использовании ChaCha20-Poly1305 вместо AES-GCM вебсервер, собранный с OpenSSL, обслужил на 10 % меньше запросов, что эквивалентно простаиванию двух ядер процессора. Можно предположить, что такая разница наблюдается в силу невысокой скорости работы самого алгоритма ChaCha20-Poly1305, но это не так.
Во-первых, BoringSSL показал одинаково хорошие результаты с обоими алгоритмами шифрования. Во-вторых, даже когда только 10 % запросов используют ChaCha20-Poly1305, производительность падает на 5,5 %, и на 7 % в случае, если доля таких запросов доходит до 20 %. Для справки: в реальном HTTPS-трафике Cloudflare доля запросов, использующих алгоритм ChaCha20-Poly1305, составляет 15 %.
По данным perf процессор тратит на обработку инструкций AVX-512 только 2,5 % времени при 100 % доле запросов с ChaCha20-Poly1305, и менее 0,3 % при доле таких запросов в 10 %. Независимо от доли запросов частота CPU снижается, ведь инструкции AVX-512 выполняются на всех ядрах сразу.
Сложно сказать, насколько понижается частота процессора в конкретный момент времени. Однако, понаблюдав за показаниями lscpu, я выяснил, что во время исполнения команды openssl speed -evp chacha20-poly1305 -multi 48 получается CPU MHz: 1199.963; для вебсервера с OpenSSL и алгоритмом AES-GCM получается CPU MHz: 2399.926, а для вебсервера с OpenSSL и алгоритмом ChaCha20-Poly1305 — CPU MHz: 2184.338, то есть на 9 % меньше.
Ещё одно интересное различие в том, что алгоритм ChaCha20-Poly1305 с использованием AVX2 немного медленнее работает в OpenSSL, но не теряет в производительности в BoringSSL. Причина в том, что BoringSSL не использует инструкции умножения AVX2 для Poly1305, а для ChaCha20 использует только относительно простые инструкции xor, shift, add, что позволяет ядру оставаться на базовой частоте.
OpenSSL 1.1.1dev всё ещё находится в разработке, поэтому я подозреваю, что эта проблема ещё никому не встретилась. Мы переключились на использование BoringSSL несколько месяцев назад, и производительность наших серверов не будет страдать от описываемого эффекта.
Что день грядущий нам готовит? Для будущих поколений Intel анонсировал новые наборы инструкций, которые должны ещё больше повысить производительность криптогра��ических операций. Это расширения включают в себя AVX512+VAES, AVX512+VPCLMULQDQ и AVX512IFMA. Однако если проблемы с понижением частоты к тому времени не будут решены, использование новых наборов инструкций может принести с точки зрения производительности больше вреда, чем пользы.
Проблема не только и не столько в криптографических библиотеках. Авторов OpenSSL нельзя винить в том, что они ищут пути для увеличения производительности, наоборот, я сам написал приличное количество кода OpenSSL, использующего AVX-512. Наблюдаемое поведение это всего лишь печальный побочный эффект. Существует множество библиотек, которые используют AVX-512, и пользователи скорее всего не в курсе деталей их реализации. Если вам не требуется использование AVX-512 для специфических вычислительно-ёмких задач, я предлагаю вам выключить их поддержку на ваших серверах и персональных компьютерах во избежание нежелательного понижения частоты процессора.
Разумеется, эти факты описаны в руководящей документации: само по себе снижение частоты описано в Optimizing Performance with Intel Advanced Vector Extensions, а пределы её изменения в зависимости от количества занятых выпонением операций ядер для конкретного процессора семейства Skylake в Intel® Xeon® Processor Scalable Family Specification Update. Однако документы эти не для широкой публики в том смысле, что читают и даже знают об их существовании немногие, да и описано там далеко не всё, что хотелось бы знать. Мне не удалось отыскать, например, официальное описание работы PCU и собственно алгоритма понижения частоты при выполнении инструкций AVX-512 и её повышения после.
На Хабре уже были статьи, в которых обсуждалось применение AVX-512 в разработке (например, Как я сделал самый быстрый ресайз изображений. Часть 2, SIMD). Я нахожу очень полезным знать о нюансах поведения современных процессоров и системным администраторам тоже (как бы их сейчас не называли), поэтому и публикую перевод в хаб «Системное администрирование».
Разъяснение: переводчик никак не связан с Cloudflare, Inc. Перевод сделан из любви к искусству, все права у их обладателей. Автор КДПВ blumblaum, CC BY-SA 2.0. Заголовок придумал CodeRush.
