Hyperthreads vs Cores (Auslastung usw)

SlaterTh90

Lt. Commander
Registriert
Nov. 2014
Beiträge
1.853
Hallo CB Forum,
mir ist in letzter Zeit einiges an interessantem Verhalten der CPU (Xeon e3-1231 v3, 4 Kerne 8 Threads) aufgefallen, da ich des öfteren mal im Taskmanager die Kerne/Threads beim zocken usw beobachtet habe. So nutzt etwa GTA5 alle CPUs mit geraden Nummern (0,2,4,6, dürften die echten Kerne sein) deutlich stärker aus. Andere Spiele wiederum nutzten alle zu ähnlichen Leveln, zum Beispiel BF4. Dann gibt es auch noch solche Spiele wie Minecraft, die irgendwie nur den letzten Thread (CPU 7) nutzen wollen, oder Star Citizen welches es sich zur Aufgabe gemacht hat CPU2 und CPU6 regelrecht zu missbrauchen während alle anderen zuschauen. Ich denke die Entwickler legen vorher fest welcher Kern zu nutzen ist im Falle einer bestimmten Threadanzahl oder? Windows im belegt die "echten" Kerne auf jeden Fall auch stärker.
Deswegen die Frage: Macht es einen unterschied ob ein Hyperthread genutzt wird oder ein echter Kern wenn z.b. CPU6 bei 3-30% läuft und dann CPU7 (sollte der dazugehörige HT sein) ~100% Last hat? Intels Technik funktioniert doch glaube ich so, dass immer wenn der Kern einen Teil der Befehlsabfolge nicht braucht, eine andere Aufgabe vom HT eingeschoben wird (wofür manche Baugruppen der CPU glaube ich doppelt vorhanden sind per Kern). Es sollte also doch kaum einen Unterschied machen in der am Ende gelieferten Leistung, denn der HT müsste ja fast durchgehen Sachen einschieben können, oder nicht?
 
in der Regel kümmert sich das OS darum, was wann und wie lange auf der CPU laufen darf (scheduling). Der Entwickler sagt idR auch nur, dass er so und so viel Threads nutzen will. Auf welchem Kern das dann am Ende läuft, entscheidet aber wieder das OS. Je nach Os, lässt sich aber trotzdem beeinflussen, auf welchem Kern etwas läuft (änhlich der Zuweisung im task-Manager). Ob man dabei aber explizit sagen kann, dass ein kern bevorzugt werden soll, oder nur, dass nur die explizit angegebenen Kerne genutzt werden können und kein anderer, kann ich jetzt nicht sagen (unter Windows ists vermutlich eher die direkte und dauerhafte Zuweisung). nomralerweise regelt das OS aber besser, als das man da irgendetwas festlegen müsste.
Das OS dürfte wissen, welcher Kern virtuell ist und welcher physisch und entscheidet auch demnach.
 
Zuletzt bearbeitet:
2 normale kerne sind schneller als 1 normaler + hyperthreading^^ somit macht es einen unterschied ob 2 normale kerne oder 1 normaler und hyperthreading genutzt werden.
 
(0,2,4,6, dürften die echten Kerne sein)
Es gibt keine "echten" Kerne und keine "unechten". Ob bei einem Kern nun Thread 0 auf 100% läuft Thread 1 gar nichts tut oder umgekehrt, macht absolut keinen Unterschied - problematisch ist es nur, wenn beide Threads auf einem Kern etwas zu tun haben, und deswegen - das hat Hellblazer ja auch schon gesagt - versucht das OS eben die Last nach Möglichkeit so zu verteilen, dass pro Kern nur ein Thread läuft. Ganz so toll skaliert Intels HT in der Regel nämlich doch nicht, auch wenn der maximale Leistungszuwachs tatsächlich bei 100% liegt.
 
Interessant. Gibt es Möglichkeiten einzelne Threads für das OS "unsichtbar" zu schalten? Zb wenn man den/die Threads für andere Sachen dediziert nutzten will (etwa eine Virtuelle Maschine)? Und rein Interesse halber mal, wie sieht das ganze bei der AMD Modellgeschichte aus? Die Kerne sollten dann ja wirklich gleichwertig sein :)
 
wie sieht das ganze bei der AMD Modellgeschichte aus? Die Kerne sollten dann ja wirklich gleichwertig sein
Nicht anders als bei Intel - entweder, du hast Glück und deine Kerne prügeln sich nicht gegenseitig um die geteilten Ressourcen, oder aber du hast Pech und der Code läuft wie Rotz. Auch da sieht (zumindest unter Linux) der Scheduler zu, nur einen Thread pro Modul zu belasten, obwohl Doppelbelegung da natürlich im Allgemeinen weit weniger dramatisch ist.

Zb wenn man den/die Threads für andere Sachen dediziert nutzten will (etwa eine Virtuelle Maschine)?
Wenn du nen Hypervisor nutzt, ja. Wenn deine VM im Kontext des Betriebssystems läuft, auf dem du Threads deaktivieren willst (was durchaus geht - zumindest unter Linux), sieht es schlecht aus, weil deine VM ja auch nur ein Prozess von vielen ist. Du kannst allerdings einzelnen Prozessen jederzeit sagen, dass sie nur auf bestimmten Kernen laufen soll.

Hin und wieder bringt das auch mal was, v.a. bei Software, die eh nicht skaliert, aber im Allgemeinen kann man einfach auf sein OS vertrauen und muss damit nicht rumspielen.
 
VikingGe schrieb:
Ganz so toll skaliert Intels HT in der Regel nämlich doch nicht, auch wenn der maximale Leistungszuwachs tatsächlich bei 100% liegt.
100% leistungszuwachs durch hyperthreading? Die Wikipedia sagt dazu:
Bei der Einführung der Hyperthreading-Technologie warb Intel mit einer Leistungssteigerung pro Hauptkern von bis zu 33 %. Dies ist vermutlich der Idealfall, im Alltag bringt ein HT-Kern eher Leistungswerte von ca. 20-25 %.
Hast Du neue Erkenntnisse oder gar Belege?

Nö, Slater:
wenn Du Leistungszuwachs bei Spielen haben willst, die nur einen oder zwei bis drei Kerne nutzen, ilft nur, im BIOS HT zu deaktivieren. Bei 1-Kern-Spielen am besten auch noch zwei Cores deaktivieren, damit Du eine Turbostufe höher rückst.
 
@ Juri-Bär
100 \% Zuwachs gehen genau dann, wenn beide Threads unterschiedliche Ressourcen nutzen (z.b einer den DRAM und der andere die FPU) oder die beiden Threads stark durch Latenzen limitiert sind. Einfaches Beispiel:

Code:
int Array[GROSSEZAHL];
for(int i=0; i < GROSSEZAHL; i+= 1)
   Array[i] = i;
RandomPermutation(Array);
int Index = 0;
while(true)
Index = Array [Index];
Das Programm ist komplett durch DRAM Latenzen limitiert, da es keinen MLP gibt und jeder Zugriff auf den DRAM übergeht. Evtl benchmarkt VikingGe ja das Programm für mich ;)
 
Zuletzt bearbeitet:
@Juri-Bär ich verweise da mal auf nen Beitrag von im PCGHX-Forum, wo ich das Thema schonmal durchgekaut habe. Wikipedia redet ja vor allem vom Nutzen bei realen Anwendungen, das wird dadurch ja nicht falsch.

@ナイ Das dürfte tatsächlich so ein Fall sein. Habe aber kein Intel-System, wo ich Root-Zugriff drauf hätte, um die IPC zu überwachen, vielleicht baue da irgendwie nen Benchmark draus.


Edit:
Ergebnis Intel (Xeon E5-2630 V2):
Code:
# 1 Kern, SMT
1 Thread:  27331ms
2 Threads: 17550ms

# 2 Kerne
1 Thread:  26226ms
2 Threads: 13474ms

Ergebnis AMD (A10-7350B):
Code:
# 1 Modul, CMT
1 Thread:  39534ms
2 Threads: 19837ms

# 2 Module
1 Thread:  39471ms
2 Threads: 19538ms

