Инструмент, подобный strace, позволяет проверить предположения о точности измерения времени ядром Oracle для конкретной системы. У нас есть один файл трассировки, сформированный версией Oracle 9.2.0.1.0 для Compaq OSF1, в котором c имеет разрешение 3333.3 мкс, e - разрешение 1000 мкс, а ela - 10000 мкс. Эти данные вызывают резонный вопрос о том, действительно ли значения e и ela для данной платформы получены от одного системного вызова gettimeofday. (Если бы это было так, т. е. значения e и ela для данной платформы были бы результатами одного системного вызова, почему бы они могли иметь очевидно различную точность измерения?) При наличии трассировки системного вызова на этот вопрос было бы очень просто ответить. В противном случае нам остается только гадать.
Ошибка квантования обозначается буквой E и определяется как разность между реальной продолжительностью события ea и его измеренной продолжительностью em. То есть
Вернемся к примеру 7.1, точнее, к его отображению на шкале времени на рис. 7.5. На этом рисунке каждое деление соответствует такту интервального таймера, подобного таймеру gettimeofday. В момент начала dosomething таймер имел значение t0 = 1492, а в момент завершения dosomething - t1 = 1498. То есть измеренная продолжительность вызова dosomething равна em = t1 -10 = 6. Однако если измерить длину отрезка, соответствующего продолжительности ea на рисунке, то окажется, что реальная длительность исполнения something (ea) равна 5,875 такта. Точное значение длительности можно получить, измерив линейкой высоту отрезка ea на рисунке, а вот приложение не может получить точное значение ea, имея в своем распоряженинии только интервальный таймер с указанным размером такта. Ошибка квантования составляет E = em - ea = 0,125 такта или около 1,7% реальной длительности (5,875 тактов).
Рис. 7.5. Интервальный таймер с удовлетворительной точностью измеряет длительности событий, охватывающих множество тактов его часов
Теперь рассмотрим случай, в котором длительность исполнения do_so-mething близка к разрешению таймера, как это показано на рис. 7.6. В левой части рисунка длительность dosomething покрывает всего одно деление шкалы, так что его измеренная продолжительность em=1. Однако фактическая продолжительность события составляет всего ea=0,25 (это значение можно проверить, измерив длину отрезка на рисунке). В данном случае ошибка квантования вычисляется как E = 0,75, т. е. составляет целых 300% реальной длительности события (0,25 такта). В правой части рисунка выполнение вызова dosomething не захватывает ни одного такта системных часов, и его измеренная продолжительность em = 0, в то время как реальная длительность ea = 0,9375. Теперь ошибка квантования E = -0,9375 и составляет -100% реальной длительности события (0,9375 такта).
Упрощенно ошибку квантования можно описать следующим образом:
Точность любого измерения, выполненного интервальным таймером, не превышает единицы его разрешающей способности.
Если формулировать строже, то разность двух отсчетов цифровых часов равна сумме фактической продолжительности и ошибки квантования, точное значение которой определить невозможно, известен лишь диапазон таких значений: приблизительно от -1 такта до +1 такта системных часов. Если обозначить разрешение некоторого таймера x переменной rx, то получится следующее соотношение:
Ошибка квантования E, свойственная любому дискретному (цифровому) измерению, - это равномерно распределенная случайная переменная (см. главу 11) с диапазоном значений -rx < E < rx, где rx - это разрешающая способность интервального таймера.
Любое значение фактической продолжительности, выводимое ядром Oracle (или каким-то другим программным обеспечением), следует воспринимать с учетом точности измерений. Например, если в файле трассировки Oracle8i встречается значение e=4, то не стоит думать, что реальная длительность какого-то события равнялась 4 сантисекун-дам. На самом деле такое значение показывает, что если разрешение таймера составляет не более 1 сантисекунды, то реальная продолжительность события находится в диапазоне от 3 до 5 сантисекунд. И это наиболее точная из доступных вам оценок.
Если собранные значения малы, то такая погрешность может привести к курьезным результатам. Например, нельзя даже будет корректно сравнить длительности событий, измеренные длительности которых приблизительно равны. Несколько таких курьезных ситуаций изображено на рис. 7.7. Представим, что таймер тикает с интервалом в одну сантисекунду. Это имитирует работу Oracle8i, где значащие разряды, следующие за сотыми долями секунды, усекаются. На рисунке видно, что фактическая продолжительность события A превышает фактическую продолжительность события B, но их измеренные продолжительности находятся в обратном отношении. Событие C заняло больше времени, чем D, при этом D имеет большую измеренную продолжительность. Обобщая, можно сказать, что любое событие с измеренной продолжительностью n + 1 может иметь фактическую длительность, которая больше, равна или даже меньше, чем у другого события с измеренной продолжительностью n. И вы не можете знать, какое отношение имеет место в данном случае.
Интервальный таймер может выполнять измерения только с точностью до ±1 такта системных часов, но на практике это ограничение не уменьшает полезность применения таких таймеров. При больших объемах выборок положительные и отрицательные ошибки квантования постепенно компенсируют друг друга. Например, сумма ошибок квантования для случая, изображенного на рис. 7.6, равна:
E1 + E2 = 0,75 + (-0,9375) = 0,1875
В отличие от значительных относительных величин отдельных ошибок их сумма оказывается гораздо меньше и составляет 16% от суммы реальных длительностей событий. Проанализировав в hotsos.com несколько сотен файлов трассировки SQL, полученных от сотен различных систем Oracle, мы выявили тенденцию взаимного погашения положительной и отрицательной ошибок квантования в файлах в несколько сотен строк. В большинстве случаев величина суммарной ошибки не выходит за пределы ±10% общего времени отклика, измеренного в файле трассировки.
Возможно, вы обратили внимание на то, что системные вызовы gettimeofday дают существенно большую точность, чем getrusage. Несмотря на то, что псевдокод в примере 7.3 создает ощущение, что gettimeofday и getrusage делают практически одно и то же, в реальности эти две функции работают совершенно по-разному. И в результате имеют абсолютно разную точность.
Разобраться, как работает gettimeofday, гораздо легче, чем сделать это для getrusage. Будем рассматривать в качестве примера Linux на процессоре Intel Pentium. Как я уже говорил, процессор Intel Pentium имеет аппаратный счетчик временных меток TSC, который обновляется с каждым тактом аппаратных часов. Например, процессор с частотой 1 ГГц обновляет счетчик приблизительно миллиард раз в секунду [Bovet and Cesati (2001) 139-141]. Подсчитывая количество тактов, зарегистрированных счетчиком с тех пор, когда пользователь установил время командой date, ядро Linux может определить, сколько тактов прошло с момента начала эпохи Unix. Возвращаемый gettimeofday результат - это полученное число, усеченное до микросекунд (для обеспечения соответствия функции gettimeofday стандарту POSIX).
Операционная система может учитывать процессорное время, израсходованное процессом в пользовательском и привилегированном режиме, двумя способами:
Опрос (Polling)
Операционная система может содержать специальный код, позволяющий каждому выполняющемуся процессу через фиксированные интервалы времени обновлять собственную таблицу rusage. На каждом интервале каждый работающий процесс может обновлять собственную статистику расходования ресурсов процессора, считая, что он использовал процессор в течение всего интервала в том режиме, в каком процесс находится в текущий момент.1
Событийно обусловленные измерения (Event-based instrumentation)
Операционная система может содержать специальный код, который при каждом переходе процесса в пользовательский или привилегированный режим осуществляет вызов таймера высокой точности. При каждом выходе процесса из такого состояния ОС может повторно вызывать таймер и выводить величину (в микросекундах) разности между двумя вызовами в структуру rusage процесса.
В большинстве операционных систем (по крайней мере, по умолчанию) применяется опрос. Так, Linux обновляет несколько атрибутов для каждого процесса, в том числе использованное до указанного момента процессорное время, при каждом прерывании по таймеру [Bovet and Cesati (2001) 144-145]. Некоторые операционные системы поддерживают событийно обусловленные измерения. Например, такая возможность имеется в Sun Solaris под названием «учет микросостояний» (microstate accounting) [Cockroft (1998)].
При учете микросостояний ошибка квантования ограничена одной единицей разрешения таймера на каждое переключение состояния. В случае применения таймера с высоким разрешением (подобного gettimeofday) общая ошибка квантования для статистики использования процессора, полученная в результате учета микросостояний, оказывается совсем небольшой. Но повышение точности достигается за счет увеличения эффекта влияния измерителя. Однако, как вы сейчас увидите, в случае применения опроса ошибка квантования может быть значительно больше.
Независимо от того, каким способом получена информация о расходовании ресурсов, операционная система предоставляет эту информацию любому процессу, которому она необходима, посредством системного вызова, подобного getrusage. Стандарт POSIX требует, чтобы в качестве единицы измерения в функции getrusage выступали микросекунды, но для систем, получающих данные rusage путем опроса, реальное разрешение получаемых данных зависит от частоты прерываний по таймеру.
В большинстве систем частота составляет 100 прерываний в секунду или 1 прерывание в сантисекунду (в текстах операционных систем значения часто выражены в миллисекундах: 1 сантисекунда = 10 миллисекунд = 0,010 секунды). Частота прерываний по таймеру во многих системах устанавливается параметром, но большинство системных администраторов оставляет ее равной 100 прерываниям в секунду. Если попросить систему обслуживать прерывания чаще 100 раз в секунду, то точность измерения времени будет выше, но при этом пострадает производительность. Даже если обслуживать прерывания всего в десять раз чаще, накладные расходы на работу планировщика в привилегированном режиме возрастут в десять раз. Такое решение нельзя считать разумным компромиссом.
Если операционная система соответствует стандарту POSIX, то определить разрешение ее планировщика поможет следующая программа на Perl [Chiesa (1996)]:
Если системные часы имеют разрешение в одну сантисекунду, то ge-trusage может возвращать значение в микросекундах, но такие значения никогда не будут содержать корректную информацию в разрядах младше, чем сотые доли секунды.
Причина, по которой я вам все это объясняю, заключается в том, что ошибка квантования статистики c для Oracle в корне отличается от ошибки квантования статистик e и ela. Вернемся к утверждению:
Точность любого измерения, выполненного интервальным таймером, не превышает единицы его разрешающей способности.
Проблема статистики c заключается в том, что величина, возвращаемая функцией getrusage, - это на самом деле не продолжительность. То есть «продолжительность» использования процессора, возвращаемая getrusage, не вычисляется как разность пары измерений интервального таймера.
В системах, где ведется учет микросостояний, объем использования процессора вычисляется как сумма очень большого количества коротких временных интервалов.
В системах, получающих информацию rusage путем опроса, объем использования процессора - это приблизительная оценка продолжительности, полученная в процессе опроса.
Получается, что в любом случае ошибка квантования, присущая статистике c, может быть гораздо более серьезной, чем просто один такт системных часов. Проблема актуальна даже в системах, применяющих учет микросостояний. В системах, которые это не делают, все обстоит еще хуже.
На рис. 7.8 изображена типичная ситуация при организации опроса, в которой ошибки определения времени занятости процессора в пользовательском режиме приводят к учету лишнего времени при вычислении времени отклика вызова базы данных. Диаграмма последовательности состояний на этом рисунке приводит время занятости процессора в пользовательском режиме и время системного вызова, потребленные вызовом базы данных. На оси ЦПУ отмечены прерывания системного таймера с интервалом в 1 сантисекунду. Размер рисунка не позволяет отобразить на оси «Время, потраченное вне ЦПУ» 10 000 тактов, которые проходят между каждой парой тактов на оси ЦПУ.
Можно предположить, что в результате операций, изображенных на рис. 7.8, ядро Oracle9i сформирует данные трассировки, подобные приведенным в примере 7.5. Я вычислил ожидаемые значения статистик e и ela, измеряя длины отрезков на рисунке. Благодаря высокой точности таймера gettimeofday, с помощью которого я выполнял измерения, ошибка квантования таких измерений e и ela пренебрежимо мала.
Пример 7.5. Временные статистики Oracle9i, которые могли бы быть получены в результате событий, изображенных на рис. 7.8
Реально затраченное вызовом базы данных процессорное время составляет 2,5 сантисекунды. Это значение я получил, физически измерив длины соответствующих отрезков на рис. 7.8. Однако getrusage получает свои данные об использовании процессора из структуры расходования ресурсов процессом, обновляемой в результате опроса по прерываниям таймера. При каждом прерывании планировщик процессов операционной системы добавляет одну полную сантисекунду (10 000 мкс) процессорного времени любому процессу, исполняемому в данный момент времени. Соответственно, getrusage сообщит о том, что вызов базы данных на рис. 7.8 занял шесть полных сантисекунд времени процессора. Проверить результат очень просто - посмотрите на рис. 7.8 и сосчитайте количество тактов, пришедшихся на моменты использования процессора.
На рисунке все кажется разумным, но обратите внимание на неучтенное время:
Если значение неучтенного времени меньше нуля, значит, данные трассировки содержат отрицательную величину «упущенного времени». Другими словами, вызову базы данных приписаны лишние 39 375 мкс. Это число выглядит настораживающе большим, но не забывайте о том, что на самом деле это всего около 4 сантисекунд. Реально же вызов базы данных затратил в пользовательском режиме всего 25 000 мкс времени процессора (опять-таки, это значение я получил мошеннически, измерив длины отрезков на рис. 7.8).
< Предыдущая | Следующая > |
---|