Системное программирование в среде Windows
Шрифт:
В приведенном ниже фрагменте кода представлен видоизмененный рабочий цикл, выполняющий две операции с семафором.
Можно предложить еще одну разновидность программы.
В уже упоминавшемся примере программы TimedMutualExclusion добавлен шестой параметр, являющийся начальным значением дроссельного счетчика семафора для количества активных потоков. Вы можете поэкспериментировать со значениями этого счетчика, как предлагается в одном из упражнений. На рис. 9.1 показана зависимость различных временных характеристик для шести потоков, синхронизируемых посредством одного объекта CS, от количества активных потоков, изменяющегося в интервале от 1 до 6. Во всех случаях объем выполняемой работы остается одним и тем же, но истекшее время резко увеличивается, когда количество активных потоков превышает 4.
Рис. 9.1. Зависимость производительности от количества потоков
Указанные зависимости получены на устаревших, медленных системах. Для системы Windows 2000 на базе процессора Intel 586 (Pentium), характеризующейся гораздо более высоким быстродействием, соответствующие значения истекшего времени для 1–6 потоков составили (в секундах) 0.8, 0.8, 2.3, 21.2, 28.4 и 29.0, и эти результаты могут быть последовательно воспроизведены. В этом случае ухудшение производительности становилось заметным, начиная уже с 3 активных потоков. В то же время, соответствующие временные характеристики, оцененные с использованием произвольно выбранной совокупности аналогичных систем, оказались примерно постоянными, независимо от количества активных потоков. Результаты некоторых экспериментов дают основания сделать следующие выводы:
• В системе NT5 достигнут значительный прогресс по сравнению с NT4, по следовательно демонстрирующей результаты, аналогичные тем, которые представлены на рис. 9.1.
• Получаемые результаты зависят от того, в каком режиме выполняются операции — приоритетном или фоновом, то есть, находится или не находится фокус на окне приложения, а также от присутствия в системе других выполняемых задач.
• Как правило, мьютексы работают медленнее по сравнению с объектами CS, но в случае NT5 результаты остаются примерно постоянными, независимо от количества активных потоков.
• В SMP-системах наиболее предпочтительным вариантом является дросселирование семафора при значении счетчика равном 1. В этом случае мьютексы становятся ненужными. Так, в случае двухпроцессорной системы Xeon частотой 1.8 ГГц использованные времена для варианта CS при 1, 2
Резюме. Дросселирование семафоров может обеспечивать хорошую производительность как для приоритетных, так и для фоновых операций даже в случае систем, загруженных выполнением других-задач. Дроссели семафоров могут играть очень важную роль в случае SMP-систем, для которых количество активных потоков должно быть равным 1. В том, что касается производительности, семафоры, по-видимому, более эффективны, чем мьютексы.
Родство процессоров
Во всем предшествующем обсуждении предполагалось, что все процессоры SMP-системы доступны всем потокам, а планирование выполнения потоков и распределение процессоров между ними осуществляет ядро. По своей сути такой простой подход является вполне естественным и согласуется с природой SMP-систем. В то же время, имеется возможность назначать потокам определенные процессоры, задавая так называемое родство процессоров (processor affinity). Родство процессоров можно использовать в нескольких ситуациях.
• Процессор может быть назначен небольшой группе, состоящей из одной и более высокоприоритетных потоков.
• Рабочие потоки, конкурирующие за право владения единственным ресурсом, могут быть распределены для выполнения на одном процессоре, что позволяет избежать затруднений с производительностью в случае SMP-систем, о которых перед этим говорилось.
• Возможен и другой вариант, когда потоки распределяются по доступным процессорам.
• Различным процессорам можно назначать различные классы рабочих потоков.
Маски родства системы, процесса и потока
У каждого процесса имеется собственная маска родства процесса (process affinity mask), представляющая собой битовый вектор. Существует также маска родства системы (system affinity mask).
• Маска родства системы отображает процессоры, сконфигурированные в системе.
• Маска родства процесса отображает процессоры, на которых разрешается выполнение потоков данного процесса.
• Каждый индивидуальный поток имеет маску родства потока (thread affinity mask), которая должна представлять собой подмножество значений маски родства процесса. Первоначально маска родства потока совпадает с маской родства процесса.
Существуют функции для получения и установки масок, хотя системную маску вы можете только считывать (получать), а маски потоков — только устанавливать. Функции установки масок используют дескрипторы потоков и процессов, поэтому процессы или потоки могут устанавливать маску родства друг для друга, если имеются соответствующие права доступа, или для самих себя. Установка маски никак не повлияет на поток, уже выполняющийся на процессоре, использование которого вы пытаетесь исключить данной маской.
Для считывания как системных масок родства, так и масок родства процессов используется одна функция — GetProcessAffinityMask. В однопроцессорных системах, включая Windows 9x, все биты маски должны быть равными 1.
Маска родства процесса, которая будет наследоваться любым дочерним процессом, устанавливается при помощи функции SetProcessAffinityMask.