SMT-Skalierung ist hier doch etwas schwächer als gedacht mit ~50-60%.

Das Testprogramm ist nicht optimal, weil die "zufällige" Permutation Zykel erzeugt. Ausreichend für ein aussagekräftiges Ergebnis ist es aber schon.
Code:
#include <atomic>
#include <chrono>
#include <future>
#include <iostream>
#include <random>
#include <string>
#include <thread>

template<size_t Size>
size_t compute_sum(const std::array<size_t, Size>& array, size_t start, size_t count) {
  return count > 0
    ? array[start] + compute_sum(array, array[start], count - 1)
    : 0;
}


template<size_t Size>
std::chrono::milliseconds run_benchmark(const std::array<size_t, Size>& array, size_t times, size_t threads) {
  
  auto time_start = std::chrono::high_resolution_clock::now();
  
  std::vector<std::future<size_t>> results;
  for (size_t i = 0; i < threads; i++) {
    results.push_back(std::async(std::launch::async, [&array, times, i] {
      return compute_sum(array, i, times * Size);
    }));
  }
  
  for (auto& r : results)
    r.wait();
  
  auto time_end = std::chrono::high_resolution_clock::now();
  return std::chrono::duration_cast<std::chrono::milliseconds>(time_end - time_start);
  
}


int main(int argc, char** argv) {
  
  constexpr size_t ARRAY_ELEMS = 32 * 1048576ul;
  
  // Allocate and initialize the array 
  auto  data_ptr = std::make_unique<std::array<size_t, ARRAY_ELEMS>>();
  auto& data     = *data_ptr;
  
  for (size_t i = 0; i < ARRAY_ELEMS; i++)
    data[i] = i;
  
  // Permute array using a fixed number sequence
  std::cout << "Preparing..." << std::endl;
  std::mt19937 rd_gen(6u);
  
  for (size_t i = 0; i < ARRAY_ELEMS - 1; i++)
    std::swap(data[i], data[std::uniform_int_distribution<size_t>(i, ARRAY_ELEMS - 1)(rd_gen)]);
  
  // Run tests
  std::cout << "Running benchmark..." << std::endl;
  std::cout << "1 Thread:  " << run_benchmark(data, 10, 1).count() << "ms" << std::endl;
  std::cout << "2 Threads: " << run_benchmark(data,  5, 2).count() << "ms" << std::endl;
  
}

Edit 2: Irgendwas ist hier aber sowieso faul. Mein Desktop:
Code:
# 2 Kerne
1 Thread:  15980ms
2 Threads: 8178ms
Das kann nicht sein, dann wäre der da ja schneller als der Sandy Bridge-basierte Uni-Server.
Naja, interpretiere man die Ergebnisse, wie man will.
 
Zuletzt bearbeitet:
Das Testprogramm ist nicht optimal, weil die "zufällige" Permutation Zykel erzeugt. Ausreichend für ein aussagekräftiges Ergebnis ist es aber schon.
Sollte die mittlere Zyklenlänge aber nicht sehr sehr sehr lang werden? Die Wahrscheinlichkeit, dass der Startwert selbst ein Zyklus der Länge 1 ist ist ja 1/N. Dafür, dass man nach dem zweiten Wert in einen Zyklus gerät, beträgt die Wahrscheinlichkeit 1/(N-1) usw. Dadurch sollte der Workingset bei großen Werten für N in den aller aller aller allermeisten größer als der Cache sein.

Notfalls muss man eben die Permutation so umgestallten dass der Zyklus maximal wird :D. Sollte auch nicht so viel Aufwand sein ;)
 
Zuletzt bearbeitet:
Hat das ganze wirklich nur was mit dem OS zu tun? Ich habe jetzt mal bei anderen PCs getestet, und da sieht die Last anders verteilt aus (Minecraft/Java nutzt den ersten Thread reproduzierbar immer) obwohl die CPU eigentlich so gut wie die selbe ist (i7 4770,4 Kerne 8 Threads). Ich habe bei beiden Systemen die selben Einstellungen genutzt, OS ist Win10 64bit.
 
Zurück
Oben