Глава 11. Тонкая настройка стека ввода/ вывода
Содержание
Отлично, вот мы и в самом конце своего путешествия. То, что вы читаете введение к нашей последней главе, вовсе не означает, что вы прочитали всю книгу, но я рискну это предположить. Если вы действительно следовали за нами, то я надеюсь, что ваше путешествие того стоило и заставило вас желать большего.
Возвращаясь к существу дел, предыдущие две главы были посвящены анализу производительности общего стека ввода/ вывода. Глава 9 была сосредоточена на наиболее распространённых метриках, подвергающихся риску, а также инструментам, которые способны помогать нам в выявлении узких мест производительности физических дисков. При всяком анализе производительности именно физические диски подвергаются наиболее тщательному анализу по сравнению со всеми прочими уровнями, что порой может вводить в заблуждение. Таким образом, в Главе 10 мы рассмотрели как мы способны исследовать более верхние слои общего стека ввода/ вывода, такие как файловая система и блочный уровень.
Это подводит нас к следующему этапу нашего поиска приключений. После того как мы определились с тем, какие именно элементы мешают нашей среде, какие шаги мы можем предпринять чтобы смягчить эти ограничения? Важно обладать конкретными целями при достижении желательных результатов настроек, потому как регулировки производительности это всегда компромисс вариантов. К примеру, настройка системы на низкие задержки способна снижать её общую пропускную способность. Для начала требуется определить базовый уровень производительности, а любые регулировки и наладку следует выполнять небольшими порциями. В данной главе мы будем иметь дело с различными регулировками, которые можно применять для улучшения производительности ввода/ вывода.
Вот общая схема наших действий:
-
Как использование памяти воздействует на ввод/ вывод
-
Регулировки подсистемы оперативной памяти
-
Регулировки файловой системы
-
Выбор верного диспетчера
Все представленные в этой главе материалы основываются на обсуждавшихся в предыдущих главах понятиях. Если вы следовали за нами и ознакомились с функциями каждого из уровней общей иерархии дискового ввода/ вывода, вам будет гораздо проще следовать по этой главе. Если вы обладаете пониманием управления памятью в Linux, это будет гигантским плюсом.
Все команды и примеры из этой главы не зависят от дистрибутива и могут исполняться в любой операционной системе Linux, такой как Debian, Ubuntu, Red Hat или Fedora {и Rocky Linux}. Присутствует всего лишь несколько ссылок на исходный код ядра. Если вы пожелаете выгрузить исходный код ядра, вы можете сделать это по ссылке.
Как мы уже это видели, VFS служит некой точкой входа для наших запросов на ввод/ вывод и она содержит различные виды кэширования, причём наиболее важным из них выступает страничный кэш. Основная цель страничного кэша заключается в улучшении производительности ввода/ вывода и в минимизации затрат ввода/ вывода, появляющихся по причине подкачки страниц и операций с файловой системой, что позволяет избегать ненужных походов к лежащим в основе физическим дискам. Хотя мы это и не рассматривали на своих страницах, важно обладать представлением о том как именно ядро контролирует подсистему управления памятью. Такая подсистема управления памятью носит название VMM (virtual memory manager, диспетчера виртуальной памяти). Вот некоторые из обязанностей диспетчера виртуальной памяти:
-
Управление выделением физической памяти для всех приложений пространства пользователя и пространства ядра
-
Реализация виртуальной памяти и запроса страниц
-
Установление соответствия файлов адресному пространству процесса
-
Высвобождение оперативной памяти в случае её нехватки, либо подрезки или подкачки страниц кэширования
Как это часто произносится, наилучший ввод/ вывод это тот, которого удаётся избегать. Ядро следует данному подходу и щедро выделяет свободную оперативную память, заполняя её различными видами кэширования. Чем больше объём доступной свободной памяти, тем действеннее применяемый механизм кэширования. Всё это достаточно хорошо работает в общем случае, когда приложения выполняют небольшие запросы и имеются в доступности относительно достаточный объём страниц кэширования:
И наоборот, когда объём доступной памяти ограничен, не только память кэша будет реально подрезаться, но и данные могут переноситься подкачкой страниц на диск, что в конечном итоге снижает производительность. Ядро работает в соответствии с принципом временной локальности, что означает, что к блокам, к которым недавно был выполнен доступ, вновь получить доступ можно с большей вероятностью. В целом, это хорошо для большинства ситуаций. Считывание данных из произвольной области диска может потребовать нескольких миллисекунд, в то время как доступ к тем же данным из оперативной памяти, раз они кэшированы, занимает всего несколько наносекунд. Таким образом, любой запросто обрабатываемый из кэша запрос сводит к минимуму затраты на операцию ввода/ вывода.
Слегка странно, что то, как Linux имеет дело с оперативной памятью способно оказывать существенное воздействие на производительность дисковой памяти. Как уже пояснялось, в большинстве ситуаций устанавливаемое по умолчанию поведение ядра работает хорошо. Однако, как говорится, избыточность во всём это грех. Частое кэширование может в результате приводить к ряду проблемных ситуаций:
-
Когда ядро накапливает в своём страничном еэше большой объём данных, а в конечном счёте приступает к сбросу таких данных на диск, такой диск может оставаться занятым продолжительное время по причине избыточности операций записи. Это способно оказывать неблагоприятное воздействие на общую производительность ввода/ вывода и увеличивать время отклика диска.
-
Ядро не обладает представлением о критической важности кэшированных страниц. Следовательно, оно не делает различий между важными и несущественными операциями ввода/ вывода. Ядро выбирает любой кажущийся ему подходящим блок данных планирует его для операции записи или считывания. Скажем, если приложение выполняет операции как в фоновом, так и не в фоновом режимах операции ввода/ вывода, то обычно приоритет операций не фонового режима обычно должен быть выше {Прим. пер.: а потому они и переводятся "в лоб" как приоритетные}. Однако, относящиеся к фоновому режиму ввода/ вывода задачи способны подавлять задачи не фонового режима.
Предоставляемый ядром кэш обычно позволяет приложениям получать более высокую производительность при считывании и записи данных, но применяемые кэшем алгоритмы не предназначены для конкретного алгоритма; они разрабатываются для общих целей. В большинстве ситуаций такое поведение по умолчанию будет работать на ура, однако при некоторых обстоятельствах это может приводить к обратным результатам. Для некоторых приложений с самостоятельной подготовкой, скажем, для систем управления базами данных, такой подход способен не приносить наилучших результатов. Подобные базам данным приложения лучше разбираются при варианте внутренней организации данных.Следовательно, для повышения производительности считывания и записи такие системы предпочитают обладать собственным механизмом кэширования.
Когда данные кэшируются напрямую на уровне приложения, тогда перемещения данных с диска в страничный кэш и обратно в кэш приложения составит значительные издержки и в результате к большему применению ЦПУ и оперативной памяти. В подобных ситуациях может оказаться желательным вовсе обходить страничный кэш ядра и оставлять ответственность за кэширование на самом приложении. Это носит название прямого ввода/ вывода (direct I/O).
При использовании прямого ввода/ вывода все файлы считываются и записываются непосредственно из приложения в устройство хранения, обходя страничное
кэширование ядра. Файловая система Unix (UFS, Unix
filesystem, не поддерживается в Linux) содержит прямой ввод/ вывод в качестве параметра файловой системы, который можно определять при монтировании
такой файловой системы. В Linux прямой ввод/ вывод не является параметром файловой системы и нет никакой команды, которая включала бы его. Вместо этого,
подобная ответственность за инициирование прямого ввода/ вывода лежит непосредственно на приложении. Приложение имеет возможность активировать прямой ввод/
вывод через флаг O_DIRECT
в системном выводе, например, в open ()
. Такой флаг
O_DIRECT
это всего лишь флаг состояния (представляемый через DIR),
который передаётся приложением при открытии или создании файла с тем, чтобы имелась возможность обхода страничного кэша ядра:
Нет никакого смысла в применении прямого ввода/ вывода в обычных приложениях, поскольку это может вызывать деградацию производительности. Тем не менее, для
приложений с самостоятельным кэшированием это способно приносить существенный вклад. Рекомендуемым методом является проверка состояния прямого ввода/ вывода
через приложение. Тем не менее, когда вы желаете выполнить проверку из командной строки, вы можете запустить команду lsof
для проверки значений флагов через которые происходило открытие файлов.
Для открываемых приложением через флаг O_DIRECT
файлов, столбец
FILE-FLAG получаемого вывода будет содержать флаг
DIR.
Прирост производительности за счёт прямого ввода/ вывода достигается как результат снижения затрат ЦПУ на копирование данных с диска в кэш и за счёт отказа от двойной буферизации, один раз в своём приложении и один раз в общей файловой системе.