Canvas not supported
Canvas not supported
Suchen
Bookmark
Canvas not supported
Shine Effekt
Direkt zum Seiteninhalt

Hauptmenü:

Process Affinity automatisiert setzen

 

Autor:

Oscar Kohler

Version:

0.11

Level:

Einfach

Sprache:

C/C++


 
Haftungsausschluss / Disclaimer
Kontakt / Contact
Drucken / Print


Was bedeutet Process Affinity ?


Process Affinity ermöglicht es, laufende Prozesse und vor allem deren Threads an einzelne Prozessoren zu binden.

Mit Aufkommen der ersten Multiprozessoren, Multicores und dem Hyperthreading fanden sich einige Softwaretitel, sowohl Anwenderprogramme als auch Spiele, die ihren Dienst auf diesen Systemen verweigerten, oder zumindest nicht fehlerfrei darauf arbeiteten.
Obwohl sich die Situation heute sehr zum Guten gewendet hat, gibt es immer noch Programme, vor allem älterer Bauart, die Probleme auf diesen Systemen verursachen.

Vorallem zwei Ursachen führen immer wieder zu Problemen:

Was ist Multithreading ?


Zugriff auf Resourcen mittels Funktionen


Jeder Prozess besteht zumindest aus einem Thread, dem Initial-Thread. Ein Thread ist sozusagen der Teil des Programmes, der vom Prozessor (CPU) im Memory abgearbeitet wird.
Solange ein Prozess nur einen Thread besitzt, besteht keine Gefahr, wenn aus verschiedenen Funktionen auf gemeinsame Resourcen zugegriffen wird.
Funktionen werden immer sequentiell abgearbeitet und behindern einander
daher nicht.

Programme die jedoch über mehrere Threads verfügen, können hingegen nicht so "einfach" auf gemeinsame Resourcen zugreifen.

Threads auf einem Uni-Prozessor System


Threads laufen unter Windows nicht sequentiell, sondern "quasi" parallel. Dadurch entsteht der Eindruck, dass mehrere Programme gleichzeitig arbeiten und bedient werden können.
Hinter den Kulissen werkelt der Scheduler, der Threads in einzelne kleine Zeiteinheiten, sogenannte Quantums zerlegt.
Je nach Priorität und Boost erhalten die Threads mehr oder weniger Ausführungszeit. Diese Zeiteinheiten bilden eine Zeitscheibe die von der CPU abgeackert wird.
Wichtig ist nochmals, dass Threads nicht parallel, sondern verzahnt ausgeführt werden.

Thread 1 / Slice 1 -> Thread 2 / Slice 1 -> Thread 3 / Slice 1 -> ...
Thread 1 / Slice 2 -> Thread 2 / Slice 2 -> Thread 3 / Slice 2 -> ...

Liest nun Thread 1 gemeinsame Daten während Thread 2 diese gemeinsamen Daten noch beschreibt, so sind die gelesenen Daten inkonsistent !
Das kann sowohl zu fehlerhaften Daten wie auch zu einem Programmabsturz führen !
Daher ist bei Multithreading fundamental wichtig, dass erst dann Daten gelesen werden, wenn alle Schreibzugriffe auf diese Daten abgeschlossen sind. Dies nennt man "Synchronisieren" von Threads. Dazu gibt es unterschiedliche Methoden wie Mutex, Semaphore, Critical Sections oder Locking, je nach Anwendungsfall.


Threads auf einem Multi-Prozessor System


Auf einem Multi-Prozessor System werden Threads zwar immer noch in Quantums zerlegt, abgearbeitet werden diese jedoch parallel auf allen verfügbaren Prozessoren und Cores.
Ging eine "Schmuddel" Multithreading Implementierung auf einem Uni-Prozessor System gerade nochmal gut, kann das auf einem Multi- Prozessor System letztendlich zum absoluten Fiasko führen.

In einigen Fällen kann es dann hilfreich sein, alle Threads dieses Programmes auf einen einzelnen Prozessor zu binden.



Grundübel: Polling


Eine der schlimmsten Unarten mancher Programmierer ist das Pollen von Ereignissen. Unter Pollen versteht man das ständige und zeitnahe Abfragen, ob ein gewisses Ereignis, wie z.B.: drücken einer Taste, eingetreten ist.
Windows ist ein "Ereignisgesteuertes" System. Der richtige Weg besteht darin, sich informieren zu lassen, ob ein bestimmtes Ereignis eingetreten ist.
Wird das Polling intensiv ausgeführt, bringt das die CPU ordentlich ins schwitzen, ohne dass diese wirklich produktiv arbeitet.



Macht man es dann besonders ungeschickt, schafft man es tatsächlich, dass auch Multi-Prozessor Systeme voll ausgelastet werden !
In diesen Fällen kann es dann helfen, solche Programme nur an einzelne  CPUs zu binden, um die anderen Prozessoren und Cores für produktivere Aufgaben freizuschaufeln.

Rechenintensive Aufgaben


Auch komplexe Berechnungen können Multi-Prozessor Systeme tüchtig auslasten. Bei solchen Programmen lässt sich natürlich einiges mit Thread Prioritäten schrauben. Bei ganz schwierigen Fällen kann man aber auch über ein Binden auf einzelne Prozessoren nachdenken.

Prozessor Affinity setzen


Grundsätzlich werden Prozesse und deren Threads nicht auf einzelne CPUs und Cores eingeschränkt.
Will man die Threads an einzelne Prozessoren binden so gibt es verschiedene Möglichkeiten, sowohl manuell als auch automatisiert.

via TaskManager


Der unkomplizierteste Weg führt über den Taskmanager.

Taskmanager -> Prozesse -> Prozess auswählen -> Zugehörigkeit festlegen ...



Hier kann man wählen, welche Prozessoren zur Verfügung gestellt werden sollen.

via Commandline


Mit dem Start-Befehl lässt sich ab Windows Vista eine Affinity Maske in Hex angeben.

Gewünschte Prozessoren binär in Hex umwandeln und dem Befehl mitgeben.

z.B.: 4 CPUs, davon CPU 0 und 1 -> %11 -> 0x3 -> 3

start /AFFINITY 3 notepad.exe

Dies lässt sich natürlich auch automatisieren, z.B.: über eine .bat Datei.

Programmiert


Mit der SetProcessAffinityMask Api Funktion lässt sich dies recht einfach implementieren:

Zuerst gewünschten Process starten z.B. mit CreateProcess(...)

Anschliessend Affinity setzen:

if ( SetProcessAffinityMask ( hProcess,
                             dwAffinity )) {
  ...
}
else {
  ...
}

 
hProcess ... Process Handle
dwAffinity ... Affinity Mask

Multi-User



Möchte man problematische Programme in einer Multi-User-Umgebung binden, kann das unter Umständen etwas problematisch werden. Starten 40 User auf einem Server solch ein Programm, so ist die Lastverteilung der einzelnen CPUs recht ungünstig.

Behelfen kann man sich aber beispielsweise mit einem Zufallsgenerator, der dieses Programme stochastisch über die verfügbaren CPUs verteilt.


 
Kein Kommentar
 
Letzte Änderung: 03.02.2015
button Canvas not supported button
Zurück zum Seiteninhalt | Zurück zum Hauptmenü