Как стать автором
Поиск
Написать публикацию
Обновить

Комментарии 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гб и более страницы.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий