[ Страница назад | Страница вперед | Содержание | Индекс | Библиотека | Юридическая информация | Поиск ]

Программирование: Разработка и отладка программ


Применение API монитора в программах

В этом разделе приведено описание библиотеки API монитора производительности.

Дополнительные сведения о применении API монитора производительности в многонитевых программах можно найти в следующих разделах:

Введение

В этом разделе описаны API библиотеки libpmapi, позволяющие программисту пользоваться рядом функций монитора производительности. Эти API могут применяться только в системах с определенными процессорами из семейства POWERPC. Их можно разделить на следующие группы:

Набор API и событий индивидуален для каждого процессора. В данном разделе события не рассматриваются, потому что набор доступных событий и их состояние никоим образом не влияют на выполнение вызовов API.

Состояние события возвращается в флагах функции инициализации API (pm_init) и может принимать значения verified, unverified или works с дополнительным значением caveat. Обязательно ознакомьтесь с важным предупреждением (Точность монитора), связанным с точностью результатов проверки состояния событий.

При вызове функции pm_init нужно указывать фильтр для выбора событий по их состоянию. Если функции pm_init не будет передан фильтр, то она не вернет ни одно событие.

Для каждого события функция pm_init возвращает его состояние, полное описание, идентификатор события для применения в последующих вызовах API, краткое имя и полное имя. Краткое имя указывается в формате PM_<имя>. Аналогичным событиям, предназначенным для разных процессоров, присвоены одни и те же имена. Например, на большинстве процессоров есть события, соответствующие количеству циклов процессора и количеству выполненных инструкций. Этим событиям на всех процессорах присвоены имена PM_CYC и PM_INST_CMPL.

Точность монитора

Если событие проверено полностью, ему присваивается состояние проверено. Состояние без гарантий означает, что проверка была выполнена только до пределов, указанных в описании события.

Состояние не проверено означает, что точность значения неизвестна. Рекомендуем вам пользоваться событиями с состоянием не проверено с чрезвычайной осторожностью; API монитора могут считывать сведения из аппаратных регистров, которые не всегда можно хоть как-либо интерпретировать.

Решения о том, какую ценность имеют конкретные события с состоянием не проверено, следует всегда принимать в индивидуальном порядке.

Контекст и состояние монитора

Определения

Для предоставления доступа к данным монитора на различных уровнях, в операционной системе предусмотрено несколько т.н. контекстов. Эти контексты представляют собой расширения стандартных контекстов процессора и нитей и содержат по одному 64-разрядному счетчику для каждого аппаратного счетчика, плюс набор управляющих слов. Управляющие слова применяются для выбора событий и для включения и выключения счетчиков.

Контекст системы

API системного уровня позволяют связывать с каждым процессором дополнительные контексты PM. Если в системе установлено расширение ядра PM (монитор), то оно автоматически обрабатывает переполнения 32-разрядного аппаратного счетчика монитора и обслуживает отдельный набор 64-разрядных счетчиков для каждого 32-разрядного аппаратного счетчика монитора.

Контекст нити

С каждой нитью ядра можно связать отдельный контекст монитора. Для каждого контекста нити операционная система и расширение ядра монитора автоматически обслуживают набор 64-разрядных счетчиков.

Контекст группы нитей и процесса

Понятие группы нитей может поддерживаться API уровня нитей. Помимо собственного контекста монитора, для всех нитей группы определен единый групповой контекст. В группу входят все потомки одной заданной нити. По определению для всех нитей группы применяется один и тот же набор событий и, за одним исключением, группа должна быть создана до того, как будут созданы потомки базовой нити. Второе ограничение обусловлено тем обстоятельством, что после создания потомков может стать невозможным определение полного списка нитей с общим предком. Особой разновидностью группы является группа нитей, принадлежащих заданному процессу. Такую группу можно создать в любой момент, независимо от текущего состояния нитей процесса. Это возможно благодаря тому, что в любой момент можно определить список нитей, принадлежащих процессу. Процесс может состоять из нескольких групп нитей, но каждая нить может входить только в одну группу монитора. Поскольку для всех нитей группы должны контролироваться одни и те же события, попытка включить в группу нить, для которой уже определен контекст, приведет к ошибке.

Наследование состояний монитора

Состоянием монитора называется совокупность контролируемых событий, состояние счетчика (включен или выключен) и, в тех случаях, когда это необходимо, список нитей в группе. Состояния счетчиков для разных групп не зависят друг от друга. При создании группы состояние счетчика наследуется у базовой нити группы. Для отдельных нитей группы состояние счетчика представляет собой результат операции AND над их собственным состоянием счетчика и состоянием группового счетчика. Такая организация упрощает включение и выключение счетчиков для групп нитей. Включение или выключение счетчика для группы влияет на все нити, входящие в эту группу. Новые нити полностью наследуют состояние PM у своих предков. Контекст монитора (значения счетчиков) не наследуется, т.е. для новых нитей отсчет начинается с нуля.

