Ich habe eine Änderung an meiner Bibliothek vorgenommen, jetzt ist es viel langsamer, aber ich kann nicht herausfinden, wo sie all diese zusätzliche Zeit verbringt. Profilberichte helfen nicht. Bitte helfen Sie mir herauszufinden, was der Grund sein könnte.
Ich habe eine Redis-Client-Bibliothek namens Hedis erstellt und ein Benchmark-Programm dafür erstellt. Nun habe ich einige interne Änderungen an der Bibliothek vorgenommen, um die Architektur zu bereinigen. Dies führte dazu, dass die Leistung (in Redis-Anfragen pro Sekunde, gemessen am Benchmark) um etwa den Faktor 2,5 sank.
Der Benchmark öffnet 50 Netzwerkverbindungen zu einem Redis-Server auf localhost. Die Verbindungen werden zwischen den beiden Versionen unterschiedlich gehandhabt:
unsafeInterleaveIO
(ich beschreibe meinen Ansatz in
Striche in einem Blogpost hier ). Ich war ein bisschen unglücklich
mit der Architektur, also änderte ich die Dinge zu Chan
s (150 Threads, die im Benchmark ausgeführt werden). Einige weitere Informationen, die relevant sein könnten:
-threaded
). forkIO
erstellt. Nicht desto teurer
forkOS
. Profiling gibt mir keinen klaren Grund für den Leistungsabfall. Laut dem Profiling-Bericht verbringen beide Versionen mehr als 99% ihrer Zeit in System.IO.hFlush
und Data.ByteString.hGetSome
. Die Anzahl der Aufrufe von hFlush
und hGetSome
ist in beiden Versionen gleich. Da der Netzwerkverkehr in beiden Fällen ebenfalls gleich ist, können diese Funktionen nicht der Grund für die Verlangsamung sein.
Der einzige signifikante Unterschied, den ich zwischen den beiden Versionen messen kann, ist, was time
(das Unix-Dienstprogramm) mir sagt: Die langsame Version (mit dreimal so vielen Threads) verbringt wesentlich mehr Zeit in "sys" als in "user" ", verglichen mit der schnellen Version. Das GHC +RTS -s
Flag meldet dies als reduzierte Produktivität.
Hier sind die Programmausgaben für beide Versionen mit dem +RTS -s
Flag:
Haben Sie irgendwelche Ideen oder Hinweise, woher diese zusätzliche Zeit kommen könnte?
Laut dem Profiling-Bericht wird die meiste Zeit in hFlush
und hGetSome
verbracht. Laut time
benötigt die langsame Version viel mehr Systemzeit. Also, meine Hypothese ist, dass viel Zeit blockiert und abgewartet wird, ob es auf mehr Input wartet oder Threads gesperrt und entsperrt wird.
Hier ist das erste, was ich tun würde: kompilieren Sie den Code mit -threaded
und sehen Sie, was passiert. Die Thread-Laufzeitumgebung verwendet einen komplett anderen IO-Manager, und ich vermute stark, dass diese einzelne Änderung Ihr Problem beheben wird.
Mein rate wäre, dass es etwas mit dem Overhead von Chan
zu tun hat.
Mein erster Gedanke war eine erhöhte GC-Zeit, aber das scheint überhaupt nicht der Fall zu sein. Mein zweiter Gedanke ist, dass vielleicht das gesamte Sperren und Freigeben bei der Verwendung von Chan
(das über MVar
implementiert ist) das Problem ist. Aber das ist nur eine Vermutung.
Sie könnten stattdessen TChan
(d. h. STM) ausprobieren und sehen, ob das den geringsten Unterschied macht. (Vielleicht könnten Sie ein kleines Skelett kodieren, nur um die beiden zu vergleichen und zu sehen, ob das Problem dort liegt, anstatt Ihren "echten" Code neu zu implementieren.)
Ansonsten habe ich keine Ideen mehr.