Что такое процессорное время
- Процессорное время (англ. process time или CPU time) — время, затраченное процессором компьютера на обработку задачи (программы). Распределяется между процессами в соответствии с используемым режимом операционной системы.
Процессорное время измеряется в тиках или секундах. Часто бывает полезно измерение процессорного времени в процентах от мощности процессора, который называется загрузкой процессора.
Процессорное время и загрузка процессора имеет две основных сферы применения.
Первое заключается в количественном измерении общей занятости системы. Когда загрузка процессора выше 70%, пользователь может почувствовать задержку. Такая высокая загрузка ЦП указывает на недостаточную вычислительную мощность. Либо вычислительная мощность процессора (процессоров) должна быть повышена или объём пользовательских задач должен быть уменьшен, например, путём перехода на более низкое графическое разрешение и отключение анимации.
3. Спецэффекты
Теперь остановимся на основных кейсах появления steal, с которыми мы сталкивались. Расскажу, как они вытекают из всего вышесказанного и как соотносятся с показателями на гипервизоре.
Переутилизация. Самое простое и частое: гипервизор переутилизирован. Действительно, много запущенных виртуалок, большое потребление процессора внутри них, большая конкуренция, утилизация по LA больше 1 (в нормировке по процессорным тредам). Внутри всех виртуалок всё тормозит. Steal, передаваемый с гипервизора, также растёт, надо перераспределять нагрузку или кого-то выключать. В общем, всё логично и понятно.
Паравиртуализация против одиноких инстансов. На гипервизоре одна единственная виртуалка, она потребляет небольшую его часть, но даёт большую нагрузку по вводу/выводу, например по диску. И откуда-то в ней появляется небольшой steal, до 10 % (как показывают несколько проведённых экспериментов).
Случай интересный. Steal тут появляется как раз из-за блокировок на уровне паравиртуализированных драйверов. Внутри виртуалки создаётся прерывание, обрабатывается драйвером и уходит в гипервизор. Из-за обработки прерывания на гипервизоре для виртуалки это выглядит как отправленный запрос, она готова к исполнению и ждёт процессора, но процессорного времени ей не дают. Виртуалка думает, что это время украдено.
Это происходит в момент отправки буфера, он уходит в kernel space гипервизора, и мы начинаем его ждать. Хотя, с точки зрения виртуалки, он должен сразу вернуться. Следовательно, по алгоритму расчёта steal это время считается украденным. Скорее всего, в этой ситуации могут быть и другие механизмы (например, обработка ещё каких-нибудь sys calls), но они не должны сильно отличаться.
Шедулер против высоконагруженных виртуалок. Когда одна виртуалка страдает от steal больше других, это связано как раз с шедулером. Чем сильнее процесс нагружает процессор, тем скорее шедулер его выгонит, чтобы остальные тоже могли поработать. Если виртуалка потребляет немного, она почти не увидит steal: её процесс честно сидел и ждал, надо ему давать побольше времени. Если виртуалка производит максимальную нагрузку по всем своим ядрам, её чаще выгоняют с процессора и стараются не давать много времени.
Ещё хуже, когда процессы внутри виртуалки пытаются заполучить больше процессора, потому что не справляются с обработкой данных. Тогда операционная система на гипервизоре, за счёт честной оптимизации, будет давать всё меньше процессорного времени. Этот процесс происходит лавинообразно, и steal подскакивает до небес, хотя остальные виртуалки его могут почти не замечать. И чем больше ядер, тем хуже попавшей под раздачу машине. Короче говоря, больше всего страдают высоконагруженные виртуалки со множеством ядер.
Низкий LA, но есть steal. Если LA примерно 0,7 (то есть, гипервизор, кажется недозагружен), но внутри отдельных виртуалок наблюдается steal:
- Уже описанный выше вариант с паравиртуализацией. Виртуалка может получать метрики, указывающие на steal, хотя у гипервизора всё хорошо. По результатам наших экспериментов, такой вариант steal не превышает 10 % и не должен оказывать существенного влияния на производительность приложений внутри виртуалки.
- Неверно считается параметр LA. Точнее, в каждый конкретный момент он считается верно, но при усреднении за одну минуту получается заниженным. Например, если одна виртуалка на треть гипервизора потребляет все свои процессоры ровно полминуты, то LA за минуту на гипервизоре будет 0,15; четыре такие виртуалки, работающие одновременно, дадут 0,6. А то, что полминуты на каждой из них был дикий steal под 25 % по показателю LA, уже не вытащить.
- Опять же, из-за шедулера, решившего, что кто-то слишком много ест, и пусть этот кто-то подождёт. А я пока попереключаю контекст, пообрабатываю прерывания и займусь другими важными системными вещами. В итоге одни виртуалки не видят никаких проблем, а другие испытывают серьезную деградацию производительности.
1. Что такое steal
Итак, steal — это метрика, указывающая на нехватку процессорного времени для процессов внутри виртуальной машины. Как описано в патче ядра KVM, steal — это время, в течение которого гипервизор выполняет другие процессы на хостовой ОС, хотя он поставил процесс виртуальной машины в очередь на выполнение. То есть, steal считается как разница между временем, когда процесс готов выполниться, и временем, когда процессу выделены процессорное время.
Метрику steal ядро виртуальной машины получает от гипервизора. При этом гипервизор не уточняет, какие именно другие процессы он выполняет, просто «пока занят, тебе времени уделить не могу». На KVM поддержка подсчёта steal добавлена в патчах. Ключевых моментов здесь два:
- Виртуальная машина узнаёт о steal от гипервизора. То есть, с точки зрения потерь, для процессов на самой виртуалке это непрямое измерение, которое может быть подвержено различным искажениям.
- Гипервизор не делится с виртуалкой информацией о том, чем другим он занят — главное, что он не уделяет время ей. Из-за этого сама виртуалка не может выявить искажения в показателе steal, которые можно было бы оценить по характеру конкурирующих процессов.
GetProcessTimes( )
На Windows и Cygwin (UNIX-подобная среда и интерфейс командной строки для Windows), функция GetProcessTimes( ) заполняет структуру FILETIME процессорным временем, использованным процессом, а функция FileTimeToSystemTime( ) конвертирует структуру FILETIME в структуру SYSTEMTIME, содержащую пригодное для использования значение времени.
Доступность GetProcessTimes( ): Cygwin, Windows XP и более поздние версии.
Получение процессорного времени:
2.4. Как мониторить steal?
Мониторить steal внутри виртуальной машины, как и любую другую процессорную метрику, просто: можно пользоваться любым средством съема метрик процессора. Главное, чтобы виртуалка была на Linux. Windows почему-то такую информацию своим пользователям не предоставляет. :(
Вывод команды top: детализация нагрузки на процессор, в крайней правой колонке — steal
Сложность возникает при попытке получить эту информацию с гипервизора. Можно попробовать спрогнозировать steal на хостовой машине, например, по параметру Load Average (LA) — усредненного значения количества процессов, ожидающих в очереди на выполнение. Методика подсчёта этого параметра непростая, но в целом, если пронормированный по количеству потоков процессора LA больше 1, это говорит о том, что сервер с линуксом чем-то перегружен.
Чего же ждут все эти процессы? Очевидный ответ — процессора. Но ответ не совсем правильный, потому что иногда процессор свободен, а LA зашкаливает. Вспомните, как отваливается NFS и как при этом растёт LA. Примерно так же может быть и с диском, и с другими устройством ввода/вывода. Но на самом деле, процессы могут ожидать окончания любой блокировки, как физической, связанной с устройством ввода/вывода, так и логической, например мьютекса. Туда же относятся блокировки на уровне железа (того же ответа от диска), или логики (так называемых блокировочных примитивов, куда входит куча сущностей, mutex adaptive и spin, semaphores, condition variables, rw locks, ipc locks. ).
Ещё одна особенность LA в том, что оно считается как среднее значение по операционной системе. К примеру, 100 процессов конкурируют за один файл, и тогда LA=50. Такое большое значение, казалось бы, говорит о том, что операционке плохо. Но для иного криво написанного кода это может быть нормальным состоянием, при том, что плохо только ему, а другие процессы в операционке не страдают.
Из-за этого усреднения (причём не меньше, чем за минуту), определение чего-то бы то ни было по показателю LA — не самое благодарное занятие, с весьма неопределёнными результатами в конкретных случаях. Если вы попытаетесь разобраться, то обнаружите, что в статьях на Википедии и прочих доступных ресурсах описаны только самые простые кейсы, без глубокого объяснения процесса. Всех интересующихся отправляю, опять же, сюда, к Brendann Gregg — далее по ссылкам. Кому лень на английском — перевод его популярной статьи про LA.
2.2. Типы виртуализации на KVM
Вообще говоря, есть три типа виртуализации, и все они поддерживаются KVM. От типа виртуализации может зависеть механизм возникновения steal.
Трансляция. В этом случае работа операционной системы виртуальной машины с физическими устройствами гипервизора происходит примерно так:
- Гостевая операционная система посылает своему гостевому устройству команду.
- Драйвер гостевого устройства принимает команду, формирует запрос для BIOS устройства и отправляет её в гипервизор.
- Процесс гипервизора производит трансляцию команды в команду для физического устройства, делая её, в том числе, более безопасной.
- Драйвер физического устройства принимает модифицированную команду и отправляет её уже в само физическое устройство.
- Результаты выполнения команд идут обратно по тому же пути.
Аппаратная виртуализация. В этом случае устройство на аппаратном уровне понимает команды из операционной системы. Это самый быстрый и хороший способ. Но, к сожалению, он поддерживается далеко не всеми физическими устройствами, гипервизорами и гостевыми операционками. На текущий момент основные устройства, которые поддерживают аппаратную виртуализацию, — это процессоры.
Паравиртуализация (paravirtualization). Самый распространённый вариант виртуализации устройств на KVM и вообще самый распространенный режим виртуализации для гостевых операционных систем. Особенность его в том, что работа с некоторыми подсистемами гипервизора (например, с сетевым или дисковым стеком) или выделение страниц памяти происходит с использованием API гипервизора, без трансляции низкоуровневых команд. Недостаток этого способа виртуализации — необходимость модификации ядра гостевой операционной системы, чтобы оно могло взаимодействовать с гипервизором с помощью этого API. Но обычно это решается за счет установки специальных драйверов на гостевую операционную систему. В KVM это API называется virtio API.
При паравиртуализации, по сравнению с трансляцией, путь до физического устройства значительно сокращается за счёт отправки команд напрямую из виртуальной машины в процесс гипервизора на хосте. Это позволяет ускорить выполнение всех инструкций внутри виртуальной машины. В KVM за это отвечает virtio API, который работает только для определенных устройств, вроде сетевого или дискового адаптера. Именно поэтому внутрь виртуальных машин ставятся virtio-драйверы.
Обратная сторона такого ускорения — не все процессы, которые выполняются внутри виртуалки, остаются внутри неё. Это создаёт некоторые спецэффекты, которые могут привести к появлению на steal. Подробное изучение этого вопроса рекомендую начать с An API for virtual I/O: virtio.
Связанные понятия
Переключение контекста (англ. context switch) — в многозадачных ОС и средах - процесс прекращения выполнения процессором одной задачи (процесса, потока, нити) с сохранением всей необходимой информации и состояния, необходимых для последующего продолжения с прерванного места, и восстановления и загрузки состояния задачи, к выполнению которой переходит процессор.
Защита памяти (англ. Memory protection) — это способ управления правами доступа к отдельным регионам памяти. Используется большинством многозадачных операционных систем. Основной целью защиты памяти является запрет доступа процессу к той памяти, которая не выделена для этого процесса. Такие запреты повышают надёжность работы как программ, так и операционных систем, так как ошибка в одной программе не может повлиять непосредственно на память других приложений. Следует различать общий принцип защиты.
А́дресное пространство (англ. address space) — совокупность всех допустимых адресов каких-либо объектов вычислительной системы — ячеек памяти, секторов диска, узлов сети и т. п., которые могут быть использованы для доступа к этим объектам при определенном режиме работы (состоянии системы).
Код операции, операционный код, опкод — часть машинного языка, называемая инструкцией и определяющая операцию, которая должна быть выполнена.
Счётчик кома́нд (также PC = program counter, IP = instruction pointer, IAR = instruction address register, СЧАК = счётчик адресуемых команд) — регистр процессора, который указывает, какую команду нужно выполнять следующей.
Иерархия компьютерной памяти — концепция построения взаимосвязи классов разных уровней компьютерной памяти на основе иерархической структуры.
Основная область памяти (Основная память, англ. Conventional memory) занимает первые 640 Кбайт оперативной памяти в IBM PC-совместимых компьютерах. В эту область загружается таблица векторов прерываний (занимает 1 Кбайт), некоторые данные из BIOS (например, буфер клавиатуры), различные 16-битные программы DOS. Для них 640 Кбайт являются барьером.
Планирование выполнения задач — одна из ключевых концепций в многозадачности и многопроцессорности как в операционных системах общего назначения, так и в операционных системах реального времени. Планирование заключается в назначении приоритетов процессам в очереди с приоритетами. Программный код, выполняющий эту задачу, называется планировщиком (англ. task switcher, scheduler).
Вытесняющая многозадачность (приоритетная многозадачность, англ. preemptive multitasking, дословно упреждающая многозадачность) — это вид многозадачности, при которой операционная система принимает решение о переключении между задачами по истечении некоего кванта времени.
Разделяемая память (англ. Shared memory) является самым быстрым средством обмена данными между процессами.
Загрузчик операционной системы — системное программное обеспечение, обеспечивающее загрузку операционной системы непосредственно после включения компьютера (процедуры POST) и начальной загрузки.
Буфер ассоциативной трансляции (англ. Translation lookaside buffer, TLB) — это специализированный кэш центрального процессора, используемый для ускорения трансляции адреса виртуальной памяти в адрес физической памяти.
Механизм копирования при записи (англ. Copy-On-Write, COW) используется для оптимизации многих процессов, происходящих в операционной системе, таких как, например, работа с оперативной памятью или файлами на диске (пример — ext3cow).
Дамп памяти (англ. memory dump; в Unix — core dump) — содержимое рабочей памяти одного процесса, ядра или всей операционной системы. Также может включать дополнительную информацию о состоянии программы или системы, например значения регистров процессора и содержимое стека. Многие операционные системы позволяют сохранять дамп памяти для отладки программы. Как правило, дамп памяти процесса сохраняется автоматически, когда процесс завершается из-за критической ошибки (например, из-за ошибки сегментации.
Неблокирующая синхронизация — подход в параллельном программировании на симметрично-многопроцессорных системах, в котором принят отказ от традиционных примитивов блокировки, таких, как семафоры, мьютексы и события. Разделение доступа между потоками идёт за счёт атомарных операций и специальных, разработанных под конкретную задачу, механизмов блокировки.
Тест производительности, бенчмарк (англ. benchmark) — контрольная задача, необходимая для определения сравнительных характеристик производительности компьютерной системы. Иногда бенчмарками также называются программы, которые тестируют время автономной работы ноутбуков и карманных персональных компьютеров, радиус действия беспроводной сети, пропускную способность каналов передачи данных, амплитудно-частотную характеристику звукового тракта и другие доступные для измерения характеристики, напрямую.
Адрес — символ или группа символов, которые идентифицируют регистр, отдельные части памяти или некоторые другие источники данных, либо место назначения информации.
В компьютерной операционной системе, легковесный процесс является средством достижения многозадачности, в традиционном понимании этого термина. В Unix System V и Solaris, легковесный процесс работает в пространстве пользователя поверх одного потока выполнения ядра, разделяет виртуальное адресное пространство и системные ресурсы потока выполнения с другими легковесными процессами, в рамках того же процесса. Несколько потоков пользовательского уровня, управляемые с помощью библиотеки потоков, могут.
Страничная память — способ организации виртуальной памяти, при котором единицей отображения виртуальных адресов на физические является регион постоянного размера (т. н. страница). Типичный размер страницы — 4096 байт, для некоторых архитектур — до 128 КБ.
Hardware Abstraction Layer (HAL, Слой аппаратных абстракций) — слой абстрагирования, реализованный в программном обеспечении, находящийся между физическим уровнем аппаратного обеспечения и программным обеспечением, запускаемом на этом компьютере. HAL предназначен для скрытия различий в аппаратном обеспечении от основной части ядра операционной системы, таким образом, чтобы большая часть кода, работающая в режиме ядра, не нуждалась в изменении при её запуске на системах с различным аппаратным обеспечением.
Снимок файловой системы, или снапшот, или снепшот (от англ. snapshot — мгновенный снимок), — моментальный снимок, копия файлов и каталогов файловой системы на определённый момент времени.
Файловый дескриптор — это неотрицательное целое число. Когда создается новый поток ввода-вывода, ядро возвращает процессу, создавшему поток ввода-вывода, его файловый дескриптор.
Объе́ктный мо́дуль (также — объектный файл, англ. object file) — файл с промежуточным представлением отдельного модуля программы, полученный в результате обработки исходного кода компилятором. Объектный файл содержит в себе особым образом подготовленный код (часто называемый двоичным или бинарным), который может быть объединён с другими объектными файлами при помощи редактора связей (компоновщика) для получения готового исполнимого модуля либо библиотеки.
Журналирование (англ. logging) — форма автоматической записи в хронологическом порядке операций в информационных технологиях, процесс записи информации о происходящих в рамках какого-либо процесса с некоторым объектом событиях, например, в файл регистрации или в базу данных. В некоторых программный комплексах используется термин "аудит", что является не верным, поскольку аудит подразумевает сравнение чего-то с чем-то, чего-то на предмет соответствия, например, требованиям, иными словами это корреляционный.
Сегментная адресация памяти — схема логической адресации памяти компьютера в архитектуре x86. Линейный адрес конкретной ячейки памяти, который в некоторых режимах работы процессора будет совпадать с физическим адресом, делится на две части: сегмент и смещение. Сегментом называется условно выделенная область адресного пространства определённого размера, а смещением — адрес ячейки памяти относительно начала сегмента. Базой сегмента называется линейный адрес (адрес относительно всего объёма памяти.
Разрядность числа в математике — количество числовых разрядов, необходимых для записи этого числа в той или иной системе счисления. Разрядность числа иногда также называется его длиной.
В информатике, блокировка — механизм синхронизации, позволяющий обеспечить исключительный доступ к разделяемому ресурсу между несколькими потоками. Блокировки — это один из способов обеспечить политику управления распараллеливанием.
В информатике термин инструкция обозначает одну отдельную операцию процессора, определённую системой команд. В более широком понимании, «инструкцией» может быть любое представление элемента исполнимой программы, такой как байт-код.
Монтирование файловой системы — системный процесс, подготавливающий раздел диска к использованию операционной системой.
Высокая доступность (англ. high availability) — характеристика технической системы, разработанной для избежания невыполненного обслуживания путём уменьшения или управления сбоями и минимизацией времени плановых простоев. Высокая доступность ожидается от систем жизнеобеспечения, здравоохранения и систем, от которых зависит благополучие общества в целом и экономического благополучия отдельных организаций.
Начальная загрузка — сложный и многошаговый процесс запуска компьютера. Загрузочная последовательность — это последовательность действий, которые должен выполнить компьютер для запуска операционной системы (точнее, загрузчика), независимо от типа установленной ОС.
Кэш или кеш (англ. cache, от фр. cacher — «прятать»; произносится — «кэш») — промежуточный буфер с быстрым доступом к нему, содержащий информацию, которая может быть запрошена с наибольшей вероятностью. Доступ к данным в кэше осуществляется быстрее, чем выборка исходных данных из более медленной памяти или удаленного источника, однако её объём существенно ограничен по сравнению с хранилищем исходных данных.
Точка монтирования (англ. mount point) — это каталог или файл, с помощью которого обеспечивается доступ к новой файловой системе, каталогу или файлу.
Уровень абстракции — один из способов сокрытия деталей реализации определенного набора функциональных возможностей. Применяется для управления сложностью проектируемой системы при декомпозиции, когда система представляется в виде иерархии уровней абстракции.
Реальный режим (или режим реальных адресов; англ. real-address mode) — режим работы процессоров архитектуры x86, при котором используется сегментная адресация памяти (адрес ячейки памяти формируется из двух чисел: сдвинутого на 4 бита адреса начала сегмента и смещения ячейки от начала сегмента; любому процессу доступна вся память компьютера). Изначально режим не имел названия, был назван «реальным» только после создания процессоров 80286, поддерживающих режим, названный «защищённым» (режим назван.
Обработчик прерываний (или процедура обслуживания прерываний) — специальная процедура, вызываемая по прерыванию для выполнения его обработки. Обработчики прерываний могут выполнять множество функций, которые зависят от причины, которая вызвала прерывание.
Планировщик задач — программа (служба или демон), часто называемая сервисом операционной системы, которая запускает другие программы в зависимости от различных критериев, как, например.
Подкачка страниц (англ. paging; иногда используется термин swapping от swap, /swɔp/) — один из механизмов виртуальной памяти, при котором отдельные фрагменты памяти (обычно неактивные) перемещаются из ОЗУ во вторичное хранилище (жёсткий диск или другой внешний накопитель, такой как флеш-память), освобождая ОЗУ для загрузки других активных фрагментов памяти. Такими фрагментами в современных ЭВМ являются страницы памяти.
Модуль ядра, загружаемый модуль ядра (англ. loadable kernel module, LKM) — объект, содержащий код, который расширяет функциональность запущенного или т. н. базового ядра ОС. Большинство текущих систем, основанных на Unix, поддерживают загружаемые модули ядра, хотя они могут называться по-разному (например, kernel loadable module в FreeBSD и kernel extension в Mac OS X).
Кома́нда — это указание компьютерной программе действовать как некий интерпретатор для решения задачи. В более общем случае, команда — это указание некоему интерфейсу командной строки, такому как shell.
Защищённый режим (режим защищённой виртуальной адресации) — режим работы x86-совместимых процессоров. Частично был реализован уже в процессоре 80286, но там существенно отличался способ работы с памятью, так как процессоры ещё были 16-битными и не была реализована страничная организация памяти. Первая 32-битная реализация защищённого режима — процессор Intel 80386. Применяется в совместимых процессорах других производителей. Данный режим используется в современных многозадачных операционных системах.
Стандартные потоки ввода-вывода в системах типа UNIX (и некоторых других) — потоки процесса, имеющие номер (дескриптор), зарезервированный для выполнения некоторых «стандартных» функций. Как правило (хотя и не обязательно), эти дескрипторы открыты уже в момент запуска задачи (исполняемого файла).
В информатике бу́фер (англ. buffer), мн. ч. бу́феры — это область памяти, используемая для временного хранения данных при вводе или выводе. Обмен данными (ввод и вывод) может происходить как с внешними устройствами, так и с процессами в пределах компьютера. Буферы могут быть реализованы в аппаратном или программном обеспечении, но подавляющее большинство буферов реализуется в программном обеспечении. Буферы используются, когда существует разница между скоростью получения данных и скоростью их обработки.
Балансировка нагрузки отличается от физического соединения тем, что балансировка нагрузки делит трафик между сетевыми интерфейсами на сетевой сокет (модель OSI уровень 4) основе, в то время как соединение канала предполагает разделение трафика между физическими интерфейсами на более низком уровне, либо в пакет (модель OSI уровень 3) или по каналу связи (модель OSI уровень 2); Основы с, как протокол соединения кратчайшего пути.
Сравнение с обменом (англ. compare and set, compare and swap, CAS) — атомарная инструкция, сравнивающая значение в памяти с одним из аргументов, и в случае успеха записывающая второй аргумент в память. Поддерживается в семействах процессоров x86, Itanium, Sparc и других.
В информатике и теории автоматов состояние цифровой логической схемы или компьютерной программы является техническим термином для всей хранимой информации, к которой схема или программа в данный момент времени имеет доступ. Вывод данных цифровой схемы или компьютерной программы в любой момент времени полностью определяется его текущими входными данными и его состоянием.
Кросс-компиля́тор (англ. cross compiler) — компилятор, производящий исполняемый код для платформы, отличной от той, на которой исполняется сам кросс-компилятор. Такой инструмент бывает полезен, когда нужно получить код для платформы, экземпляров которой нет в наличии, или в случаях когда компиляция на целевой платформе невозможна или нецелесообразна (например, это касается мобильных систем или микроконтроллеров с минимальным объёмом памяти).
Масштаби́руемость (англ. scalability) — в электронике и информатике означает способность системы, сети или процесса справляться с увеличением рабочей нагрузки (увеличивать свою производительность) при добавлении ресурсов (обычно аппаратных).
Атомарная (атом от греч. atomos — неделимое) операция — операция, которая либо выполняется целиком, либо не выполняется вовсе; операция, которая не может быть частично выполнена и частично не выполнена.
После запуска я получил много вопросов о том, как именно учитываются ресурсы в облаке. Некоторые интуитивно понимают, что такое «час процессорного времени» но есть и те, кто хочет подробного объяснения. Поскольку в общем анонсе подробные объяснения заняли бы много места, я вынес его в отдельный топик. Заодно, такой формат позволит более подробно описать, как Зен и виртуальные машины взаимодействуют. Уровень этого текста научно-популярный, то есть я не буду вдаваться в дебри кольцевых буферов, маскировки событий, «кредитного планировщика» и т.д., вместо этого я попробую рассказать относительно человеческим языком о том, как гипервизор управляет гостевыми машинами.
Что такое «процессорное время»? Сначала мы его хотели назвать более привычным «машинное время», благо, такой термин использовался во времена мейнфреймов, когда идея разделения машинного времени только-только зародилась, но вовремя остановились. Машинное время тех лет подразумевало все ресурсы, которые использовались машиной, а в нашем случае речь идёт именно о процессоре, так как каждый ограниченный ресурс учитывается раздельно.
Итак, что такое «процессорное время» и как может оказаться, что у одной виртуальной машины его насчитывается 4 часа в сутки, а у другой накручивает 30 «часов» за часов десять?
Облако Селектел работает под управлением Xen, точнее, Xen Cloud Platform, в котором гипервизором выступает Xen.
В Xen есть понятие «планировщик доменов». Оставляя в стороне разницу между доменом и виртуальной машиной (домен — запущенная конкретная виртуальная машина, когда виртуальная машина перезагружается, получается новый домен, когда виртуальная машина выключена, домена нет, а сама машина — есть), можно считать, что этот планировщик виртуальных машин. Те, кто знаком с работой современных ОС, наверное уже догадались, что планировщик доменов подозрительно похож на планировщик процессов в этих самых современных ОС.
Как выглядит работа виртуальной машины?
Происходит какое-то событие: приходит сетевой пакет, срабатывает таймер, сигнал о перезагрузке и т.д. Xen отдаёт процессору команду начать выполнять виртуальную машину (точнее, домен, но в пределах этого объяснения будем считать эти понятия эквивалентными). Ядро виртуальной машины обрабатывает событие, из-за которого его разбудили. Если надо, то оно вызывает пользовательские процессы. Процессы делают свою работу и говорят ядру «всё, закончили». Ядро разбирается со своими вопросами и так же говорят гипервизору (Xen'у) — «всё, я закончило». После этого Xen останавливает выполнение машины. Она просто ничего не делает в буквальном смысле слова. Машина пребывает в таком состоянии до момента, пока не наступает новое событие.
В современных машинах эти события наступают с огромной скоростью — например, если вы загружаете файл со скоростью 5Мб/с, то это (при размере пакета в 1500 байт) — это больше 3000 пакетов в секунду. Каждый пакет — это отдельное прерывание (точнее, в Xen'е всё хитрее, там несколько вызовов объединяются в один, так что иногда виртуальная машина оказывается чуть-чуть быстрее, чем даже на голом железе). И каждое такое событие — это пробуждение машины. Но скорость современных процессоров такова, что после каждого такого вызова ядро виртуальной машины и процессы (например, апач или nginx) успевают отработать и заснуть. Отдача статики на 5Мб/с — это очень низкая нагрузка, примерно 1-2% одного ядра процессора, так что, не смотря на то, что события происходят с интервалом в 300 микросекунд, виртуальная машина отрабатывает за 3-6 микросекунд и оставшиеся 294-296 микросекунд успевает сказать гипервизору «я всё» и заснуть. А через микросекунды снова проснуться, отработать и снова заснуть. Так и получается, что виртуальная машина большую часть времени просто спит.
Вот именно моменты времени, когда виртуальная машина работает и являются «процессорным временем».
Вдумчивый читатель может спросить — а что, если виртуальная машина не скажет «я всё»? Если бы у нас была Windows 3.11, где была кооперативная многозадачность, то это бы привело к тому, что остальные не получили бы полагающееся им время. Но в Xen'е используется вытесняющая многозадачность — и виртуальная машина, которая слишком жадно работает, будет просто приостановлена. Принудительно. А потом снова продолжена.
Обычно такая ситуация происходит в условиях нехватки процессорного времени, и авторы Xen'а потратили тысячи часов, разрабатывая справедливые планировщики, которые в условиях перегруженности процессора решают задачу распределения времени так, чтобы все продолжили работать более-менее равномерно.
Однако, в реальных условиях современного хостинга, скорость работы процессора так высока, что процессор — наименее востребованный и самый простаивающий ресурс и в 99% случаев конкуренции за ресурсы вообще не возникает.
Процессорным временем является время, в течение которого работает виртуальная машина. Если она работала 2с за час, то это так и есть. Если 40 минут — значит, сорок минут. Процессорное время никак не связано с «реальным» временем на часах. Так как Xen командует виртуальными машинами, то Xen с точностью до наносекунды знает, сколько времени отработала каждая машина. Мы это значение округляем до микросекунд (чтобы избежать проблемы с int64), а в биллинге фиксируются лишь целые секунды (дробная часть копится, пока не набежит на секунду). Деньги же за процессорное время списываются как только набежит хотя бы на 1 копейку (в настоящий момент это 36 секунд). Для сравнения — загрузка виртуальной машины съедает примерно 3-6 секунд машинного времени, а это самая «дорогостоящая» операция в жизненном цикле домена.
Если загрузка машины меньше 100% (то есть она потребляет меньше часа процессорного времени в час), то, формально, можно было бы ограничиться одним ядром.
Но, помните, что я выше сказал про одновременное обслуживание клиентов? Несколько ядер обеспечивают большую «отзывчивость» на запросы, хотя, возможно, одно ядро вполне бы справилось, пусть и ценой увеличения времени ответа на запрос.
Кстати, это ответ и ещё на один вопрос: влияет ли количество ядер на затрачиваемое процессорное время? Ответ — нет, если эти ядра простаивают, то процессорное время не используется. А большое число ядер лишь уменьшает задержку при обслуживании одновременных запросов от нескольких клиентов.
Ну и вдогонку немного о том, как нужно понимать понятия «отдаёт время», «выделяет время». Процессор — железка кремниевая и бестолковая. Всё, что может делать процессор — это выполнять код (ну и реагировать на прерывания). И процессор не особо разбирается «домен виртуальной машины» это, или запущенная копия angry birds. Таким образом, понятие «домен», «гипервизор» — это в каком-то смысле условности. Когда мы говорим «виртуальная машина работала 10 мс», мы на самом деле подразумеваем фразу «процессор исполнял код виртуальной машины 10 мс». Когда мы говорим «гипервизор вытеснил виртуальную машину», мы на самом деле подразумеваем «по прерыванию таймера процессор обновил счётчик времени, сохранил контекст процесса и передал управление в другое место, отличное от места, где его прервал таймер». Подобный перевод объекта (код) в субъект, обладающий способностью к действию, сильно упрощает объяснение — у каждой программы есть алгоритм поведения, и проще сказать, что «программа ведёт себя так-то», вместо того, чтобы говорить «процессор, исполняя программу, делает то-то и то-то».
Теперь немного о том, что сколько кушает. В начале статьи — график весьма нагруженного сервера, который держит на себе asterisk с звонками целой компании, веб-сервер, сбор статистики с машрутизаторов и т.д. Внизу — сайт с примерно 5000 уникальных посетителей в день. Это к вопросу о том, сильно ли используют процессор современные серверные приложения (циан на графиках — простаивающий процессор).
КДПВ
От переводчика:
Большинство моих знакомых для измерения времени в разного вида бенчмарках в С++ используют chrono или, в особо запущенных случаях, ctime . Но для бенчмаркинга гораздо полезнее замерять процессорное время. Недавно я наткнулся на статью о кроссплатформенном замере процессорного времени и решил поделиться ею тут, возможно несколько увеличив качество местных бенчмарков.
P.S. Когда в статье написано "сегодня" или "сейчас", имеется ввиду "на момент выхода статьи", то есть, если я не ошибаюсь, март 2012. Ни я, ни автор не гарантируем, что это до сих пор так.
P.P.S. На момент публикации оригинал недоступен, но хранится в кэше Яндекса
Функции API, позволяющие получить процессорное время, использованное процессом, отличаются в разных операционных системах: Windows, Linux, OSX, BSD, Solaris, а также прочих UNIX-подобных ОС. Эта статья предоставляет кросс-платформенную функцию, получающую процессорное время процесса и объясняет, какие функции поддерживает каждая ОС.
Процессорное время увеличивается, когда процесс работает и потребляет циклы CPU. Во время операций ввода-вывода, блокировок потоков и других операций, которые приостанавливают работу процессора, процессорное время не увеличивается пока процесс снова не начнет использовать CPU.
Разные инструменты, такие как ps в POSIX, Activity Monitor в OSX и Task Manager в Windows показывают процессорное время, используемое процессами, но часто бывает полезным отслеживать его прямо из самого процесса. Это особенно полезно во время бенчмаркинга алгоритмов или маленькой части сложной программы. Несмотря на то, что все ОС предоставляют API для получения процессорного времени, в каждой из них есть свои тонкости.
Функция getCPUTime( ) , представленная ниже, работает на большинстве ОС (просто скопируйте код или скачайте файл getCPUTime.c). Там, где это нужно, слинкуйтесь с librt, чтобы получить POSIX-таймеры (например, AIX, BSD, Cygwin, HP-UX, Linux и Solaris, но не OSX). В противном случае, достаточно стандартных библиотек.
2.3. «Справедливый» шедулинг
Виртуалка на гипервизоре является, фактически, обычным процессом, который подчиняется законам шедулинга (распределения ресурсов между процессами) в ядре Linux, поэтому рассмотрим его подробнее.
В Linux используется так называемый CFS, Completely Fair Scheduler, начиная с ядра 2.6.23 ставший диспетчером по умолчанию. Чтобы разобраться с этим алгоритмом, можно почитать Linux Kernel Architecture или исходники. Суть CFS заключается в распределении процессорного времени между процессами в зависимости от длительности их выполнения. Чем больше процессорного времени требует процесс, тем меньше этого времени он получает. Это гарантирует «честное» выполнение всех процессов — чтобы один процесс не занимал все процессоры постоянно, и остальные процессы тоже могли выполняться.
Иногда такая парадигма приводит к интересным артефактам. Давние пользователи Linux наверняка помнят замирание обычного текстового редактора на десктопе во время запуска ресурсоемких приложений типа компилятора. Так получалось, потому что нересурсоемкие задачи десктопных приложений конкурировали с задачами, активно потребляющими ресурсы, такими как компилятор. CFS считает, что это нечестно, поэтому периодически останавливает текстовый редактор и даёт процессору обработать задачи компилятора. Это поправили с помощью механизма sched_autogroup, но остались многие другие особенности распределения процессорного времени между задачами. Собственно, это рассказ не про то, как всё плохо в CFS, а попытка обратить внимание на то, что «честное» распределение процессорного времени — не самая тривиальная задача.
Ещё один важный момент в шедулере — preemption. Это нужно, чтобы выгнать зажравшийся процесс с процессора и дать поработать другим. Процесс изгнания называется context switching, переключение контекста процессора. При этом сохраняется весь контекст таски: состояние стека, регистры и прочее, после чего процесс отправляется ждать, а на его место встает другой. Это дорогая операция для ОС, и используется она редко, но по сути ничего плохого в ней нет. Частое переключение контекста может говорить о проблеме в ОС, но обычно оно идет непрерывно и ни на что особенно не указывает.
Такой длинный рассказ нужен для объяснения одного факта: чем больше ресурсов процессора пытается потребить процесс в честном шедулере Linux, тем быстрее он будет остановлен, чтобы другие процессы тоже могли поработать. Правильно это или нет — сложный вопрос, который при разных нагрузках решается по-разному. В Windows до недавнего времени шедулер был ориентирован на приоритетную обработку десктопных приложений, из-за чего могли зависать фоновые процессы. В Sun Solaris было пять различных классов шедулеров. Когда запустили виртуализацию, добавили шестой, Fair share scheduler, потому что предыдущие пять работали с виртуализацией Solaris Zones неадекватно. Подробное изучение этого вопроса рекомендую начать с книг вроде Solaris Internals: Solaris 10 and OpenSolaris Kernel Architecture или Understanding the Linux Kernel.
clock_gettme( )
На большинстве POSIX-совместимых ОС, clock_gettime( ) (смотри мануалы к AIX, BSD, HP-UX, Linux и Solaris) предоставляет самое точное значение процессорного времени. Первый аргумент функции выбирает "clock id", а второй это структура timespec , заполняемая использованным процессорным временем в секундах и наносекундах. Для большинства ОС, программа должна быть слинкована с librt.
Однако, есть несколько тонкостей, затрудняющих использование этой функции в кросс-платформенном коде:
- Функция является опциональной частью стандарта POSIX и доступна только если _POSIX_TIMERS определен в значением больше 0. На сегодняшний день, AIX, BSD, HP-UX, Linux и Solaris поддерживают эту функцию, но OSX не поддерживает.
- Структура timespec , заполняемая функцией clock_gettime( ) может хранить время в наносекундах, но точность часов отличается в разных ОС и на разных системах. Функция clock_getres( ) возвращает точность часов, если она вам нужна. Эта функция, опять-таки, является опциональной частью стандарта POSIX, доступной только если _POSIX_TIMERS больше нуля. На данный момент, AIX, BSD, HP-UX, Linux и Solaris предоставляют эту функцию, но в Solaris она не работает. определяет имена нескольких стандартных значений "clock id", включая CLOCK_PROCESS_CPUTIME_ID , чтобы получить процессорное время процесса. Тем не менее, сегодня BSD и HP-UX не имеют этого id, и взамен определяют собственный id CLOCK_VIRTUAL для процессорного времени. Чтобы запутать все ещё больше, Solaris определяет оба этих, но использует CLOCK_VIRTUAL для процессорного времени потока, а не процесса.
- Вместо того, чтобы использовать одну из констант, объявленных выше, функция clock_getcpuclockid( ) возвращает таймер для выбранного процесса. Использование процесса 0 позволяет получить процессорное время текущего процесса. Однако, это ещё одна опциональная часть стандарта POSIX и доступна только если _POSIX_CPUTIME больше 0. На сегодняшний день, только AIX и Linux предоставляют эту функцию, но линуксовские include-файлы не определяют _POSIX_CPUTIME и функция возвращает ненадёжные и несовместимые с POSIX результаты.
- Функция clock_gettime( ) может быть реализована с помощью регистра времени процессора. На многопроцессорных системах, у отдельных процессоров может быть несколько разное восприятие времени, из-за чего функция может возвращать неверные значения, если процесс передавался от процессора процессору. На Linux, и только на Linux, это может быть обнаружено, если clock_getcpuclockid( ) возвращает не-POSIX ошибку и устанавливает errno в ENOENT . Однако, как замечено выше, на Linux clock_getcpuclockid( ) ненадежен.
Доступность clock_gettime( ): AIX, BSD, Cygwin, HP-UX, Linux и Solaris. Но clock id на BSD и HP-UX нестандартные.
Доступность clock_getres( ): AIX, BSD, Cygwin, HP-UX и Linux, но не работает Solaris.
Доступность clock_getcpuclockid( ): AIX и Cygwin, не недостоверна на Linux.
Получение процессорного времени:
clock( )
На всех UNIX-подобных ОС, очень старая функция clock( ) возвращает процессорное время процесса в тиках, а макрос CLOCKS_PER_SEC количество тиков в секунду.
Заметка: Возвращенное процессорное время включает в себя время проведенное в user mode И в system mode от имени процесса.
Внимание: Хотя изначально CLOCKS_PER_SEC должен был возвращать значение, зависящее от процессора, стандарты C ISO C89 и C99, Single UNIX Specification и стандарт POSIX требуют, чтобы CLOCKS_PER_SEC имел фиксированное значение 1,000,000, что ограничивает точность функции микросекундами. Большинство ОС соответствует этим стандартам, но FreeBSD, Cygwin и старые версии OSX используют нестандартные значения.
Внимание: На AIX и Solaris, функция clock( ) включает процессорное время текущего процесса И и любого завершенного дочернего процесса для которого родитель выполнил одну из функций wait( ) , system( ) или pclose( ) .
Внимание: В Windows, функция clock( ) поддерживается, но возвращает не процессорное, а реальное время.
Доступность clock( ): AIX, BSD, Cygwin, HP-UX, Linux, OSX и Solaris.
Получение процессорного времени:
Использование
Чтобы замерить процессорное время алгоритма, вызовите getCPUTime( ) до и после запуска алгоритма, и выведите разницу. Не стоит предполагать, что значение, возвращенное при единичном вызове функции, несет какой-то смысл.
Каждая ОС предоставляет один или несколько способов получить процессорное время. Однако некоторые способы точнее остальных.
OS | clock | clock_gettime | GetProcessTimes | getrusage | times |
---|---|---|---|---|---|
AIX | yes | yes | yes | yes | |
BSD | yes | yes | yes | yes | |
HP-UX | yes | yes | yes | yes | |
Linux | yes | yes | yes | yes | |
OSX | yes | yes | yes | ||
Solaris | yes | yes | yes | yes | |
Windows | yes |
Каждый из этих способов подробно освещен ниже.
getrusage( )
На всех UNIX-подобных ОС, функция getrusage( ) это самый надежный способ получить процессорное время, использованное текущим процессом. Функция заполняет структуру rusage временем в секундах и микросекундах. Поле ru_utime содержит время проведенное в user mode, а поле ru_stime — в system mode от имени процесса.
Внимание: Некоторые ОС, до широкого распространения поддержки 64-бит, определяли функцию getrusage( ) , возвращающую 32-битное значение, и функцию getrusage64( ) , возвращающую 64-битное значение. Сегодня, getrusage( ) возвращает 64-битное значение, а getrusage64( ) устарело.
Доступность getrusage( ): AIX, BSD, Cygwin, HP-UX, Linux, OSX, and Solaris.
Получение процессорного времени:
4. Другие искажения
Есть ещё миллион причин для искажений честной отдачи процессорного времени на виртуалке. Например, сложности в расчёты вносят гипертрединг и NUMA. Они окончательно запутывают выбор ядра для исполнения процесса, потому что шедулер использует коэффициенты — веса, которые при переключении контекста делают подсчёт ещё сложнее.
Бывают искажения из-за технологий типа турбобуста или, наоборот, режима энергосбережения, которые при подсчёте утилизации могут искусственно повышать или понижать частоту или даже квант времени на сервере. Включение турбобуста уменьшает производительность одного процессорного треда из-за увеличения производительности другого. В этот момент информация об актуальной частоте процессора виртуальной машине не передаётся, и она считает, что её время кто-то тырит (например, она запрашивала 2 ГГц, а получила вдвое меньше).
В общем, причин искажений может быть много. В конкретной системе вы можете обнаружить что-то ещё. Начать лучше с книг, на которые я дал линки выше, и съема статистики с гипервизора утилитами типа perf, sysdig, systemtap, коих десятки.
Процессорное время - это время, которое потратил процессор сервера на обработку задачи. За базовое время можно взять общее время решения задачи (генерации страницы и т.д.), однако, в него не входит время, которое было затрачено на генерацию других параллельных задачи или ожидание внешних событий.
Процессорное время зависит только от проделанной работы. В случае, если из-за загруженности сервера или из-за ожиданий (например, ответа от mySQL сервера) время выполнения скрипта изменяется, реальное процессорное время (т.е. нагрузка на процессор) остается постоянным.
Другие подходы
Существуют и другие ОС-специфичные способы получить процессорное время. На Linux, Solarisи некоторых BSD, можно парсить /proc/[pid]/stat, чтобы получить статистику процесса. На OSX, приватная функция API proc_pidtaskinfo( ) в libproc возвращает информацию о процессе. Также существуют открытые библиотеки, такие как libproc, procps и Sigar.
На UNIX существует несколько утилит позволяющих отобразить процессорное время процесса, включая ps, top, mpstat и другие. Можно также использовать утилиту time, чтобы отобразить время, потраченное на команду.
На Windows, можно использовать диспетчер задач, чтобы мониторить использование CPU.
На OSX, можно использовать Activity Monitor, чтобы мониторить использование CPU. Утилита для профайлинга Instruments поставляемая в комплекте с Xcode может мониторить использование CPU, а также много других вещей.
CPU steal time — это время, в течение которого виртуальная машина не получает ресурсы процессора для своего выполнения. Это время считается только в гостевых операционных системах в средах виртуализации. Причины, куда деваются эти самые выделенные ресурсы, как и в жизни, весьма туманны. Но мы решили разобраться, даже поставили целый ряд экспериментов. Не то чтобы мы теперь всё знаем о steal, но кое-что интересное сейчас расскажем.
Связанные понятия
Переключение контекста (англ. context switch) — в многозадачных ОС и средах - процесс прекращения выполнения процессором одной задачи (процесса, потока, нити) с сохранением всей необходимой информации и состояния, необходимых для последующего продолжения с прерванного места, и восстановления и загрузки состояния задачи, к выполнению которой переходит процессор.
Защита памяти (англ. Memory protection) — это способ управления правами доступа к отдельным регионам памяти. Используется большинством многозадачных операционных систем. Основной целью защиты памяти является запрет доступа процессу к той памяти, которая не выделена для этого процесса. Такие запреты повышают надёжность работы как программ, так и операционных систем, так как ошибка в одной программе не может повлиять непосредственно на память других приложений. Следует различать общий принцип защиты.
А́дресное пространство (англ. address space) — совокупность всех допустимых адресов каких-либо объектов вычислительной системы — ячеек памяти, секторов диска, узлов сети и т. п., которые могут быть использованы для доступа к этим объектам при определенном режиме работы (состоянии системы).
Код операции, операционный код, опкод — часть машинного языка, называемая инструкцией и определяющая операцию, которая должна быть выполнена.
Счётчик кома́нд (также PC = program counter, IP = instruction pointer, IAR = instruction address register, СЧАК = счётчик адресуемых команд) — регистр процессора, который указывает, какую команду нужно выполнять следующей.
Иерархия компьютерной памяти — концепция построения взаимосвязи классов разных уровней компьютерной памяти на основе иерархической структуры.
Основная область памяти (Основная память, англ. Conventional memory) занимает первые 640 Кбайт оперативной памяти в IBM PC-совместимых компьютерах. В эту область загружается таблица векторов прерываний (занимает 1 Кбайт), некоторые данные из BIOS (например, буфер клавиатуры), различные 16-битные программы DOS. Для них 640 Кбайт являются барьером.
Планирование выполнения задач — одна из ключевых концепций в многозадачности и многопроцессорности как в операционных системах общего назначения, так и в операционных системах реального времени. Планирование заключается в назначении приоритетов процессам в очереди с приоритетами. Программный код, выполняющий эту задачу, называется планировщиком (англ. task switcher, scheduler).
Вытесняющая многозадачность (приоритетная многозадачность, англ. preemptive multitasking, дословно упреждающая многозадачность) — это вид многозадачности, при которой операционная система принимает решение о переключении между задачами по истечении некоего кванта времени.
Разделяемая память (англ. Shared memory) является самым быстрым средством обмена данными между процессами.
Загрузчик операционной системы — системное программное обеспечение, обеспечивающее загрузку операционной системы непосредственно после включения компьютера (процедуры POST) и начальной загрузки.
Буфер ассоциативной трансляции (англ. Translation lookaside buffer, TLB) — это специализированный кэш центрального процессора, используемый для ускорения трансляции адреса виртуальной памяти в адрес физической памяти.
Механизм копирования при записи (англ. Copy-On-Write, COW) используется для оптимизации многих процессов, происходящих в операционной системе, таких как, например, работа с оперативной памятью или файлами на диске (пример — ext3cow).
Дамп памяти (англ. memory dump; в Unix — core dump) — содержимое рабочей памяти одного процесса, ядра или всей операционной системы. Также может включать дополнительную информацию о состоянии программы или системы, например значения регистров процессора и содержимое стека. Многие операционные системы позволяют сохранять дамп памяти для отладки программы. Как правило, дамп памяти процесса сохраняется автоматически, когда процесс завершается из-за критической ошибки (например, из-за ошибки сегментации.
Неблокирующая синхронизация — подход в параллельном программировании на симметрично-многопроцессорных системах, в котором принят отказ от традиционных примитивов блокировки, таких, как семафоры, мьютексы и события. Разделение доступа между потоками идёт за счёт атомарных операций и специальных, разработанных под конкретную задачу, механизмов блокировки.
Тест производительности, бенчмарк (англ. benchmark) — контрольная задача, необходимая для определения сравнительных характеристик производительности компьютерной системы. Иногда бенчмарками также называются программы, которые тестируют время автономной работы ноутбуков и карманных персональных компьютеров, радиус действия беспроводной сети, пропускную способность каналов передачи данных, амплитудно-частотную характеристику звукового тракта и другие доступные для измерения характеристики, напрямую.
Адрес — символ или группа символов, которые идентифицируют регистр, отдельные части памяти или некоторые другие источники данных, либо место назначения информации.
В компьютерной операционной системе, легковесный процесс является средством достижения многозадачности, в традиционном понимании этого термина. В Unix System V и Solaris, легковесный процесс работает в пространстве пользователя поверх одного потока выполнения ядра, разделяет виртуальное адресное пространство и системные ресурсы потока выполнения с другими легковесными процессами, в рамках того же процесса. Несколько потоков пользовательского уровня, управляемые с помощью библиотеки потоков, могут.
Страничная память — способ организации виртуальной памяти, при котором единицей отображения виртуальных адресов на физические является регион постоянного размера (т. н. страница). Типичный размер страницы — 4096 байт, для некоторых архитектур — до 128 КБ.
Hardware Abstraction Layer (HAL, Слой аппаратных абстракций) — слой абстрагирования, реализованный в программном обеспечении, находящийся между физическим уровнем аппаратного обеспечения и программным обеспечением, запускаемом на этом компьютере. HAL предназначен для скрытия различий в аппаратном обеспечении от основной части ядра операционной системы, таким образом, чтобы большая часть кода, работающая в режиме ядра, не нуждалась в изменении при её запуске на системах с различным аппаратным обеспечением.
Снимок файловой системы, или снапшот, или снепшот (от англ. snapshot — мгновенный снимок), — моментальный снимок, копия файлов и каталогов файловой системы на определённый момент времени.
Файловый дескриптор — это неотрицательное целое число. Когда создается новый поток ввода-вывода, ядро возвращает процессу, создавшему поток ввода-вывода, его файловый дескриптор.
Объе́ктный мо́дуль (также — объектный файл, англ. object file) — файл с промежуточным представлением отдельного модуля программы, полученный в результате обработки исходного кода компилятором. Объектный файл содержит в себе особым образом подготовленный код (часто называемый двоичным или бинарным), который может быть объединён с другими объектными файлами при помощи редактора связей (компоновщика) для получения готового исполнимого модуля либо библиотеки.
Журналирование (англ. logging) — форма автоматической записи в хронологическом порядке операций в информационных технологиях, процесс записи информации о происходящих в рамках какого-либо процесса с некоторым объектом событиях, например, в файл регистрации или в базу данных. В некоторых программный комплексах используется термин "аудит", что является не верным, поскольку аудит подразумевает сравнение чего-то с чем-то, чего-то на предмет соответствия, например, требованиям, иными словами это корреляционный.
Сегментная адресация памяти — схема логической адресации памяти компьютера в архитектуре x86. Линейный адрес конкретной ячейки памяти, который в некоторых режимах работы процессора будет совпадать с физическим адресом, делится на две части: сегмент и смещение. Сегментом называется условно выделенная область адресного пространства определённого размера, а смещением — адрес ячейки памяти относительно начала сегмента. Базой сегмента называется линейный адрес (адрес относительно всего объёма памяти.
Разрядность числа в математике — количество числовых разрядов, необходимых для записи этого числа в той или иной системе счисления. Разрядность числа иногда также называется его длиной.
В информатике, блокировка — механизм синхронизации, позволяющий обеспечить исключительный доступ к разделяемому ресурсу между несколькими потоками. Блокировки — это один из способов обеспечить политику управления распараллеливанием.
В информатике термин инструкция обозначает одну отдельную операцию процессора, определённую системой команд. В более широком понимании, «инструкцией» может быть любое представление элемента исполнимой программы, такой как байт-код.
Монтирование файловой системы — системный процесс, подготавливающий раздел диска к использованию операционной системой.
Высокая доступность (англ. high availability) — характеристика технической системы, разработанной для избежания невыполненного обслуживания путём уменьшения или управления сбоями и минимизацией времени плановых простоев. Высокая доступность ожидается от систем жизнеобеспечения, здравоохранения и систем, от которых зависит благополучие общества в целом и экономического благополучия отдельных организаций.
Начальная загрузка — сложный и многошаговый процесс запуска компьютера. Загрузочная последовательность — это последовательность действий, которые должен выполнить компьютер для запуска операционной системы (точнее, загрузчика), независимо от типа установленной ОС.
Кэш или кеш (англ. cache, от фр. cacher — «прятать»; произносится — «кэш») — промежуточный буфер с быстрым доступом к нему, содержащий информацию, которая может быть запрошена с наибольшей вероятностью. Доступ к данным в кэше осуществляется быстрее, чем выборка исходных данных из более медленной памяти или удаленного источника, однако её объём существенно ограничен по сравнению с хранилищем исходных данных.
Точка монтирования (англ. mount point) — это каталог или файл, с помощью которого обеспечивается доступ к новой файловой системе, каталогу или файлу.
Уровень абстракции — один из способов сокрытия деталей реализации определенного набора функциональных возможностей. Применяется для управления сложностью проектируемой системы при декомпозиции, когда система представляется в виде иерархии уровней абстракции.
Реальный режим (или режим реальных адресов; англ. real-address mode) — режим работы процессоров архитектуры x86, при котором используется сегментная адресация памяти (адрес ячейки памяти формируется из двух чисел: сдвинутого на 4 бита адреса начала сегмента и смещения ячейки от начала сегмента; любому процессу доступна вся память компьютера). Изначально режим не имел названия, был назван «реальным» только после создания процессоров 80286, поддерживающих режим, названный «защищённым» (режим назван.
Обработчик прерываний (или процедура обслуживания прерываний) — специальная процедура, вызываемая по прерыванию для выполнения его обработки. Обработчики прерываний могут выполнять множество функций, которые зависят от причины, которая вызвала прерывание.
Планировщик задач — программа (служба или демон), часто называемая сервисом операционной системы, которая запускает другие программы в зависимости от различных критериев, как, например.
Подкачка страниц (англ. paging; иногда используется термин swapping от swap, /swɔp/) — один из механизмов виртуальной памяти, при котором отдельные фрагменты памяти (обычно неактивные) перемещаются из ОЗУ во вторичное хранилище (жёсткий диск или другой внешний накопитель, такой как флеш-память), освобождая ОЗУ для загрузки других активных фрагментов памяти. Такими фрагментами в современных ЭВМ являются страницы памяти.
Модуль ядра, загружаемый модуль ядра (англ. loadable kernel module, LKM) — объект, содержащий код, который расширяет функциональность запущенного или т. н. базового ядра ОС. Большинство текущих систем, основанных на Unix, поддерживают загружаемые модули ядра, хотя они могут называться по-разному (например, kernel loadable module в FreeBSD и kernel extension в Mac OS X).
Кома́нда — это указание компьютерной программе действовать как некий интерпретатор для решения задачи. В более общем случае, команда — это указание некоему интерфейсу командной строки, такому как shell.
Защищённый режим (режим защищённой виртуальной адресации) — режим работы x86-совместимых процессоров. Частично был реализован уже в процессоре 80286, но там существенно отличался способ работы с памятью, так как процессоры ещё были 16-битными и не была реализована страничная организация памяти. Первая 32-битная реализация защищённого режима — процессор Intel 80386. Применяется в совместимых процессорах других производителей. Данный режим используется в современных многозадачных операционных системах.
Стандартные потоки ввода-вывода в системах типа UNIX (и некоторых других) — потоки процесса, имеющие номер (дескриптор), зарезервированный для выполнения некоторых «стандартных» функций. Как правило (хотя и не обязательно), эти дескрипторы открыты уже в момент запуска задачи (исполняемого файла).
В информатике бу́фер (англ. buffer), мн. ч. бу́феры — это область памяти, используемая для временного хранения данных при вводе или выводе. Обмен данными (ввод и вывод) может происходить как с внешними устройствами, так и с процессами в пределах компьютера. Буферы могут быть реализованы в аппаратном или программном обеспечении, но подавляющее большинство буферов реализуется в программном обеспечении. Буферы используются, когда существует разница между скоростью получения данных и скоростью их обработки.
Балансировка нагрузки отличается от физического соединения тем, что балансировка нагрузки делит трафик между сетевыми интерфейсами на сетевой сокет (модель OSI уровень 4) основе, в то время как соединение канала предполагает разделение трафика между физическими интерфейсами на более низком уровне, либо в пакет (модель OSI уровень 3) или по каналу связи (модель OSI уровень 2); Основы с, как протокол соединения кратчайшего пути.
Сравнение с обменом (англ. compare and set, compare and swap, CAS) — атомарная инструкция, сравнивающая значение в памяти с одним из аргументов, и в случае успеха записывающая второй аргумент в память. Поддерживается в семействах процессоров x86, Itanium, Sparc и других.
В информатике и теории автоматов состояние цифровой логической схемы или компьютерной программы является техническим термином для всей хранимой информации, к которой схема или программа в данный момент времени имеет доступ. Вывод данных цифровой схемы или компьютерной программы в любой момент времени полностью определяется его текущими входными данными и его состоянием.
Кросс-компиля́тор (англ. cross compiler) — компилятор, производящий исполняемый код для платформы, отличной от той, на которой исполняется сам кросс-компилятор. Такой инструмент бывает полезен, когда нужно получить код для платформы, экземпляров которой нет в наличии, или в случаях когда компиляция на целевой платформе невозможна или нецелесообразна (например, это касается мобильных систем или микроконтроллеров с минимальным объёмом памяти).
Масштаби́руемость (англ. scalability) — в электронике и информатике означает способность системы, сети или процесса справляться с увеличением рабочей нагрузки (увеличивать свою производительность) при добавлении ресурсов (обычно аппаратных).
Атомарная (атом от греч. atomos — неделимое) операция — операция, которая либо выполняется целиком, либо не выполняется вовсе; операция, которая не может быть частично выполнена и частично не выполнена.
После запуска я получил много вопросов о том, как именно учитываются ресурсы в облаке. Некоторые интуитивно понимают, что такое «час процессорного времени» но есть и те, кто хочет подробного объяснения. Поскольку в общем анонсе подробные объяснения заняли бы много места, я вынес его в отдельный топик. Заодно, такой формат позволит более подробно описать, как Зен и виртуальные машины взаимодействуют. Уровень этого текста научно-популярный, то есть я не буду вдаваться в дебри кольцевых буферов, маскировки событий, «кредитного планировщика» и т.д., вместо этого я попробую рассказать относительно человеческим языком о том, как гипервизор управляет гостевыми машинами.
Что такое «процессорное время»? Сначала мы его хотели назвать более привычным «машинное время», благо, такой термин использовался во времена мейнфреймов, когда идея разделения машинного времени только-только зародилась, но вовремя остановились. Машинное время тех лет подразумевало все ресурсы, которые использовались машиной, а в нашем случае речь идёт именно о процессоре, так как каждый ограниченный ресурс учитывается раздельно.
Итак, что такое «процессорное время» и как может оказаться, что у одной виртуальной машины его насчитывается 4 часа в сутки, а у другой накручивает 30 «часов» за часов десять?
Облако Селектел работает под управлением Xen, точнее, Xen Cloud Platform, в котором гипервизором выступает Xen.
В Xen есть понятие «планировщик доменов». Оставляя в стороне разницу между доменом и виртуальной машиной (домен — запущенная конкретная виртуальная машина, когда виртуальная машина перезагружается, получается новый домен, когда виртуальная машина выключена, домена нет, а сама машина — есть), можно считать, что этот планировщик виртуальных машин. Те, кто знаком с работой современных ОС, наверное уже догадались, что планировщик доменов подозрительно похож на планировщик процессов в этих самых современных ОС.
Как выглядит работа виртуальной машины?
Происходит какое-то событие: приходит сетевой пакет, срабатывает таймер, сигнал о перезагрузке и т.д. Xen отдаёт процессору команду начать выполнять виртуальную машину (точнее, домен, но в пределах этого объяснения будем считать эти понятия эквивалентными). Ядро виртуальной машины обрабатывает событие, из-за которого его разбудили. Если надо, то оно вызывает пользовательские процессы. Процессы делают свою работу и говорят ядру «всё, закончили». Ядро разбирается со своими вопросами и так же говорят гипервизору (Xen'у) — «всё, я закончило». После этого Xen останавливает выполнение машины. Она просто ничего не делает в буквальном смысле слова. Машина пребывает в таком состоянии до момента, пока не наступает новое событие.
В современных машинах эти события наступают с огромной скоростью — например, если вы загружаете файл со скоростью 5Мб/с, то это (при размере пакета в 1500 байт) — это больше 3000 пакетов в секунду. Каждый пакет — это отдельное прерывание (точнее, в Xen'е всё хитрее, там несколько вызовов объединяются в один, так что иногда виртуальная машина оказывается чуть-чуть быстрее, чем даже на голом железе). И каждое такое событие — это пробуждение машины. Но скорость современных процессоров такова, что после каждого такого вызова ядро виртуальной машины и процессы (например, апач или nginx) успевают отработать и заснуть. Отдача статики на 5Мб/с — это очень низкая нагрузка, примерно 1-2% одного ядра процессора, так что, не смотря на то, что события происходят с интервалом в 300 микросекунд, виртуальная машина отрабатывает за 3-6 микросекунд и оставшиеся 294-296 микросекунд успевает сказать гипервизору «я всё» и заснуть. А через микросекунды снова проснуться, отработать и снова заснуть. Так и получается, что виртуальная машина большую часть времени просто спит.
Вот именно моменты времени, когда виртуальная машина работает и являются «процессорным временем».
Вдумчивый читатель может спросить — а что, если виртуальная машина не скажет «я всё»? Если бы у нас была Windows 3.11, где была кооперативная многозадачность, то это бы привело к тому, что остальные не получили бы полагающееся им время. Но в Xen'е используется вытесняющая многозадачность — и виртуальная машина, которая слишком жадно работает, будет просто приостановлена. Принудительно. А потом снова продолжена.
Обычно такая ситуация происходит в условиях нехватки процессорного времени, и авторы Xen'а потратили тысячи часов, разрабатывая справедливые планировщики, которые в условиях перегруженности процессора решают задачу распределения времени так, чтобы все продолжили работать более-менее равномерно.
Однако, в реальных условиях современного хостинга, скорость работы процессора так высока, что процессор — наименее востребованный и самый простаивающий ресурс и в 99% случаев конкуренции за ресурсы вообще не возникает.
Процессорным временем является время, в течение которого работает виртуальная машина. Если она работала 2с за час, то это так и есть. Если 40 минут — значит, сорок минут. Процессорное время никак не связано с «реальным» временем на часах. Так как Xen командует виртуальными машинами, то Xen с точностью до наносекунды знает, сколько времени отработала каждая машина. Мы это значение округляем до микросекунд (чтобы избежать проблемы с int64), а в биллинге фиксируются лишь целые секунды (дробная часть копится, пока не набежит на секунду). Деньги же за процессорное время списываются как только набежит хотя бы на 1 копейку (в настоящий момент это 36 секунд). Для сравнения — загрузка виртуальной машины съедает примерно 3-6 секунд машинного времени, а это самая «дорогостоящая» операция в жизненном цикле домена.
Если загрузка машины меньше 100% (то есть она потребляет меньше часа процессорного времени в час), то, формально, можно было бы ограничиться одним ядром.
Но, помните, что я выше сказал про одновременное обслуживание клиентов? Несколько ядер обеспечивают большую «отзывчивость» на запросы, хотя, возможно, одно ядро вполне бы справилось, пусть и ценой увеличения времени ответа на запрос.
Кстати, это ответ и ещё на один вопрос: влияет ли количество ядер на затрачиваемое процессорное время? Ответ — нет, если эти ядра простаивают, то процессорное время не используется. А большое число ядер лишь уменьшает задержку при обслуживании одновременных запросов от нескольких клиентов.
Ну и вдогонку немного о том, как нужно понимать понятия «отдаёт время», «выделяет время». Процессор — железка кремниевая и бестолковая. Всё, что может делать процессор — это выполнять код (ну и реагировать на прерывания). И процессор не особо разбирается «домен виртуальной машины» это, или запущенная копия angry birds. Таким образом, понятие «домен», «гипервизор» — это в каком-то смысле условности. Когда мы говорим «виртуальная машина работала 10 мс», мы на самом деле подразумеваем фразу «процессор исполнял код виртуальной машины 10 мс». Когда мы говорим «гипервизор вытеснил виртуальную машину», мы на самом деле подразумеваем «по прерыванию таймера процессор обновил счётчик времени, сохранил контекст процесса и передал управление в другое место, отличное от места, где его прервал таймер». Подобный перевод объекта (код) в субъект, обладающий способностью к действию, сильно упрощает объяснение — у каждой программы есть алгоритм поведения, и проще сказать, что «программа ведёт себя так-то», вместо того, чтобы говорить «процессор, исполняя программу, делает то-то и то-то».
Теперь немного о том, что сколько кушает. В начале статьи — график весьма нагруженного сервера, который держит на себе asterisk с звонками целой компании, веб-сервер, сбор статистики с машрутизаторов и т.д. Внизу — сайт с примерно 5000 уникальных посетителей в день. Это к вопросу о том, сильно ли используют процессор современные серверные приложения (циан на графиках — простаивающий процессор).
КДПВ
От переводчика:
Большинство моих знакомых для измерения времени в разного вида бенчмарках в С++ используют chrono или, в особо запущенных случаях, ctime . Но для бенчмаркинга гораздо полезнее замерять процессорное время. Недавно я наткнулся на статью о кроссплатформенном замере процессорного времени и решил поделиться ею тут, возможно несколько увеличив качество местных бенчмарков.
P.S. Когда в статье написано "сегодня" или "сейчас", имеется ввиду "на момент выхода статьи", то есть, если я не ошибаюсь, март 2012. Ни я, ни автор не гарантируем, что это до сих пор так.
P.P.S. На момент публикации оригинал недоступен, но хранится в кэше Яндекса
Функции API, позволяющие получить процессорное время, использованное процессом, отличаются в разных операционных системах: Windows, Linux, OSX, BSD, Solaris, а также прочих UNIX-подобных ОС. Эта статья предоставляет кросс-платформенную функцию, получающую процессорное время процесса и объясняет, какие функции поддерживает каждая ОС.
Процессорное время увеличивается, когда процесс работает и потребляет циклы CPU. Во время операций ввода-вывода, блокировок потоков и других операций, которые приостанавливают работу процессора, процессорное время не увеличивается пока процесс снова не начнет использовать CPU.
Разные инструменты, такие как ps в POSIX, Activity Monitor в OSX и Task Manager в Windows показывают процессорное время, используемое процессами, но часто бывает полезным отслеживать его прямо из самого процесса. Это особенно полезно во время бенчмаркинга алгоритмов или маленькой части сложной программы. Несмотря на то, что все ОС предоставляют API для получения процессорного времени, в каждой из них есть свои тонкости.
Функция getCPUTime( ) , представленная ниже, работает на большинстве ОС (просто скопируйте код или скачайте файл getCPUTime.c). Там, где это нужно, слинкуйтесь с librt, чтобы получить POSIX-таймеры (например, AIX, BSD, Cygwin, HP-UX, Linux и Solaris, но не OSX). В противном случае, достаточно стандартных библиотек.
2. Что влияет на steal
Содержание
Лучше всего пояснить этот термин примерами.
- На вашей домашней машине вы архивировали большой файл. Это заняло 40 секунд. Общее время - 40 секунд, процессорное время - 40 секунд.
- Аналогично, но два файла одновременно. Общее время завершения процессов - 80 секунд, процессорное время каждого - 40 секунд.
- На пустом сервере выполняется скрипт, время генерации страницы - 2 секунды. Процессорное время - 2 секунды.
- На сервере выполняется одновременно 10 таких скриптов. Время генерации страницы будет 20 секунд, однако, процессорное время по прежнему 2 секунды.
- На соседнем сервере произошла перегрузка mySQL сервера, в результате чего ваш скрипт работал не 2 секунды, а 45 секунд. Время генерации страницы - 45 секунд, процессорное время по прежнему 2 секунды.
Именно процессорное время - тот ресурс, который может закончиться на сервере. Необходим его учет. При этом, как видно из примеров, не важно, сколько именно работал ваш скрипт - важно, какую именно работу он при этом делал, т.е. процессорное время, необходимое для работы скрипта. Нормальным количеством времени на одну страницу является 10 - 100 мс (т.е. в 10 - 100 раз меньше, чем одна секунда). Поскольку сервер одновременно обрабатывает десятки запросов, время генерации страницы составляет обычно в пределах одной секунды. Это нормально.
Веб-сервер ведет лог-файл процессорного времени, которое было затрачено на каждый запрос. Его сообщает операционная система. Затем данные суммируется для каждого сайта в пределах одного часа. Если суммарное процессорное время для вашего сайта составило 1 минуту в 1 час, это обозначает, что в этот час вы потратили 1/60 = 1.7% ресурсов одного процессора сервера.
Сервера обладают разным количеством процессоров. На сервере, где 8 процессоров (точнее, более реалистично, 2 процессора по 4 ядра в каждом – таков современный сервер 1Gb), общее процессорное время может достигать 800%, это обозначает, что вы на 100% загрузили все 8 логических процессоров.
Учет ведется именно по ядрам процессоров, так как в одном сервере может быть от 1 до 8 независимых ядер, при этом цифра «3.5% от ресурсов сервера» может означать мощность, которая фактически отличается в 8 раз в зависимости от конфигурации сервера, что было бы некорректно.
Для сравнения наших тарифов с тарифами других провайдеров необходимо делать на это поправку. Например, тариф П-15, обеспечивающий нагрузку до 15% на один процессор, эквивалентен общей нагрузке на сервер в размере 3.75%, если другой провайдер указывает её для 4-х процессорного сервера целиком.
Таким образом, нагрузка в пределах 3.5% - 7.5% (предел стандартных тарифов) по этому показателю соответствует принятым для виртуального хостинга пределам, выраженным в нагрузке от всего сервера – встречаются условия от 1% до 3%. Общий предел нужно сначала умножить на 8 - общее количество процессоров в текущем сервере 1Gb - а потом поделить на среднее кол-во процессоров для установленного предела у сравниваемого провайдера. В подавляющем большинстве упоминается "машина класса Dual Xeon 3 ГГц", что составляет 4 логических процессора.
times( )
На всех UNIX-подобных ОС, устаревшая функция times( ) заполняет структуру tms с процессорным временем в тиках, а функция sysconf( ) возвращает количество тиков в секунду. Поле tms_utime содержит время, проведенное в user mode, а поле tms_stime — в system mode от имени процесса.
Внимание: Более старый аргумент функции sysconf( ) CLK_TCK устарел и может не поддерживаться в некоторых ОС. Если он доступен, функция sysconf( ) обычно не работает при его использовании. Используйте _SC_CLK_TCK вместо него.
Доступность times( ): AIX, BSD, Cygwin, HP-UX, Linux, OSX и Solaris.
Получение процессорного времени:
2.1. Вычисление steal
По сути, steal считается примерно так же, как и обычное время утилизации процессора. Информации о том, как считается утилизация, не много. Наверное, потому что большинство считает этот вопрос очевидным. Но здесь тоже бывают подводные камни. Для ознакомления с этим процессом можно прочитать статью Brendann Gregg: вы узнаете о куче нюансов при расчете утилизации и о ситуациях, когда этот подсчёт будет ошибочным по следующим причинам:
- Перегрев процессора, при котором пропускаются такты.
- Включение/выключение турбобуста, в результате которого изменяется тактовая частота процессора.
- Изменение продолжительности кванта времени, происходящее при использовании технологий энергосбережения процессора, например SpeedStep.
- Проблема подсчёта среднего: оценка утилизации в течение одной минуты на уровне 80 % может спрятать кратковременный бурст в 100 %.
- Циклическая блокировка (spin lock) приводит к тому, что процессор утилизирован, но пользовательский процесс не видит продвижения по своему выполнению. В результате расчётная утилизация процессора процессом будет стопроцентной, хотя физически процессорное время процесс потреблять не будет.
Процесс подсчёта steal подвержен тем же самым проблемам, что и обычный подсчёт утилизации. Не сказать, что такие проблемы появляются часто, но выглядят обескураживающе.
Читайте также: