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

Руководство по настройке производительности


Настройка производительности нитей

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

Переменные среды, управляющие работой нитей

В среде libpthreads.a предусмотрен набор переменных для настройки производительности приложения. Разработчикам приложений рекомендуется создавать внешний сценарий оболочки для вызова двоичной версии исполняемых программ, позволяющий изменить значения по умолчанию для этих переменных. К таким переменным относятся:

SPINLOOPTIME=n

Указывает, сколько раз система будет пытаться захватить занятую взаимную блокировку или блокировку с ожиданием из-за занятости, не выполняя дополнительных действий. Примером такого действия может служить обращение к ядру для передачи управления другому процессу. Эта переменная предназначена для использования в системах SMP, где всегда предполагается, что блокировка, занятая другой активной нитью pthread, будет когда-нибудь освобождена. Этот параметр может устанавливаться только в среде libpthreads (для пользовательских нитей). В функциях блокировки ядра для управления временем ожидания снятия блокировки предусмотрен параметр MAXSPIN (см. раздел Команда schedtune -s). Если обычно блокировки освобождаются через небольшой промежуток времени, вы можете увеличить время ожидания блокировки. n - это число попыток захватить блокировку перед тем, как управление будет передано другой нити pthread. n должно быть больше нуля. Значение по умолчанию равно 40.

YIELDLOOPTIME=n

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

AIXTHREAD_SCOPE={P|S}

P задает локальную область действия, совпадающую с процессом (M:N), а S - глобальную область действия (1:1). Допустимы значения P и S. По умолчанию устанавливается локальная область действия.

Данная переменная среды влияет только на нити, при создании которых был установлен атрибут по умолчанию. Он устанавливается в том случае, если параметр attr функции pthread_create() равен нулю.

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

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

Исследования, проведенные в операционной системе AIX версии 4.3.2, показали, что некоторые приложения гораздо быстрее работают в рамках модели 1:1.

AIXTHREAD_GUARDPAGES=n

n задает количество дополнительных страниц, добавляемых в конец стека pthread. Это значение переопределяет значение атрибута, заданного при создании нити pthread. Если приложение использует собственный стек, то дополнительные страницы не создаются. n должно быть больше либо равно нуля. Значение по умолчанию равно нулю.

MALLOCMULTIHEAP={considersize,heaps:n}

Для того чтобы в приложении с несколькими нитями все нити могли использовать вызовы malloc(), free() и realloc(), должно быть создано несколько куч. Если будет создана только одна куча, то вызовы malloc(), free() и realloc() всех нитей будут сериализованы (другими словами, в каждый момент времени только одна нить может вызывать функцию malloc, free или realloc). Это приведет к значительному снижению производительности при работе в многопроцессорной системе. Если будет создано несколько куч, то каждой нити может быть выделена своя куча. Если все кучи заняты, то нить, вызвавшая функцию malloc, free или realloc, будет ждать освобождения кучи. В этом случае также есть сериализация доступа, однако она выполняется намного реже, и поэтому намного меньше влияет на производительность.

Для поддержки такой схемы была изменена процедура блокировки, защищающая нити от возникновения конфликтов при доступе к ресурсам. У каждой кучи есть своя блокировка. Процедура блокировки выбирает кучу не случайным образом, а так, чтобы минимизировать вероятность последующей сериализации доступа. Если в переменной среды MALLOCMULTIHEAP задано значение considersize, то процедура блокировки будет выбирать ту кучу, в которой достаточно свободной памяти для обработки запроса, а не первую освободившуюся кучу.

В этой переменной можно указать несколько параметров через запятую, причем в любом порядке. Например, MALLOCMULTIHEAP=considersize,heaps:3. Допустимы следующие параметры:

heaps:n
Этот параметр позволяет изменить число куч. Если значение n недопустимо (то есть n<=0 или n>32), то устанавливается значение 32.

considersize
Устанавливает другой алгоритм выбора кучи, который пытается минимизировать размер рабочего набора процесса. По умолчанию этот фактор не учитывается и используется самый быстрый алгоритм.

Для переменной среды MALLOCMULTIHEAP значение по умолчанию не задано (по умолчанию применяется только одна куча). Если переменная среды MALLOCMULTIHEAP задана (например, MALLOCMULTIHEAP=1), то приложение с несколькими нитями сможет использовать все 32 кучи. Если вы укажете MALLOCMULTIHEAP=heaps:n, то приложение сможет использовать всего n куч вместо 32.

Переменные для нитей с локальной областью действия

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

AIXTHREAD_MNRATIO=p:k
где k - число нитей ядра, которое будет использоваться для обработки p готовых к запуску нитей pthread. Эта переменная среды определяет коэффициент масштабирования библиотеки. Этот коэффициент требуется при создании и завершении работы нитей pthread. Эта переменная может быть задана только для нитей с локальной областью действия. В случае глобальной области действия эта переменная будет проигнорирована. Значение по умолчанию - 8:1.

