2013-08-26 18 views
5

Ich portiere Code in Windows und finde Threading extrem langsam. Die Aufgabe dauert 300 Sekunden unter Windows (mit zwei xeon E5-2670 8-Core 2.6 GHz = 16 Core) und 3,5 Sekunden unter Linux (Xeon E5-1607 4 Core 3 GHz). Mit vs2012 Express.Portieren von Threads zu Fenstern. Kritische Abschnitte sind sehr langsam

Ich habe 32 Threads alle aufrufen EnterCriticalSection(), einen 80-Byte-Job von einem std :: stack, LeaveCriticalSection und einige Arbeit (250k Jobs insgesamt). Ich drucke die Thread-ID und die aktuelle Uhrzeit

Vor und nach jedem kritischen Abschnitt Anruf.

  • Die Wartezeit für eine Sperre des einzelnen Thread ist ~ 160ms
  • den Job Pop-off der Stapel ~ 3 ms
  • Aufruf Urlaub nimmt ~ 3 ms
  • Der Job ~ nimmt 1ms
nimmt

(ungefähr gleich für Debug/Release, Debug dauert ein wenig länger. Ich würde gerne in der Lage sein, den Code richtig Profil: P)

Kommentieren Der Job-Aufruf dauert 2 Sekunden (immer noch mehr als Linux).

Ich habe versucht, sowohl die Queryperformancecounter und timeGetTime, beide geben ca. das gleiche Ergebnis.

AFAIK den Job macht nie sync Anrufe, aber ich kann die Verlangsamung nicht erklären, es sei denn, es tut.

Ich habe keine Ahnung, warum das Kopieren von einem Stapel und ruft Pop so lange dauert. Eine weitere sehr verwirrende Sache ist, warum ein Anruf zu verlassen() so lange dauert.

Kann jemand darüber spekulieren, warum es so langsam läuft?

würde ich nicht den Unterschied in dem Prozessor habe gedacht, einen 100x Performance-Unterschied geben, aber könnte es überhaupt zu Dual-CPUs zusammenhänge? (Synchronisation zwischen separaten CPUs als internen Kernen).

By the way, ich bin mir dessen bewusst std :: thread aber meine Bibliothek Code wollen mit pre C++ 11 zu arbeiten.

bearbeiten

//in a while(hasJobs) loop... 

EVENT qwe1 = {"lock", timeGetTime(), id}; 
events.push_back(qwe1); 

scene->jobMutex.lock(); 

EVENT qwe2 = {"getjob", timeGetTime(), id}; 
events.push_back(qwe2); 

hasJobs = !scene->jobs.empty(); 
if (hasJobs) 
{ 
    job = scene->jobs.front(); 
    scene->jobs.pop(); 
} 

EVENT qwe3 = {"gotjob", timeGetTime(), id}; 
events.push_back(qwe3); 

scene->jobMutex.unlock(); 

EVENT qwe4 = {"unlock", timeGetTime(), id}; 
events.push_back(qwe4); 

if (hasJobs) 
    scene->performJob(job); 

und der Mutex-Klasse, mit Sachen Linux #ifdef entfernt ...

CRITICAL_SECTION mutex; 

... 

Mutex::Mutex() 
{ 
    InitializeCriticalSection(&mutex); 
} 
Mutex::~Mutex() 
{ 
    DeleteCriticalSection(&mutex); 
} 
void Mutex::lock() 
{ 
    EnterCriticalSection(&mutex); 
} 
void Mutex::unlock() 
{ 
    LeaveCriticalSection(&mutex); 
} 
+0

Was verwendeten Sie unter Linux? Schützen Sie nur den Zugriff auf den std :: stack mit den kritischen Abschnitten? – xanatos

+2

Wo drucken Sie die Thread-ID und die aktuelle Zeit? – avakar

+0

Wählen Sie eine Sprache. Und das scheint seltsam. Es klingt sicher * einfach * genug für eine [SSCCE] (http://www.sscce.org) um praktikabel zu sein. Ich stimme zu, dass meine Erfahrung mit Windows im Vergleich zu einer vergleichbar ausgestatteten Linux-Distribution etwas glanzlos war, aber das scheint wirklich eine ziemlich große Kluft zu sein. – WhozCraig

Antwort

0

Es scheint, wie sind Ihre Fenster Fäden Supercontengegenüber. Sie scheinen vollständig serialisiert zu sein. Sie haben in Ihrem kritischen Abschnitt und 32 Threads ungefähr 7ms Gesamtverarbeitungszeit. Wenn alle Threads in der Sperre in der Warteschlange sind, würde der letzte Thread in der Warteschlange erst nach dem Einschlafen von ca. 217ms laufen. Dies ist nicht zu weit von Ihrer 160ms Wartezeit.

Also, wenn die Threads nichts anderes zu tun haben, als den kritischen Abschnitt zu betreten, arbeiten, dann den kritischen Abschnitt verlassen, das ist das Verhalten, das ich erwarten würde.

Versuchen Sie, das Profilierungsverhalten von Linux zu charakterisieren, und sehen Sie, ob das Programmverhalten wirklich ist und Äpfel zu Äpfeln vergleichen.

+0

Ich stimme zu, sie scheinen lose hintereinander zu laufen (gelegentlich gibt es einen kleinen Unterschied in der Ausführungsreihenfolge). Es gibt keinen Code, um sie zu serialisieren, und eine 80-Byte-Kopie und Pop sollte schneller sein als mein Job. Was wirklich keinen Sinn ergibt, ist die Zeit, die LeaveCriticalSection benötigt. Ich glaube, dass die Serialisierung ein Symptom eines zugrundeliegenden Problems mit kritischen Abschnitten ist. Ich werde jedoch die Konkurrenz auf Linux überprüfen. – jozxyqk

1

Fenster CRITICAL_SECTION dreht sich in einer engen Schleife, wenn Sie es zum ersten Mal eingeben. Es unterbricht den Thread, der EnterCriticalSection aufgerufen hat, nicht, es sei denn, ein wesentlicher Zeitraum ist in der Spin-Schleife abgelaufen. Wenn also 32 Threads um denselben kritischen Abschnitt konkurrieren, werden viele CPU-Zyklen verbraucht und verschwendet. Versuchen Sie stattdessen einen Mutex (siehe CreateMutex).

+3

[Ich vermute das gleiche] (http://stackoverflow.com/questions/18442574/porting-threads-to-windows-critical-sections-are-very-slow#comment27103010_18442574), aber würde nicht die Anzahl der Spin sein passender/schneller als mit einem Mutex? – dyp