Jak harmonogram Windows 10 radzi sobie z Hyper Threading, skoro Core Parking jest domyślnie wyłączony dla procesorów Intel?

6

Korzystam z systemu Windows 10 (1607) na procesorze Intel Xeon E3-1231v3 (Haswell, 4 rdzenie fizyczne, 8 rdzeni logicznych ).

Kiedy po raz pierwszy zainstalowałem system Windows 7 na tym komputerze, zauważyłem, że cztery z ośmiu rdzeni logicznych były zaparkowane, dopóki aplikacja nie wymagała więcej niż 4 wątków. Za pomocą monitora zasobów Windows można sprawdzić, czy rdzenie są zaparkowane, czy nie ( przykład ). O ile rozumiem, jest to ważna technika utrzymywania równowagi wątków w rdzeniach fizycznych, jak wyjaśniono na stronie internetowej Microsoft : „ Algorytm Core Parking i infrastruktura są również wykorzystywane do równoważenia wydajności procesorów między procesorami logicznymi w systemach klienckich Windows 7 z procesory, które zawierają technologię Intel Hyper-Threading ”.

Jednak po uaktualnieniu do systemu Windows 10 zauważyłem, że nie ma parkingu podstawowego. Wszystkie rdzenie logiczne są aktywne przez cały czas, a po uruchomieniu aplikacji przy użyciu mniej niż czterech wątków można zobaczyć, jak program planujący równo rozdziela je na wszystkie logiczne rdzenie procesora. Pracownicy Microsoft potwierdzili, że Core Parking jest wyłączony w Windows 10 .

Ale zastanawiam się dlaczego? Jaki był tego powód? Czy jest zamiennik, a jeśli tak, jak to wygląda? Czy Microsoft wdrożył nową strategię planowania, która sprawiła, że ​​parkowanie rdzeni stało się przestarzałe?


Dodatek:

Oto przykład, w jaki sposób Core Core Parking wprowadzony w Windows 7 może poprawić wydajność (w porównaniu do Visty, która nie miała jeszcze funkcji Core Parking). Widać, że w systemie Vista HT (Hyper Threading) szkodzi wydajności, podczas gdy w systemie Windows 7 nie:

wprowadź opis zdjęcia tutaj

wprowadź opis zdjęcia tutaj

( źródło )

Próbowałem włączyć Core Parking, jak wspomniano tutaj , ale zauważyłem, że algorytm Core Parking nie jest już świadomy Hyper Threading. Zaparkował rdzenie 4,5,6,7, podczas gdy powinien mieć zaparkowany rdzeń 1,3,5,7, aby uniknąć przypisania wątków do tego samego rdzenia fizycznego. Windows wylicza rdzenie w taki sposób, że dwa kolejne wskaźniki należą do tego samego rdzenia fizycznego. Bardzo dziwny. Wygląda na to, że Microsoft całkowicie to popsuł. I nikt nie zauważył ...

Ponadto wykonałem testy wydajności procesora, używając dokładnie 4 wątków.

Powinowactwo procesora ustawione na wszystkie rdzenie (Windows defualt):

Średni czas pracy: 17.094498, odchylenie standardowe: 2.472625

Powinowactwo procesora ustawione na każdym innym rdzeniu (aby działało na różnych rdzeniach fizycznych, najlepsze możliwe planowanie):

Średni czas pracy: 15.014045, odchylenie standardowe: 1.302473

Powinowactwo procesora ustawione na najgorsze możliwe planowanie (cztery rdzenie logiczne na dwóch rdzeniach fizycznych):

Średni czas pracy: 20,811493, odchylenie standardowe: 1,405621

Więc nie jest różnica wydajności. I możesz zobaczyć, że planowanie deficytu w systemie Windows plasuje się pomiędzy najlepszym i najgorszym możliwym, ponieważ spodziewalibyśmy się, że stanie się tak w przypadku harmonogramu świadomego bez hipertekstu. Jednak, jak wskazano w komentarzach, mogą być za to odpowiedzialne inne przyczyny, takie jak mniejsza liczba przełączeń kontekstu, wnioskowanie przez aplikacje monitorujące itp. Więc nadal nie mamy ostatecznej odpowiedzi.

Kod źródłowy mojego testu porównawczego:

#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;
}
manuel
źródło
1
Trzymaj się, „cztery z ośmiu rdzeni”? Wspomniałeś właśnie, że twój procesor ma tylko 4 rdzenie i 8 wątków . Czy faktycznie obowiązuje tutaj „parking podstawowy”?
grawity
1
@grawity W teorii parkowanie rdzenia miało umożliwić procesorowi zaparkowanie połowy rdzenia z technologią wielowątkową. Rozumiem, że pamięć podręczna na rdzeń została podzielona na dwa logiczne (hiperwątkowe) rdzenie, a po zaparkowaniu jeden mógł zostać zwolniony i zasadniczo umożliwić jednemu rdzeniu dostęp do pełnej pamięci podręcznej, która była z nim połączona. Może to zapewnić niewielką poprawę dla zadań o małej liczbie wątków, przy jednoczesnym zachowaniu zdolności do przywracania hiperwątkowości i pełnego wykorzystania rdzenia w zadaniach wielowątkowych.
Mokubai
@grawity: Przepraszamy za zamieszanie. Miałem na myśli osiem „rdzeni logicznych”, tj. To tylko wirtualny rdzeń.
manuel
Procesory @mokubai Intel mogą obsługiwać dwa wątki na jednym rdzeniu procesora, co powoduje wzrost wydajności o około 30%. Załóżmy jednak, że masz aplikację z dwoma aktywnymi wątkami. Teraz chcesz, aby działały na dwóch różnych rdzeniach fizycznych, co idealnie podwaja wydajność w porównaniu do pojedynczego wątku. Ale jeśli działają na tym samym fizycznym rdzeniu z hiperwątkowością, działają tylko o 30% szybciej niż pojedynczy wątek. Właśnie tam pojawia się parking podstawowy i unika się tej ostatniej sytuacji.
manuel
Procesory AMD Zen mają podobny problem, ale AMD niedawno wydało oświadczenie, które zasadniczo wskazywało, że problemem było narzędzie przeprowadzające kontrole wydajności, a nie sam system Windows na podstawie wszystkich przeprowadzonych badań. Więc zanim obwiniesz Microsoft za cokolwiek, sprawdź, czy narzędzia nie są winne ...
Ramhound

Odpowiedzi:

-2

Huh, mógłbym opowiedzieć ci historię, ale nienawidzisz jej, a ja nienawidzę pisać :-)

Krótka wersja - Win10 spieprzył wszystko, co mógł, i jest w ciągłym stanie głodujących rdzeni z powodu problemu systemowego znanego jako nadmierna subskrypcja procesora (zbyt wiele wątków, nikt nigdy nie może ich serwisować, coś dusi się w dowolnym momencie, na zawsze). Dlatego desperacko potrzebuje tych fałszywych procesorów, skraca podstawowy zegar harmonogramu do 1 ms i nie może pozwolić ci nic parkować. Spaliłoby to system. Otwórz Process Explorer i dodaj liczbę wątków, teraz zrób matematykę :-)

Interfejs API zestawów procesorów został wprowadzony, aby dać przynajmniej szansę walki tym, którzy znają i mają czas na napisanie kodu do walki z bestią. Możesz de facto zaparkować fałszywe procesory, umieszczając je w zestawie procesorów, którego nikomu nie dasz, i utwórz domyślny zestaw, aby wyrzucić go do piranii. Ale nie możesz tego zrobić na skryptach klienta (technicznie rzecz biorąc, nie będzie to honorowane), ponieważ jądro przejdzie w stan paniki i albo całkowicie zignoruje zestawy procesorów, albo niektóre inne rzeczy zaczną się zawieszać. Musi bronić integralności systemu za wszelką cenę.