Независимость контекстов монитора

Контекст нитей, входящих в группу, не зависит от контекста группы. Благодаря этому можно получать полную и независимую статистику для отдельных нитей группы. Иными словами, для каждой нити и группы нитей отладчик позволяет определить собственный набор событий (единственное исключение - API уровня системы).

Согласование индивидуальных и групповых счетчиков

Счетчики нити обновляются в момент, когда ее работа приостанавливается или возобновляется. Если нить входит в группу, то при этом также обновляются счетчики группы.

Кроме того, к счетчикам нити добавляется содержимое соответствующих аппаратных счетчиков монитора в момент, когда нить выключает счетчики или считывает данные монитора. Если нить входит в группу, то в такой ситуации обновляются и счетчики группы.

По возможности монитор пытается сохранять согласованность между групповыми и индивидуальными счетчиками. Если нить по собственной инициативе покидает группу (т.е. удаляет свой контекст), то соответствующие значения автоматически вычитаются из групповых счетчиков. Если нить, входящая в группу, сбрасывает свои счетчики, групповые счетчики также корректируются. Однако групповые счетчики не корректируются в случае, если нить уничтожается.

Единственная операция, при которой групповые счетчики перестают быть согласованными с индивидуальными, - это обнуление групповых счетчиков для группы, в которую входят несколько нитей. В такой ситуации группа помечается специальным флагом, который возвращается вместе с данными при чтении групповых счетчиков.

Защита

Общие сведения

Общие правила

Перед началом работы с API нужно вызвать функцию pm_init с фильтром событий. В дальнейшем в функции pm_set_program calls можно использовать только те события, которые были возвращены функцией pm_init. Помимо списка событий, функция pm_init возвращает имя процессора, количество доступных счетчиков (2, 4 или 8) и пороговый множитель. Для каждого события возвращается флаг, указывающий, можно ли для него установить порог. Если для события будет установлен порог, то подсчет статистики будет начат только после того, как будет превышен указанный порог, умноженный на пороговый множитель данного процессора.

Контексты монитора нельзя изменять и использовать повторно. Иными словами, между двумя вызовами функции pm_set_program обязательно должна быть вызвана функция pm_delete_program. Это также означает, что на момент создания группы процессов ни у одного из процессов, которые будут входить в группу, не будет контекста.

Все API возвращают 0 при успешном завершении или положительное число в случае ошибки. Для интерпретации кода ошибки можно воспользоваться функцией pm_error.

Информация о группе

Монитор хранит следующую информацию о каждой группе нитей:

Эту информацию можно получить с помощью интерфейсов pm_get_data_mygroup и pm_get_data_group структуры pm_groupinfo_t.

Семь основных вызовов API

В следующих разделах описаны наиболее широко применяемые API системного уровня, которые с незначительными отличиями могут применяться как API первого или третьего уровня для ядра. Отличия в основном заключаются в именах функций и выделены в тексте, например: pm_set_program, pm_set_program_mythread, pm_set_program_group и т.д.

pm_set_program
Задает конфигурацию сбора статистики. С помощью этой функции можно выбрать события для контроля и указать режим сбора статистики. Список доступных событий должен быть получен предварительно с помощью функции pm_init. В списке могут находиться события, для которых можно установить порог.

 
Данные могут собираться в пользовательском режиме или в режиме ядра. Это значение также указывает, нужно ли начать сбор информации немедленно. Для API уровня системы это значение также указывает, будут ли данные собираться для всей системы, или же только для одного процесса и его потомков. Для API уровня групп нитей это значение указывает тип создаваемой группы: базовая нить и ее потомки, либо все нити некоторого процесса.

pm_get_program
Определяет текущие параметры монитора. В число параметров входят режим сбора статистики и набор контролируемых событий. Для событий, для которых можно установить порог, указывается значение порога.

pm_delete_program
Удаляет конфигурацию монитора событий. Это действие обратно действию pm_set_program.

pm_start
Начинает сбор статистики.

pm_stop
Прекращает сбор статистики.

pm_get_data
Выдает значения счетчиков монитора. Данные возвращаются в виде набора 64-разрядных значений, одно значение на каждый аппаратный счетчик. Для API уровня групп дополнительно возвращаются описанные выше сведения о группах.

pm_reset_data
Сбрасывает значения счетчиков монитора. Все счетчики устанавливаются в 0.

Примеры

Пример кода помещен в каталог /usr/samples/pmapi.

Простая программа с одной нитью

#include main()
{
       pm_info_t pminfo;
       pm_prog_t prog;
       pm_data_t data;
       int filter = PM_VERIFIED; /* использовать только проверенные события */
 
       pm_init(filter, &pminfo)
 
       prog.mode.w       = 0;  /* начать отсчет с нуля */
       prog.mode.b.user  = 1;  /* собирать данные в пользовательском режиме */
 
       for (i = 0; i < pminfo.maxpmcs; i++)
                prog.events[i] = COUNT_NOTHING;
 
       prog.events[0]    = 1;  /* выделить первый счетчик под событие 1 */
       prog.events[1]    = 2;  /* выделить второй счетчик под событие 2 */
 
       pm_program_mythread(&prog);
       pm_start_mythread();
 
(1)    ... собственно программа ....
 
       pm_stop_mythread();
       pm_get_data_mythread(&data);
 
       ... вывод результатов ...
}

Пример отладчика для предыдущей программы
Для просмотра данных монитора во время работы программы нужно выполнить следующие действия:

from a debugger at breakpoint (1)
 
       pm_init(filter);
(2)    pm_get_program_thread(pid, tid, &prog);
       ... показать сведения о мониторе ...
 
(3)    pm_get_data_thread(pid, tid);
       ... показать данные монитора ...
 
       pm_delete_program_thread(pid, tid);
       prog.events[0] = 2; /* выделить счетчик 1 под событие 2 */
       pm_set_program_thread(pid, tid, &prog);
 
continue program

Этот алгоритм можно применять и в случаях, когда в программе нет вызовов API монитора. Единственное отличие будет заключаться в том, что вызовы (2) и (3) не будут выполнены, а после продолжения работы программа будет подсчитывать только событие 2 в счетчике 1.

Простой пример для нескольких нитей

Ниже приведен простой пример сбора статистики по одним и тем же событиям для нескольких независимых нитей.

#include pm_data_t data2;
 
void *
doit(void *)
{
 
(1)    pm_start_mythread();
 
       ... собственно программа ....
 
       pm_stop_mythread();
       pm_get_data_mythread(&data2);
}
 
main()
{
       pthread_t threadid;
       pthread_attr_t attr;
       pthread_addr_t status;
 
       ... инициализация, аналогичная предыдущему примеру ...
 
       pm_program_mythread(&prog);
 
       /* настройка режима 1:1 - API нельзя применять с режимом M:N */
       pthread_attr_init(&attr);
       pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM);
       pthread_create(&threadid, &attr, doit, NULL);
 
(2)    pm_start_mythread();
 
       ... собственно программа ....
 
       pm_stop_mythread();
       pm_get_data_mythread(&data);
 
       ... вывод данных для главной нити ...
 
       pthread_join(threadid, &status);
 
       ... вывод результатов для остальных нитей ...
}

Сбор данных для главной и остальных нитей начинается в точках (1) и (2). Первоначально счетчик для основной нити выключен, и вторая нить тоже наследует это значение.

Простой пример сбора статистики для группы

Пример группы с двумя нитями. Тело процедуры инициализации второй нити такое же, как в предыдущем примере.

main()
{
        ... инициализация, аналогичная предыдущему примеру ...
 
        pm_program_mygroup(&prog); /* создание группы */
(1)     pm_start_mygroup()
 
        pthread_create(&threadid, &attr, doit, NULL)
 
(2)     pm_start_mythread(); 
 
        ... собственно программа ....
 
        pm_stop_mythread();
        pm_get_data_mythread(&data)
 
 
        ... вывод данных для главной нити ...
 
        pthread_join(threadid, &status);
 
        ... вывод данных для остальных нитей ...
 
        pm_get_data_mygroup(&data)
 
 
        ... вывод данных для группы ...
}

Команда (2) нужно потому, что команда (1) включает сбор статистики только для группы, а не для индивидуальных нитей. Итоговые данные для группы должны совпадать с суммами по отдельным нитям.

Пример сбора статистики со сбросом счетчиков

Этот пример иллюстрирует пользу групповых счетчиков. Тело дополнительной нити отличается от предыдущих примеров только отсутствием вызова pm_start_mythread(), который в данном случае не нужен.

main()
{
        ... инициализация, аналогичная предыдущему примеру ...
 
        prog.mode.b.count  = 1;  /* начать сбор данных немедленно */
        pm_program_mygroup(&prog);
 
        pthread_create(&threadid, pthread_attr_default, doit, NULL)
 
        ... собственно программа ....
 
        pm_stop_mythread()
        pm_reset_data_mythread()
 
        pthread_join(threadid, &status);
 
        ... печать результатов по вспомогательной нити ...
 
        pm_get_data_mygroup(&data)
 
 
        ... печать результатов по группе ...
}

Счетчики главной нити и группы включены до создания дополнительной нити, поэтому она унаследует их состояние, и для нее сбор данных будет начат сразу.

Итоговое значение data1 должно быть равно значению data, потом у что функция pm_reset_data_mythread автоматически вычла значения счетчиков главной нити из значений счетчиков группы. Таким образом, в каждый момент времени значения групповых счетчиков совпадают с суммами индивидуальных счетчиков для основной и дополнительной нитей несмотря на то, что счетчики главной нити обнуляются.

Связанная информация

Файл pmapi.h


[ Страница назад | Страница вперед | Содержание | Индекс | Библиотека | Юридическая информация | Поиск ]