Почему потоки голодают даже в упреждающей многозадачной ОС (Windows 7)

Я написал приложение Win32 (в Delphi-7, которое является 32-разрядным, используя класс TThread) для создания 100 потоков. Каждый поток при возобновлении будет непрерывно (в цикле) увеличивать 64-разрядный счетчик, связанный с объектом потока (таким образом, блокировка или совместное использование данных).

Если вы позволяете системе работать от 10 до 15 секунд и останавливаться после этого, вы должны увидеть примерно одинаковые подсчеты в каждом из потоков. Но то, что я наблюдал, было то, что 81 поток работал под 400 миллионами циклов, а остальные - более чем в 950 миллионов раз. Самая медленная нить получила только 230 миллионов по сравнению с самыми быстрыми 2111 миллионами.

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

Edit1: Конфигурация машины: Intel i7 Quad Core 3,4 ГГц с включенной поддержкой гиперпотока (8 активных потоков за раз). Запуск 64-разрядной версии Windows-7 (а тестовое приложение - 32 бит)

Edit2 (код потока): тестовое приложение построено с включенной оптимизацией и без какой-либо информации об отладке. Запустите тестовое приложение вне IDE.

type

  TMyThread = class(TThread)
  protected
    FCount: Int64;
  public
    constructor Create;
    procedure Execute; override;
    property Count: Int64 read FCount;
  end;


{ TMyThread }

constructor TMyThread.Create;
begin
  inherited Create(True);
  FCount := 0;
end;  

procedure TMyThread.Execute;
begin
  inherited;
  while not Terminated do
  begin
    Inc(FCount);
  end;
end;

Ответ 1

Круглое планирование - очевидная стратегия для ядра. Это не так, как работает планировщик Windows. Раньше он использовался в Windows 9x days, планировщик, который был очень способен давать разные виртуальные машины равным временем. Но не в ветки NT, начатой ​​группой Дейва Катлера, планирование основано исключительно на приоритете.

Независимо от того, какой поток имеет наивысший приоритет, процессор получает процессор. Там есть еще один кусок кода в Windows, который перерабатывает с приоритетом потока, изменяя его с приоритетом по умолчанию, который он получает при создании потока. Этот код знает о вещах, таких как поток, владеющий окном, которое на переднем плане. Или поток, ожидающий объекта синхронизации, который получил сигнализацию. Или более странные задачи планирования, которые пытаются решить проблему инверсии приоритета. Случайно давая нитку возможность бежать, даже если это не был ее поворот.

Сначала сосредоточьтесь на написании нормального кода. Начиная сотню потоков - это не очень разумная вещь. Вы пытаетесь использовать ресурсы, которые на самом деле отсутствуют, никто не имеет машины со ста ядер. Все же. Силы двух, сначала возьмите машину с 128 ядрами.

Ответ 2

Я воспроизвел и подтвердил ваши результаты. Кроме того, отключение повышения приоритета потока не изменяет распределение. GetThreadTimes сообщает, что потоки с более высокими значениями занимают больше UserTime и наоборот, в то время как KernelTime, похоже, не имеет корреляции со значениями.

Thread 97: 1081,5928 Ke:0 Us:25116161
Thread 98: 1153,8029 Ke:0 Us:26988173
Thread 99: 704,6996  Ke:0 Us:16848108

Очевидно, что некоторые потоки действительно работают чаще, чем другие.

Я не оценил результаты, но я предполагаю, что мы видим Normal distribution, что означает, что результаты зависят от количество факторов, некоторые из которых являются случайными.

Я попытался отключить гиперпоточность (этот вид сгладил результаты), а затем назначил каждому потоку один физический процессор (используя SetThreadAffinityMask). Во втором случае значения были намного ближе друг к другу.

SetThreadAffinityMask(Self.Handle, 1 shl (FIndex mod 4));

Я могу понять, как работа в гиперпотоковой системе может сделать некоторые потоки "несчастливыми": они должны конкурировать с другими потоками на одном физическом процессоре и из-за "soft affinity" к этому виртуальному ядру, которое они получают, чтобы запускать его снова и снова, тем самым забивая меньше, чем другие.

Но о том, почему привязка каждого потока к фиксированному ядру помогает в не гиперпотоковой системе, я не знаю.

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

Все это гадает.

Ответ 3

Windows 7 предназначена для пользователей. Когда ваш первый поток хочет работать, ОС дает ему временной срез. Вы, пользователь, только начали его. К тому моменту, когда 50-й поток подряд (из одного и того же процесса!) Хочет работать, вступают потоки с более высоким приоритетом (фоновые процессы, управляемые самой Windows 7). Это происходит таким образом, чтобы сделать some удачи.

Мы с тобой не хотим, чтобы персональная ОС выдавала процессорное время на основе капризов пользовательских наземных процессов. Мне было бы интересно посмотреть, как сервер 2008 R2 справился с этим. Вы также можете играть с настройкой вкладки "Дополнительно": "Выберите способ распределения ресурсов процессора".

Ответ 4

Некоторые хорошие рассуждения здесь... но есть некоторые особенности, которые следует учитывать. Windows пытается выполнять многозадачность с помощью программного обеспечения. Аппаратное обеспечение не является многозадачным, его использование позволяет делать то, что будет делать система с параллельной обработкой. Под окнами это дает приоритет. во многом... и его запутывание.

Позвольте мне объяснить это так. У меня небольшая программа, которая наблюдает за моими ядрами за их использование. Когда загружаются окна, вы можете подумать, что все ядра будут использоваться. НЕА. По мере загрузки окон начинают использовать другие ядра. Тогда вы могли бы подумать, что при загрузке окон он будет ускорять загрузку, так как он имеет доступ к ядрам. он не ускоряется. Он не использует ядра, чтобы ПОЛНАЯ скорость была загружена быстрее. Даже если окна запустили программы на 1 ядро ​​EACH, когда они загружались и запускались, он ЖДЕТ для их завершения. Если он использовал ВСЕ ядра для обработки каждой программы, он использует Программное обеспечение (примерно в 100 раз медленнее, чем аппаратное), чтобы собрать детали на другом конце. Долгое время Intel хотела изменить аппаратное обеспечение на параллельную обработку, и MS заявила "НЕТ", поскольку их программное обеспечение не предназначено для этого. СЕЙЧАС они пытаются подтолкнуть аппаратный дизайн на основе последовательного интерфейса к точке N. Даже после того, как MS купила программное обеспечение NT. Недавно они забыли использовать большую часть своего дизайна. Должны быть некоторые аппаратные изменения. Там должен быть язык программирования Изменения (MS создала язык программирования), и Core окон необходимо спроектировать еще раз. Не изменилось. ему нужно вернуться и начать с нуля. Удачи с этим. чтобы рассказать вам, сколько лет эта идея мысли... VIVA La 'Amiga.