2017-06-07 5 views
0

Ok, nehmen wir an, wir einen Thread-Pool mit einer „Art“ dynamische flachen Behälter haben, die eine maximale Kapazität von x hat, da der Speicher auf dem Stack ist die Leistung zu verbessern.Eine gute Thread-Pool Queue Größe

in minimal-Code (ich will nicht ins Detail gehen):

template <int32 QSIZE, int32 PSIZE> class ThreadPool 
{ 
public: 
ThreadPool() 
{ 
    for (int32 i = 0; PSIZE > i; ++i) 
    { 
     m_Workers.push(Thread(thread_main, m_Queue, m_Signal, m_IsRunning)); 
    } 
} 

~ThreadPool() 
{ 
    //Wait and destroy all threads 
} 

void run(Task task) 
{ 
    m_Queue.push(task); 
    m_Signal.wake_all(); 
} 

private: 
    FlatVector<Thread, PSIZE> m_Workers; //PSIZE --> max capacity 
    FlatQueue<Task, QSIZE> m_Queue; //QSIZE --> max capacity 
    ConditionVariable   m_Signal; 
    AtomicBool    m_IsRunning; 
}; 

die class Task ist eine Implementierung für eine Inplace-Funktion mit gebundenen Parametern und semantischen bewegen.

Die FlatVector ist ein Vektor mit Speicher auf dem Stapel und einer maximalen Kapazität von PSIZE (Poolgröße).

die FlatQueue ist grundsätzlich das gleiche Konstrukt wie eine Warteschlange mit einer Kapazität von QSIZE (Warteschlangengröße)

One Task eine maximale Größe von 512 Bit hat.

Gibt es eine gute Daumenregel, wie groß eine Thread-Pool-Task-Queue im schlimmsten Fall wachsen sollte? (Wenn möglich mit Berücksichtigung des gegebenen Beispiels, wenn nicht möglich, ist eine Schätzung auf regulären Thread-Pools auch in Ordnung.)

In den meisten Fällen läuft mein Pool mit 8 Threads, da das meine Kernzahl und die Anwendung ist Die Verwendung des Pools kann einen anständigen Vorteil einer höheren Thread-Anzahl bieten. (Es ist eine einfache Physik-Simulation)

Wäre es ein besserer Weg, um Aufgaben zu Aufgabenbündeln zusammen zu verpacken (solange sie 512 Bit zusammen nicht überschreiten, unter Berücksichtigung dieses Beispiels.) Oder sollte ich einfach die Berechnung überspringen welches kann nicht mehr in diesem Rahmen platziert werden und im nächsten berechnet werden? Die physikalische Berechnung wird dann für 2 Frames berechnet.

Normalerweise wähle ich eine Warteschlange Größe etwas zwischen 64 - 128 Aufgaben, die in Ordnung ist (zumindest Leistung), aber eigentlich fühlt es sich an wie 128 Aufgaben in einem Pool zur gleichen Zeit ein bisschen zu mir und ich nicht ' Ich möchte diese Menge an Speicher verschwenden.

Ich Überschreitung der Grenze von 64 Aufgaben in dem Pool zur gleichen Zeit Manchmal, wenn ich auf den Pool unter hoher Last eingestellt. (Deshalb habe ich mich entschieden, die Poolgröße zu erhöhen.)

Das Hinzufügen einer einzelnen 512-Bit-Task (worst case) zu meinem Pool dauert etwas zwischen 1,02 und 1,3 e (-7) Sekunden auf meinem System.

Das gleiche mit einem "normalen" Thread-Pool und "normalen" Funktionsbindungen mit Heap-Zuweisung und Bewegung Semantik nimmt etwas zwischen 1,8 - 2,3 e Power (-5) Sekunden, was zeigt, gibt es einen echten Vorteil bei der Verwendung der in diesem Fall stapeln.

+1

Das Schreiben einer eigenen Threadpool-Klasse ähnelt dem Schreiben einer eigenen String-Klasse. Ein bisschen wichtig, dass Sie dies mindestens einmal in Ihrem Leben tun, aber es ist genauso wichtig zu vergleichen, was Sie getan haben, was andere Programmierer getan haben. Nur * das * gibt Einblick. Wenn Sie dies tun, werden Sie PSIZE schließlich eliminieren, da der optimale Wert ein Laufzeitdetail ist, das von der spezifischen Maschine abhängt, auf der Ihr Code ausgeführt wird. Und Sie werden sehr wahrscheinlich auch QSIZE eliminieren, da es keinen vernünftigen Weg gibt, den Wert im Voraus zu schätzen und damit umzugehen, dieses Limit zu überschreiten. Vergleichen Sie, es ist wichtig. –

+0

Fairer Punkt, ich weiß, dass ein Pool (normalerweise) sollte eine Größe von CPU-Kernen haben. Später wird zumindest die Anzahl der Threads zur Laufzeit durch eine getCoreCount() -Funktion ersetzt (dort haben Sie Recht). Trotzdem bleibt der Stapelcontainer mit dem QSIZE erhalten, da der Leistungsvorteil des Stackcontainers zu stark ist. Tatsächlich behandele ich die Überläufe bereits auf eine Weise, die ich nicht mag: Wenn die Warteschlange voll ist, führe ich sie auf dem Hauptweg aus. Das möchte ich in Zukunft vermeiden, deshalb suche ich nach einer besseren Lösung, die mehr parallel läuft. – Mango

Antwort

0

Eine allgemeine Antwort auf die Frage:

für eine Auslastung, die die gleiche wie die Anzahl der physischen Prozessoren (oder zweimal soll die maximale Anzahl von Threads auf anderen Ressourcen, ohne zu warten, logisch ständig läuft, dass, wenn die Prozessor hat Hyper-Threading).

Für eine Workload, die auf andere Ressourcen wartet (z. B. warten, bis ein Socket eine Verbindung herstellt), möchten Sie diese Latenz kompensieren, um maximalen Durchsatz zu erzielen, indem Sie mehr Threads als logische Prozessoren haben (abhängig von Ihrer Wartezeit). Hunderte von Threads wären in Ordnung, wenn die meisten blockiert sind. Man könnte erwägen, die Latenz gebundenen Teil der Aufgabe und dem CPU-intensive Teil der Aufgabe in vollem Umfang die Arbeitsbelastung von denen jede eine unterschiedliche Fadenzahl auszubalancieren Aussondern zu laden.

Sie können Ihre optimale Fadenzahl empirisch ermitteln, wenn Sie den Durchsatz maximieren möchten.

Eine interessante Lösung für Software-Selbstabstimmung der Thread-Anzahl könnte durch die Verwendung von Kontrolltheorie erreicht werden. Das Buch Feedback Control für Computersysteme von Philipp K. Janert ist eine gute Referenz dazu.

0

Gibt es eine gute Faustregel, wie groß die Task-Queue eines Thread-Pools im schlimmsten Fall wachsen sollte?

denke ich, die richtigen Fragen zu stellen:

  • Wie lange eine Aufgabe Warte ausgewertet, bevor sie können?
  • Übertrifft eine neue Aufgabe eines bestimmten Typs eine vorhandene Aufgabe?