Кадр из фильма «Матрица: Революция» В этой статье мы подробно рассмотрим детали одной интересной находки: два часто используемых системных вызова (gettimeofday, clock_gettime) в AWS EC2 выполняются очень медленно. В Linux реализован механизм по ускорению этих двух часто используемых системных вызовов, благодаря которому их код выполняется в пространстве пользователя, что позволяет избежать переключениям в контекст ядра. Это сделано с помощью предоставляемой ядром виртуальной общей библиотеки (virtual shared library), которая отображается в адресное пространство всех запущенных программ. Два вышеназванных системных вызова не могут использовать vDSO (virtual Dynamic Shared Object) в AWS EC2, поскольку виртуализированный источник временных меток (virtualized clock source) в xen (и некоторых конфигурациях kvm) не поддерживает получение информации о времени через vDSO. Обойти эту проблему не получится. Можно поменять источник информации о времени на tsc, но это небезопасно. Далее мы рассмотрим вопрос более подробно и проведем сравнительное тестирование с помощью microbenchmark. Чтобы быстро проверить систему на наличие этой проблемы, скомпилируйте приведенную ниже программу и выполните ее с strace: Strace насчитала 100 вызовов gettimeofday. Это значит, что vDSO не использовался, а вместо него делались реальные системные вызовы, что приводило к переключению в контекст ядра. Механизм vDSO в Linux был спроектирован с учетом gettimeofday (этот вызов даже упомянут в man-странице vDSO). Любой системный вызов, сделанный через vDSO, полностью выполняется в пространстве пользователя, избегая тем самым переключения контекстов. В результате любой системный вызов, который успешно прошел через vDSO, не появится в выводе strace. Далее мы в подробностях разберем, почему и как это происходит, а также посмотрим на весьма интересные результаты профилирования. Есть несколько важных аспектов, с которыми желательно ознакомиться, чтобы лучше понять описание проблемы и соответствующие фрагменты кода. Прежде чем продолжать чтение этой статьи, настоятельно рекомендуем внимательно изучить нашу предыдущую публикацию, в которой детально описана работа системных вызовов Linux: The Definitive Guide to Linux System Calls (на английском языке). vDSO по сути является предоставляемой ядром общей библиотекой, которая отображается в адресное пространство каждого процесса. Когда происходит вызов gettimeofday, clock_gettime, getcpu или time, glibc пытается выполнить код, предоставляемый vDSO. Этот код получает необходимые данные без переключения в контекст ядра и таким образом помогает избежать дополнительных расходов на выполнение настоящего системного вызова. Поскольку сделанные через vDSO системные вызовы не приводят к переключению в контекст ядра, strace не получает соответствующие уведомления. В результате в выводе strace не будет упоминания о, например, gettimeofday, если программа успешно сделала этот системный вызов через vDSO. Вместо strace в этом случае нужно использовать ltrace. Более подробную информацию о том, как устроена утилита strace, можно найти в нашей публикации «How does strace work». В AWS EC2 gettimeofday появляется в выводе strace. Это происходит потому, что vDSO в некоторых ситуациях выполняет обычные системные вызовы. В работающих под управлением Linux системах с архитектурой x86 для получения информации о времени используется несколько различных механизмов: У каждого из названных механизмов есть свои плюсы и минусы. Подробную информацию можно найти в исходниках ядра Documentation/virtual/kvm/timekeeping.txt. Важно понимать, что виртуализация создает дополнительные трудности при работе с информацией о времени. Например: Парни из VMWare опубликовали очень интересную статью, в которой описаны эти и другие вопросы, связанные с работой со временем. Информация в этой статье представлена как специфичная для VMWare, но по большому счета она относится к любой системе виртуализации. Для решения этих и других проблем в KVM и Xen имеются собственные системы работы со временем: KVM PVclock и Xen time. В ядре Linux они называются clocksource (источник информации о времени). Текущий clocksource системы может быть найден в файле /sys/devices/system/clocksource/clocksource0/current_clocksource. Именно к этому источнику обратится система при выполнении вызова gettimeofday или clock_gettime. Давайте посмотрим, как вызов gettimeofday реализован в коде vDSO. Напомню, что этот код идет в составе ядра, но выполняется в пространстве пользователя. Если мы внимательно изучим код, расположенный в arch/x86/vdso/vclock_gettime.c, и сравним реализации gettimeofday (__vdso_gettimeofday) и clock_gettime (__vdso_clock_gettime) в vDSO, мы обнаружим, что в обоих случаях ближе к концу функции есть похожие блоки if: В коде __Vdso_clock_gettime есть такая же проверка, но вызывается другая функция: vdso_fallback_gettime. Если ret равен VCLOCK_NONE, то это значит, что текущий системный источник информации о времени не поддерживает vDSO. В данном случае функция vdso_fallback_gtod в обычном порядке выполняет системный вызов (переключаясь в контекст ядра со всеми вытекающими дополнительными расходами). Но в каких случаях ret получает значение VCLOCK_NONE? Если начать двигаться по коду вверх от этого блока условия, мы обнаружим, что ret получает значение поля vclock_mode текущего clocksource. В следующих источниках: vclock_mode не равен VCLOCK_NONE. С другой стороны, в источниках: vclock_mode равен VCLOCK_NONE (0). AWS EC2 использует Xen. В идущем по умолчанию в Xen clocksource (xen) поле vclock_mode установлено в VCLOCK_NONE, поэтому инстансы EC2 всегда будут использовать медленные системные вызовы – механизм vDSO задействован не будет. Но как это повлияет на производительность? В этом эксперименте мы с помощью microbenchmark измерим, насколько быстрее gettimeofday выполняется через vDSO по сравнению обычным системным вызовом. Для этого мы запустим в EC2-инстансе тестовую программу с тремя циклами. Сначала будем тестировать с clocksource, равным xen, а затем — tsc. Устанавливать clocksource равным tsc в EC2 небезопасно. Маловероятно, но все же возможно, что это может привести непредвиденному отставанию часов (backwards clock drift). Не делайте этого в production-системах. Параметры AWS-инстанса: Время выполнения мы будем измерять с помощью программы time. Вы можете удивиться: «как можно использовать программу time, которая способна дестабилизировать источник информации о времени (clocksource)?» К счастью, разработчик ядра Ingo Molnar написал программу для определения фактов искажения времени (time warps): time-warp-test.c. Прошу заметить, что для работы на 64bit x86-системах программа должна быть немного изменена. Во время проведения нашего эксперимента утилита time-warp-test искажений времени не зафиксировала. Для получения более обоснованного результата можно сделать следующее: Для целей нашего эксперимента было достаточно прогона тестов на искажения времени. Из результатов видно, что обычные системные вызовы в условиях ec2 примерно на 77% медленнее vDSO-вызовов: 5 миллионов вызовов gettimeofday: 50 миллионов вызовов gettimeofday: 500 миллионов вызовов gettimeofday: Чтобы исправить эту проблему, необходимо добавить поддержку vDSO в Xen. К счастью, в работе уже находится несколько соответствующих патчей. Пока это (или аналогичное) изменение не попадет в ядро, а затем и в EC2, системные вызовы gettimeofday и clock_gettime будут выполняться на 77% медленнее, чем на аналогичных системах с поддержкой vDSO. Как и ожидалось, системные вызовы vDSO значительно быстрее обычных системных вызовов. Это достигается за счет того, что vDSO не переключается в контекст ядра. Важно помнить, что успешно выполненные системные вызовы vDSO не попадают в вывод strace. Если vDSO использовать не удалось, будет сделан обычный системный вызов, который появится в выводе strace. В работе находится несколько патчей, которые призваны добавить поддержку vDSO в Xen, но не известно, когда эти изменения появятся в AWS EC2. Пока этого не произошло, gettimeofday и clock_gettime будут выполняться примерно на 77% медленнее, чем должны были бы. Использование strace замедляет выполнение приложения, но дает бесценные сведения о том, что конкретно оно делает. Всем программистам следует прогонять свои приложения через strace и анализировать вывод этой утилиты. Если вам понравилась эта статья, рекомендую прочитать и другие наши публикации, в которых также содержится много низкоуровневой технической информации: Ссылки: CEO в Southbridge