Cały ten stan rzeczy jest z reguły tabu, ponieważ wymagałoby to poważnych przeróbek i unicestwienia liczby niepoważnych wątków i przyznania się do pomyłki. Hyperthreads faktycznie muszą zostać trwale wyłączone (nagrzewają rdzenie pod rzeczywistym obciążeniem, obniżają wydajność i destabilizują HTM - główny powód, dla którego nigdy nie stał się głównym nurtem). Duże sklepy SQL Server robią to jako pierwszy krok konfiguracji, podobnie jak Azure. Bing nie jest, działają serwery z de facto konfiguracją klienta, ponieważ będą potrzebować znacznie więcej rdzeni, aby odważyć się na zmianę. Problem dotyczył serwera 2016.

SQL Server jest jedynym prawdziwym użytkownikiem zestawów procesorów (jak zwykle :-), 99% doskonałych rzeczy w Win zawsze było zrobionych tylko dla SQL Server, zaczynając od super wydajnej obsługi plików mapowanych w pamięci, które zabijają ludzi pochodzących z Linuksa od czasu przyjmują inną semantykę).

Aby grać z tym bezpiecznie, potrzebujesz 16 rdzeni min dla skrzynki klienta, 32 dla serwera (to faktycznie robi coś prawdziwego :-) Musisz ustawić co najmniej 4 rdzenie w zestawie domyślnym, aby jądro i usługi systemowe ledwo mogły oddychać ale nadal jest to tylko dwurdzeniowy odpowiednik laptopa (wciąż masz wieczne duszenie), co oznacza 6-8, aby system mógł oddychać poprawnie.

Win10 potrzebuje 4 rdzeni i 16 GB, aby ledwo oddychać. Laptopy uciekają z 2 rdzeniami i 2 fałszywymi „procesorami”, jeśli nie ma nic do zrobienia, ponieważ ich zwykła dystrybucja pracy jest taka, że ​​zawsze jest wystarczająco dużo rzeczy, które muszą czekać (długa kolejka na memaloc „bardzo pomaga” :-) .

To nadal nie pomoże ci w OpenMP (ani w żadnej automatycznej równoległości), chyba że masz sposób na to, aby wyraźnie użyć zestawu procesorów (poszczególne wątki należy przypisać do zestawu procesorów) i nic więcej. Nadal potrzebujesz również zestawu koligacji procesów, jest to warunek wstępny dla zestawów procesorów.

Serwer 2k8 był ostatnim dobrym (tak, to znaczy także Win7 :-). Ludzie ładowali masowo TB w 10 minut wraz z nim i SQL Server. Teraz ludzie chwalą się, czy potrafią załadować go w ciągu godziny - pod Linuksem :-) Tak więc są szanse, że stan rzeczy nie jest o wiele lepszy „tam”. Linux miał zestawy procesorów na długo przed Win.

ZXX
źródło
Ta „odpowiedź” ma niewiele wspólnego z rzeczywistością. Na początek liczba wszystkich wątków w systemie nie ma nic wspólnego z „nadsubskrypcją” procesora; większość wątków spędza większość czasu NIE chcąc biegać („czekając” na IO lub podobny).
Jamie Hanrahan,
Ponadto licznik czasu harmonogramu - czyli przedział, według którego liczony jest wątek „kwantowy” (timeslcie) - nadal wynosi 15,625 ms (64 Hz). Domyślny timer interwału jest nadal taki. Można go programowo zmienić na zaledwie pół milisekundy na obecnych platformach, a niektóre aplikacje multimedialne i inne aplikacje, które wymagają precyzyjnego pomiaru czasu, robią to - ale przedziały czasowe wątków są nadal wielokrotnościami 15.625 ms.
Jamie Hanrahan,