Читаем без скачивания Системное программирование в среде Windows - Джонсон Харт
Шрифт:
Интервал:
Закладка:
Существуют функции для получения и установки масок, хотя системную маску вы можете только считывать (получать), а маски потоков — только устанавливать. Функции установки масок используют дескрипторы потоков и процессов, поэтому процессы или потоки могут устанавливать маску родства друг для друга, если имеются соответствующие права доступа, или для самих себя. Установка маски никак не повлияет на поток, уже выполняющийся на процессоре, использование которого вы пытаетесь исключить данной маской.
Для считывания как системных масок родства, так и масок родства процессов используется одна функция — GetProcessAffinityMask. В однопроцессорных системах, включая Windows 9x, все биты маски должны быть равными 1.
BOOL GetProcessAffinityMask(HANDLE hProcess, LPDWORD lpProcessAffinityMask, LPDWORD lpSystemAffinityMask)
Маска родства процесса, которая будет наследоваться любым дочерним процессом, устанавливается при помощи функции SetProcessAffinityMask.
BOOL SetProcessAffinityMask(HANDLE hProcess, DWORD dwProcessAffinityMask)
В документации Microsoft говорится, что значение новой маски должно быть строгим подмножеством (proper subset) значений масок, получаемых с помощью функции GetProcessAffinityMask. Как показывает несложный эксперимент, включенный в код программы TimedMutualExclusion, новая маска может быть той же, что и маска системы или предыдущая маска процесса. Однако упомянутое ограничение не может быть справедливым, ибо в таком случае вы были бы лишены возможности восстанавливать маску родства системы до предыдущего значения.
В Windows 9x поддержка SMP, а также функций манипулирования масками процессов не поддерживаются. Новые значения масок влияют на все потоки, принадлежащие данному процессу.
Для установки маски родства потоков применяется аналогичная функция.
DWORD SetThreadAffinityMask(HANDLE hThread, DWORD dwThreadAffinityMask)
Типы возвращаемых значений этих функций не согласуются между собой. Типом возвращаемого значения функции SetThreadAffinityMask является DWORD, a не BOOL, но результат остается одним и тем же (1 — в случае успеха, 0 — в противном случае). Функция SetThreadAffinityMask работает и под управлением Windows 9х, но маска должна быть единичной, что не дает никакого прока. Кроме того, невзирая на документацию, новая маска не обязательно должна быть строгим подмножеством системной маски.
Функция SetThreadIdealProcessor является видоизменением функции SetThreadAffinityMask. Вы указываете предпочтительный ("идеальный") номер процессора (а не маску), и планировщик назначает потоку этот процессор, если такая возможность имеется, или назначит ему другой процессор, если предпочтительный процессор недоступен. Возвращаемым значением функции является номер предыдущего предпочтительного процессора, если таковой был назначен.
Определение количества процессоров в системе
Фактически, на количество процессоров, установленных в системе, указывает маска родства системы; чтобы его определить, вам достаточно подсчитать количество ненулевых битов в маске. Вместе с тем, гораздо проще вызвать функцию GetSystemInfo, возвращающую структуру SYSTEM_INFO, среди полей которой имеются поля, содержащие количество процессоров и активную маску процессоров, которая совпадает с маской системы. Простая программа и проект Version, доступные на Web-сайте книги, отображают эту информацию вместе с версией Windows.
Гиперпотоки и счетчик процессоров
Процессоры Intel Pentium 4 и Xeon поддерживают механизм HyperThreading (гиперпотоки), посредством которого состояния ожидания, возникающие в процессе выполнения потока, используются для выполнения другого потока. Для поддержки этого средства используется второй регистровый файл, что вполне осуществимо, поскольку архитектура процессоров х8б характеризуется сравнительно небольшим количеством регистров. Xeon или любой другой процессор, поддерживающий гиперпоточную обработку, воспринимается функциями GetSystemInfo и GetProcessAffinityMask как одиночный процессор.
Порты завершения ввода/вывода
В главе 14 описываются порты завершения ввода/вывода, которые предоставляют другой механизм, позволяющий избежать состязательности между потоками путем ограничения их количества. Порты завершения ввода/вывода дают возможность небольшому количеству потоков управлять большим количеством параллельно выполняющихся операций ввода/вывода. Отдельные операции ввода/вывода начинают выполняться в асинхронном режиме и, вообще говоря, не завершаются сразу же после того, как осуществляется возврат из функции чтения или записи. В то же время, обработка данных по мере завершения операций, ожидающих выполнения, поручается одной из небольшого числа рабочих потоков. В главе 14 приведен пример сервера, связывающегося с удаленными клиентами (программа 14.4).
Рекомендации по повышению производительности и возможные риски
Многопоточные приложения предоставляют значительные программные преимущества, включая возможность использования более простых моделей программирования и повышение быстродействия программ. Вместе с тем, существует ряд факторов, которые способны оказывать на производительность заметное отрицательное влияние, с трудом поддающееся прогнозированию, причем характер этого влияния может быть различным на различных компьютерах, даже если на них и выполняются одни и те же версии Windows. Некоторые простые рекомендации, суммирующие сведения, изложенные в настоящей главе, помогут вам минимизировать эти риски. Часть этих рекомендаций, равно как и многие из советов по проектированию, отладке и тестированию программ, которые приводятся в следующей главе, в переработанном виде взята из [6].
• Критически относитесь к аргументации предположительного и теоретического характера, касающейся вопросов производительности, которая часто звучит убедительно, но на практике оказывается ошибочной. Проверяйте предположения на простых прототипах программ, таких как TimedMutualExclusion, или проверяйте их действенность на альтернативных вариантах реализации своего приложения.
• Используйте для тестирования производительности приложений как можно более широкий круг систем из числа тех, которые доступны вам. Полезно запускать программу с использованием самых различных конфигураций памяти, типов процессоров, версий Windows и количества процессоров. Приложение может продемонстрировать очень высокую производительность на одной системе, но крайне низкую на другой; см. обсуждение программы 9.1.
• Блокирование потребляет значительные системные ресурсы; пользуйтесь этим средством лишь при настоятельной необходимости. Предоставляйте возможность удержания (владения) мьютекса или объекта CS строго в пределах лишь необходимого времени. Варьирование параметров задержки или точек "засыпания" демонстрирует снижение производительности с увеличением длительности периодов блокирования.
• Используйте различные мьютексы для различных ресурсов, чтобы уменьшить степень детализации блокировок настолько, насколько это возможно. В частности, старайтесь не использовать глобальные блокировки.
• Условия высокой состязательности между блокировками затрудняют достижение высокой производительности. Чем выше частота блокирования и разблокирования потоков, тем заметнее снижается производительность. Ухудшение производительности с увеличением количества потоков может быть очень резким, заметно отклоняясь от простой линейной зависимости.
• Объекты CS предоставляют эффективный упрощенный механизм блокирования при небольшом количестве конкурирующих потоков, но в некоторых случаях мьютексы обеспечивают лучшую производительность. При использовании объектов CS в критических по отношению к производительности SMP-приложениях возможно настройка производительности с помощью спин-счетчиков.
• Семафоры могут помочь уменьшить количество конкурирующих активных потоков, не вынуждая вас менять программную модель.
• Переход на SMP-систему может приводить к неожиданному ухудшению производительности в тех случаях, когда производительность, казалось бы, могла только улучшиться. Сохранить приемлемую производительность в подобных ситуациях позволяют методики, уменьшающие состязательность между потоками и использующие маски родства потоков.
• Заметное влияние на производительность оказывает также выбор модели — сигнальной или широковещательной, о чем более подробно говорится в главе 10.
• Используйте доступные стандартные программы протоколирования, позволяющие оценивать время выполнения различных функций и анализировать факторы, влияющие на производительность, что поможет вам лучше представить себе поведение потоков в вашей программе и определить участки кода, выполнение которых занимает наибольшее время.