2016-07-11 10 views
1

Es handelt sich um einen Multithread-C# -Dienst mit Deedle. Tests auf einem Quad-Core-System im Vergleich zu einem Octa-Core-Zielsystem zeigen, dass der Service auf dem Zielsystem ungefähr zweimal langsamer ist als zweimal schneller. Selbst wenn die Anzahl der Threads auf zwei beschränkt wird, ist das Zielsystem immer noch fast 40% langsamer.Schlechte Multithread-Performance auf besserem System (möglicherweise aufgrund von Deedle)

Die Analyse zeigt viel Warten in Deedle (/ F #), wodurch das Zielsystem grundsätzlich auf zwei Kernen läuft. Nicht-Deedle-Testprogramme zeigen normales Verhalten und eine höhere Speicherbandbreite auf dem Zielsystem.

Irgendwelche Ideen dazu, was dies verursachen könnte oder wie man diese Situation am besten angehen könnte?

EDIT: Es scheint die meiste Zeit warten in Calls zu Invoke getan wird.

+0

Ich glaube nicht, dass Deedle eine ausgeklügelte Thread - Synchronisation ausführt, die dazu führen würde, dass das Programm langsam läuft - obwohl Sie, wenn Sie aus mehreren Threads auf denselben Frame/Serie zugreifen, möglicherweise Auswirkungen auf CPU - Caches haben Overhead der Parallelisierung ist größer als der Nutzen davon. –

+0

@TomasPetricek Für unsere Verwendung können die Threads als ziemlich unabhängig angesehen werden. Natürlich fällt es mir schwer zu sagen, was in Bibliotheken passiert (oder noch tiefer in CLR). Soweit ich in der Lage war zu messen (z. B. mit Intels PCM), ist Caching kein Problem. Die seltsame Sache ist, dass Sie zumindest erwarten würden, dass es ähnlich wie das aktuelle System läuft, wenn die Anzahl der Threads (auf beiden Systemen) begrenzt wird. – mweerden

+0

Haben Sie im Code genügend Sleep-Anweisungen, die den Thread inaktiv machen, sobald er nichts mehr zu tun hat? Es scheint so, als würden Threads konkurrieren, sie könnten einfach Looping ohne Pausen machen? –

Antwort

1

Das Problem stellte sich heraus, eine Kombination von Windows 7, .NET 4.5 (oder eigentlich die 4.0-Laufzeit) und die starke Verwendung von Tail-Rekursion in F #/Deedle.

Mit dem Visual Concert Visualizer von Visual Studio habe ich bereits festgestellt, dass die meiste Zeit damit verbracht wird, in Invoke-Aufrufen zu warten. Bei genauerem Hinsehen dieses Ergebnis in der folgenden Anrufverfolgung:

ntdll.dll:RtlEnterCriticalSection 
ntdll.dll:RtlpLookupDynamicFunctionEntry 
ntdll.dll:RtlLookupFunctionEntry 
clr.dll:JIT_TailCall 
<some Deedle/F# thing>.Invoke 

für diese Funktion Suche gab mehrere Artikel und Forenthreads anzeigt, dass F # unter Verwendung von in einer Menge Anrufe zu JIT_TailCall führen kann und dass .NET 4.6 verfügt über eine neuen JIT-Compiler, der sich mit einigen Problemen in Bezug auf diese Aufrufe zu befassen scheint. Ich habe zwar keine Probleme mit Sperren/Synchronisation gefunden, aber das hat mir die Idee gegeben, dass das Aktualisieren auf .NET 4.6 eine Lösung sein könnte.

Auf meinem eigenen Windows 8.1-System, das auch .NET 4.5 verwendet, tritt das Problem jedoch nicht auf. Nach der Suche ein wenig nach ähnlichen Invoke rufen fand ich, dass die Anrufverfolgung auf diesem System sah wie folgt aus:

ntdll.dll:RtlAcquireSRWLockShared 
ntdll.dll:RtlpLookupDynamicFunctionEntry 
ntdll.dll:RtlLookupFunctionEntry 
clr.dll:JIT_TailCall 
<some Deedle/F# thing>.Invoke 

Offenbar in Windows 8 (0,1) wurde der Verriegelungsmechanismus auf etwas weniger streng, geändert, die in Folge in viel weniger Notwendigkeit, auf das Schloss zu warten.

Also nur mit der Zielsystemkombination von Windows 7's striktes Sperren und weniger effizientem JIT-Compiler von .NET 4.5, verursachte F # 's starke Verwendung der Tail-Rekursion Probleme. Nach dem Update auf .NET 4.6 ist das Problem verschwunden und unser Dienst läuft wie erwartet.

Verwandte Themen