AIXTHREAD_SLPRATIO=k:p
где k - число нитей ядра, которые следует зарезервировать для p нитей pthread, находящихся в состоянии ожидания. Это соотношение отражает число нитей ядра, которое должно поддерживаться для обработки ожидающих нитей. В целом для поддержки ожидающих нитей pthread требуется меньше нитей ядра, так как обычно эти нити активизируются последовательно. Это позволяет сэкономить ресурсы ядра. В качестве p и k можно указать любое натуральное число. Если k>p, то устанавливается соотношение 1:1. Значение по умолчанию - 1:12.

AIXTHREAD_MINKTHREADS=n
где n - минимальное число нитей ядра, которое следует использовать. Планировщик библиотеки не возвращает нити ядра, если их число меньше указанного. Нить ядра может быть возвращена практически в любой момент. Обычно нить ядра возвращается в результате завершения нити pthread. Значение по умолчанию равно 8.

Опции отладки нитей

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

После инициализации блокировка добавляется в список. При этом предполагается, что ее еще нет в списке. Список компонуется динамически, поэтому в случае большого списка проверка отсутствия новой блокировки в списке потребовала бы значительного объема ресурсов. Ситуация осложняется тем, что список защищен с помощью блокировки (dbx__mutexes), которую необходимо было бы захватывать на время поиска в списке. В этом случае все остальные вызовы функции pthread_mutex_init() должны были бы ожидать окончания поиска.

Если перечисленные ниже переменные среды равны OFF (значение по умолчанию - ON), то соответствующий список отладки создаваться не будет. Это означает, что команда dbx (или любой другой отладчик, использующий библиотеку функций отладки pthread) не будет показывать ни одного объекта в списке.

Для изменения значений этих переменных вызовите следующую команду:

# export имя-переменной=OFF

Настройка производительности нитей - Обзор

Администратор может выбрать ту модель нитей, которая лучше всего подходит для выполнения приложения. Исследования, проведенные в операционной системе AIX версии 4.3.2, показали, что некоторые приложения гораздо быстрее работают в рамках модели 1:1. Это важно учитывать, поскольку по умолчанию в данной операционной системе, как и в AIX 4.3.1, применяется модель M:N. Установив для процесса значение переменной среды AIXTHREAD_SCOPE=S, вы можете задать модель нитей 1:1, а затем сравнить скорость работы программы в обеих моделях.

Если вы заметите, что приложение создает и удаляет нити, это могут быть нити ядра, появившиеся из-за высокого соотношения пользовательских нитей и нитей ядра (8:1). В сочетании с высокой нагрузкой на планировщик библиотеки это может отрицательно повлиять на производительность. С другой стороны, если в системе работает несколько тысяч пользовательских нитей, то затраты на планирование их выполнения в пользовательском пространстве в рамках библиотеки могут быть меньше затрат, которые потребуются на управление тысячами нитей ядра. Если при работе с нитями pthread вы заметите резкое снижение производительности, то в первую очередь нужно попытаться изменить область действия нитей. Во многих случаях нити с глобальной областью действия работают быстрее.

Если приложение выполняется в системе SMP, и пользовательская нить не может захватить взаимную блокировку, то она повторит попытку захвата не более 40 раз. Довольно часто взаимные блокировки освобождаются через небольшой промежуток времени, поэтому иногда имеет смысл увеличить количество попыток. Если с увеличением количества CPU производительность уменьшается, то, скорее всего, это связано с блокировками. В этом случае рекомендуется увеличить время активного ожидания блокировки, задав переменную среды SPINLOOPTIME=n, где n - число попыток захвата. В некоторых случаях это значение может исчисляться тысячами - все зависит от мощности и числа процессоров. После того как число попыток захвата достигнет максимума, нить может быть приостановлена до освобождения взаимной блокировки, либо она может вызвать функцию yield() и уступить процессор другой нити, оставшись в очереди выполнения. По умолчанию нить приостанавливается, однако если вы зададите переменную среды YIELDLOOPTIME, то нить будет приостановлена только после того, как она указанное число раз уступит управление другой нити. Каждый раз, когда нити будет возвращаться управление, она может попробовать захватить взаимную блокировку.

Некоторые пользовательские процессы с несколькими нитями, активно использующие подсистему malloc, будут работать быстрее, если перед их запуском экспортировать переменную среды MALLOCMULTIHEAP=1. В частности, это рекомендуется делать для программ C++ с несколькими нитями, так как они используют подсистему malloc при вызове конструкторов и деструкторов. Повышение производительности будет лучше всего заметно при запуске пользовательских приложений с несколькими нитями в системе SMP, в особенности если для нитей будет установлена глобальная область действия (соотношение M:N равно 1:1). Однако в некоторых случаях повышения производительности можно добиться и в другой среде, в частности, в однопроцессорной системе.


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