Как планировщик Windows 10 справляется с Hyper Threading, поскольку Core CPU по умолчанию отключена для процессоров Intel?
Я использую Windows 10 (1607) на процессоре Intel Xeon E3-1231v3 (Haswell, 4 физических ядра, 8 логических ядер).
Когда я впервые установил Windows 7 на эту машину, я заметил, что четыре из восьми логических ядер были припаркованы до тех пор, пока приложению не потребовалось более 4 потоков. С помощью монитора ресурсов Windows можно проверить, припаркованы ли ядра или нет ( пример). Насколько я понимаю, это важная техника для поддержания баланса потоков между физическими ядрами, как объясняется на веб-сайте Microsoft: " Алгоритм и инфраструктура парковки ядра также используются для балансировки производительности процессора между логическими процессорами в клиентских системах Windows 7 с помощью процессоры с технологией Intel Hyper-Threading ".
Однако после обновления до Windows 10 я заметил, что нет базовой парковки. Все логические ядра активны все время, и когда вы запускаете приложение, используя менее четырех потоков, вы можете видеть, как планировщик равномерно распределяет их по всем логическим ядрам процессора. Сотрудники Microsoft подтвердили, что базовая парковка отключена в Windows 10.
Но мне интересно, почему? Какова была причина этого? Есть ли замена и если да, то как это выглядит? Разработала ли Microsoft новую стратегию планировщика, которая сделала парковку ядра устаревшей?
Приложение:
Вот пример того, как базовая парковка, представленная в Windows 7, может повысить производительность (по сравнению с Vista, у которой пока нет функции базовой парковки). Вы можете видеть, что в Vista HT (Hyper Threading) снижает производительность, а в Windows 7 - нет:
( источник)
Я попытался включить Core Parking, как упоминалось здесь, но я заметил, что алгоритм Core Parking больше не поддерживает технологию Hyper Threading. Он припарковал ядра 4,5,6,7, в то время как он должен был припарковать ядра 1,3,5,7, чтобы избежать назначения потоков одному и тому же физическому ядру. Windows перечисляет ядра таким образом, что два последовательных индекса принадлежат одному физическому ядру. Очень странно. Кажется, Microsoft все испортила в корне. И никто не заметил...
Кроме того, я сделал несколько тестов процессора, используя ровно 4 потока.
Сродство процессора установлено ко всем ядрам (Windows defualt):
Среднее время работы: 17.094498, стандартное отклонение: 2.472625
Привязка к ЦП устанавливается для каждого другого ядра (чтобы оно работало на разных физических ядрах, наилучшее планирование):
Среднее время работы: 15.014045, стандартное отклонение: 1.302473
Привязка ЦП установлена на худшее из возможных расписаний (четыре логических ядра на двух физических ядрах):
Среднее время работы: 20.811493, стандартное отклонение: 1.405621
Так что есть разница в производительности. И вы можете видеть, что планирование по умолчанию в Windows ранжируется между лучшим и наихудшим планированием, как мы ожидаем, что это произойдет с планировщиком, не поддерживающим гиперпоточность. Однако, как указано в комментариях, могут быть и другие причины, такие как меньшее количество переключений контекста, логический вывод приложений и т. Д. Таким образом, мы до сих пор не имеем однозначного ответа.
Исходный код для моего теста:
#include <stdlib.h>
#include <Windows.h>
#include <math.h>
double runBenchmark(int num_cores) {
int size = 1000;
double** source = new double*[size];
for (int x = 0; x < size; x++) {
source[x] = new double[size];
}
double** target = new double*[size * 2];
for (int x = 0; x < size * 2; x++) {
target[x] = new double[size * 2];
}
#pragma omp parallel for num_threads(num_cores)
for (int x = 0; x < size; x++) {
for (int y = 0; y < size; y++) {
source[y][x] = rand();
}
}
#pragma omp parallel for num_threads(num_cores)
for (int x = 0; x < size-1; x++) {
for (int y = 0; y < size-1; y++) {
target[x * 2][y * 2] = 0.25 * (source[x][y] + source[x + 1][y] + source[x][y + 1] + source[x + 1][y + 1]);
}
}
double result = target[rand() % size][rand() % size];
for (int x = 0; x < size * 2; x++) delete[] target[x];
for (int x = 0; x < size; x++) delete[] source[x];
delete[] target;
delete[] source;
return result;
}
int main(int argc, char** argv)
{
int num_cores = 4;
system("pause"); // So we can set cpu affinity before the benchmark starts
const int iters = 1000;
double avgElapsedTime = 0.0;
double elapsedTimes[iters];
for (int i = 0; i < iters; i++) {
LARGE_INTEGER frequency;
LARGE_INTEGER t1, t2;
QueryPerformanceFrequency(&frequency);
QueryPerformanceCounter(&t1);
runBenchmark(num_cores);
QueryPerformanceCounter(&t2);
elapsedTimes[i] = (t2.QuadPart - t1.QuadPart) * 1000.0 / frequency.QuadPart;
avgElapsedTime += elapsedTimes[i];
}
avgElapsedTime = avgElapsedTime / iters;
double variance = 0;
for (int i = 0; i < iters; i++) {
variance += (elapsedTimes[i] - avgElapsedTime) * (elapsedTimes[i] - avgElapsedTime);
}
variance = sqrt(variance / iters);
printf("Average running time: %f, standard deviation: %f", avgElapsedTime, variance);
return 0;
}
1 ответ
Да, я мог бы рассказать вам историю, но вы будете ненавидеть это, и я буду ненавидеть писать это:-)
Короткая версия - Win10 испортил все, что мог, и находится в постоянном состоянии голодающих ядер из-за системной проблемы, известной как переподписка процессора (слишком много потоков, никто не может их обслуживать, что-то задыхается в любой точке, навсегда). Вот почему они отчаянно нуждаются в этих поддельных процессорах, сокращают базовый таймер планировщика до 1 мс и не могут позволить вам парковаться. Это просто опалило бы систему. Откройте Process Explorer и добавьте количество потоков, теперь посчитайте:-)
Был представлен API-интерфейс CPU Sets, чтобы дать хотя бы некоторый шанс для борьбы тем, кто знает и имеет время написать код для борьбы со зверем. Вы можете де-факто парковать поддельные процессоры, помещая их в набор процессоров, который вы никому не дадите, и создав набор по умолчанию, чтобы бросать его пираньям. Но вы не можете сделать это на клиентских sku-s (технически это может быть просто невозможно), так как ядро перейдет в состояние паники и либо полностью проигнорирует наборы процессоров, либо некоторые другие вещи начнут падать. Он должен защищать целостность системы любой ценой.
В целом положение дел в целом является табу, поскольку для этого потребуются серьезные переписки, и каждый выберет несерьезные темы и признает, что они все испортили. Гиперпотоки на самом деле должны быть постоянно отключены (они нагревают ядра под реальной нагрузкой, ухудшают производительность и дестабилизируют HTM - основная причина, по которой он никогда не стал основным). Крупные магазины SQL Server делают это в качестве первого шага установки, как и Azure. Bing нет, они запускают серверы с де-факто настройкой клиента, так как им нужно было бы гораздо больше ядер, чтобы осмелиться переключиться. Проблема просочилась в Server 2016.
SQL Server является единственным реальным пользователем наборов ЦП (как обычно:-), 99% продвинутых возможностей в Win всегда выполнялись только для SQL Server, начиная с суперэффективной обработки файлов с отображением в памяти, которая убивает людей, пришедших из Linux с тех пор они предполагают различную семантику).
Чтобы безопасно играть с этим, вам понадобится минимум 16 ядер для клиентского компьютера, 32 для сервера (что на самом деле делает что-то реальное:-) Вы должны установить как минимум 4 ядра по умолчанию, чтобы ядро и системные службы едва дышали. но это все равно всего лишь двухъядерный эквивалент ноутбука (у вас все еще есть постоянный удушье), что означает 6-8, чтобы позволить системе дышать правильно.
Win10 нужно 4 ядра и 16 ГБ, чтобы едва дышать. Ноутбуки обходятся без 2-х ядерных и 2-х фальшивых "процессоров", если ничего делать не нужно, так как их обычное распределение работы таково, что всегда есть достаточно вещей, которые все равно нужно ждать (длинная очередь на memaloc "очень помогает":-),
Это все равно не поможет вам с OpenMP (или каким-либо автоматическим распараллеливанием), если у вас нет способа явно указать ему использование вашего набора ЦП (отдельные потоки должны быть назначены для набора ЦП) и ничего больше. Вам все еще нужно установить соответствие процессов, это предварительное условие для наборов процессоров.
Сервер 2k8 был последним хорошим (да, это также означает и Win7:-). Люди массово загружали ТБ за 10 минут им и SQL Server. Теперь люди хвастаются, что могут загрузить его за один час - под Linux:-) Так что, скорее всего, положение "не там" тоже намного лучше. У Linux были CPU Sets задолго до Win.