Как стать автором
Обновить

Комментарии 10

нужно было сразу делать профилирование (perf(1), CPU Flame Graphs для наглядности) чтобы увидеть кто ест большими ломтями.

Там не умеют Overhead'ы мерить в принципе…
Даже банальный встраиваемый ehcache с offheap'ом был бы быстрее Redis.
Вот в Terracotta / Hazelcast было пару багов с RMI так что их пока не стоит рассматривать.


Не знаю, мне, лично, после DPDK/SPDK видеть 1.5K RPS довольно прискорбно, так как даже на PHP7 можно в ~10К+ RPS.

Самое смешное, что redis при старте в логи орет, мол, выключите THP, если включено.
Столько работы провели, а можно было просто в лог глянуть :)

Спасибо, это полезная инфа.


Хочу отметить, что по проблеме не очевидно, в чьём логе ошибки искать. Тем более, сообщение появляется там не в момент высокой нагрузки, а при ребуте.


Тем и отличается хороший инженер по производительности, что и в отсутствии очевидного сообщения о ошибке он отыщет корень бед.

Лучше сделать:

echo madvise > /sys/kernel/mm/transparent_hugepage/enabled
Чтобы дать приложениям возможность использовать фичу явно, если она им нужна.

Работает до перезагрузки ОС. Правильное решение: указать этот же параметр в настройках загрузчика и перезагрузить компьютер.

Давно уже у RedHat и Oracle есть документы, где явно предлагается выключать THP при использовании на серверах. Лучше бы наверно эта «фича» для серверных инсталляций была выключена по умолчанию.
Сомневаюсь, что какой-то адекватный вендор будет просить отключить эту фичу. Скорее он будет просить отключить always.

К тому же, данная фича в подавляющем большинстве случаев полезна, нежели вредна.
А при моем профиле нагрузки на mariadb 10.3 наоборот включение улучшает ситуацию(сейчас стоит always)
Очень неочевидная фича.
запуск задач по расписанию и другие системные задачи.

Что за глупости? Какое отношение все это имеет к ядру?

Выделение и освобождение памяти происходит с помощью вызовов malloc и free соответственно

Нет. В линуксе(да и везде) маллок/free не занимаются памятью, а работают только с виртуальным адресным пространством.

которые в итоге будут обработаны системными вызовами ядра, а значит отобразятся в утилизации CPU как системное время.

Не всегда, очень даже не всегда.

В большинстве современных операционных систем

К операционным системам это не имеет никакого отношения. Они лишь использует механизмы предоставленные железом.

виртуальная память организуется с помощью страничной адресации

Не виртуальная, как минимум не только виртуальная.

при таком подходе вся область памяти делится на страницы фиксированной длины

Память.

и при выделении, например, 2 Гб памяти, менеджеру памяти придётся оперировать более чем 500000 страниц

Нет. Менеджер памяти тут вообще не при делах и работает он, как я уже сказал, с адресным пространством. Т.е. он выделит просто диапазон и повесит на него права.

Отображением занимаются совершенно другие подсистемы. Причём занимаются только только созданием этих отображений — далее процессор всё делает сам.

В таком подходе появляются большие накладные расходы на управление и для их уменьшения были придуманы технологии Huge pages

Не для этого.

с их помощью можно увеличить размер страницы

Это аппаратная фича.
например до 2МБ, что существенно сократит количество страниц в куче памяти.

Не для этого.

В некоторых случаях THP вызывает ничем не мотивированное увеличение потребления CPU в систем.

Не в некоторых, а вполне в конкретных. И всё там мотивировано.

В заключение хотелось бы дать несколько советов, для успешного поиска проблем с производительностью:

Ничего из этого сделано не было. Да и советы достаточно глупые. Абстрактная нагрузка на процессор — ничего не показывает. Нужно использовать нормальный профайлер и собирать адекватный трейс, а уже далее смотреть.

Дак для чего же нужны эти большие страницы? Всё очень просто. Абсолютно неважно — сколько там страниц, пока они влезают в кэш. Есть специальный кэш — называется tlb. Когда происходит промах(т.е. отображения нету в кэше) — процессор вынужден пойти(автономно — ЭТО НЕ СИСТЕМНОЕ ВРЕМЯ) — найти и прочитать это отображение из памяти(если не нашел — тогда уже триггерится пейджфолт и уже тогда включается ядро).

Если отображение уже существует в памяти, то это на трупут почти не влияет. Процессор умеет в префетч и заранее подготавливает все отображения. Да, это съедает чуть-чуть трупута памяти(т.к. отображения читать из памяти нужно), но это крохи.

Но проблемы возникают именно на random access, когда никакой префетч не работает и трупут упирается в летенси(помноженной на блок) памяти. Поиск отображения — очень дорогая по летенси операция и от того задержки памяти возрастаю в разы.

Для предотвращения подобного поведения и нужны большие страницы. Их нужно меньше и в кэш влезают уже не мегабайты, а сотни, тысячи мегабайт памяти.

У этого подхода существует только одна проблема — это разряженная память. Дело в том, что по-умолчанию ядро попросту разрешает r/w в какой диапазон вадресспейса. Памяти в «памяти» выделяемой маллоком нет. Вообще(если это большие блоки(100к и больше по умолчанию, насколько я помню)).

Далее, когда мы начнём читать/писать в память — процессор не найдёт отображения и сгнерирует пейджфолт. Далее ядро пойдёт и в специальную таблицу(дерево) запишем отображение. Далее процессор всегда будет читать его или из памяти или из кэша(тлб).

Если мы будем читать/писать память байт за байтом, то первое чтение затригеррит пейджфолт и ядро свяжет первую(нулевую) страницу нашего отображения с реальной страницей(физической памяти). Для последующих 4096 байт(включая текущие чтение/запись) память будет. После — её не будет и опять будет пейджфол.

Тут уже можно понять — в чём проблема. Если мы выделим 2 мегабайта памяти и запишем половину — в ситуации с 4к страницами — выделится 1 мегабайт физической памяти. В ситуации с 2м страница — 2 мегабайта. Подобная ситуация сплошь и рядом в том же С++.

Тоже самое с разряженными массивами. Если у нас есть гигантский массив и мы сделаем туда 10 записей — мы затриггерем в среднем 10 пейджфолтом и свяжем 10 страница по 4к. Если тоже самое мы сделаем с 2м страницами — будет 10 по 2м.

Откуда может взяться высокий systime? Всё очень просто. Пейджфолты сами по себе дорогие, но разницы между типа страниц почти нет. Но.

Дело в том, что ядро при связывании страницы ОБЯЗАНО её забить нулями, т.е. пройтись мемсетом. Очевидно, что если мы в С++ запишем в строку 1гб + 1 байт, то ядро сделает мемсет на 1гб + 4к в случае с 4к страницами и 1гб + 2м в случае с 2м. И 1гб + 1гб в случае с 1гб страницами. Этим же обусловлено и возросшее потребление памяти.

И если в софте очень много разряженной памяти, то очевидно — ему повсеместные 2м+ страницы будут вредны, а в современном железе там 1гб и более страницы.
Зарегистрируйтесь на Хабре , чтобы оставить комментарий