2013-06-05 7 views
5

Ich habe vor einer Weile mit Haskell angefangen und jetzt konzentriere ich mich auf das Networking. Ich folgte setzen einige Tutorials und Quellpunkte zusammen einen sehr einfachen Echo-Server:Haskell - Langsam Steckverbindung bei Gewinde

main = withSocketsDo $ do 
    forkIO $ acceptor 8080  
    print "Server running ... " >> getLine >>= print 


tcpSock :: IO Socket 
tcpSock = socket AF_INET Stream 0 

acceptor :: PortNumber -> IO() 
acceptor port = do 

    -- Setup server socket 
    sock <- tcpSock 
    setSocketOption sock ReuseAddr 1 
    bindSocket sock (SockAddrInet port iNADDR_ANY) 
    listen sock 50 

    -- Start with zero index 
    loop sock 0 
     where 
     loop sock sockId = do 
      -- Accept socket 
      (nextSock, addr) <- accept sock 

      -- Setup the socket for performance 
      (_, handle) <- setupClient nextSock 

      -- Run client in own thread 
      forkIO $ do 
       -- Get a stream of bytes 
       stream <- BS.hGetContents handle 

       -- Echo the first received char 
       BS.hPut handle $ BS.take 1 stream 

       -- Kill the socket 
       SIO.hClose handle 


      -- Accept next client 
      loop sock (sockId + 1) 


setupClient :: Socket -> IO (Socket, SIO.Handle) 
setupClient sock = do 
     -- Disable nagle 
     setSocketOption sock NoDelay 1 

     -- Disable buffering 
     hdl <- socketToHandle sock SIO.ReadWriteMode 
     SIO.hSetBuffering hdl SIO.NoBuffering 

     return (sock, hdl) 

Jetzt habe ich den Code mit dem ab-Tool zum Benchmark der Server getestet. Der Code wird mit -O2 und-Threaded kompiliert und das Programm wird mit + RTS -N gestartet, um mehrere OS-Threads zu verwenden.

Der Code erstellt einen neuen Lightweight-Thread pro Client und soweit ich mich erinnere, sind diese Threads ziemlich billig, weil sie von einer Reihe von echten OS-Threads geplant werden.

Nachdem das Werkzeug ausgeführt wird, sind die Ergebnisse:

ab -n 10000 -c 1000 http://localhost:8080/ ~ 500-1600 req/sec Ja, es manchmal zwischen 500 und 1600 nicht verändert!

Zuerst dachte ich gut, nicht schlecht. Dann habe ich das Programm ohne "+ RTS -N" ausgeführt und die Ergebnisse sind fast alle mal ~ 20000 req/sec.

Offensichtlich tötet das Threading die Leistung ziemlich schlecht, aber warum? Meine Vermutung ist, dass der IO-Manager eine ziemlich schlechte Arbeit leistet, wenn es um viele Verbindungen geht.

BTW: Ich benutze Ubuntu 13.04 und Ghc 7.6, aber ich habe den Code unter Windows 8 getestet, die mir weit schlechtere Ergebnisse gab, aber ich denke, der IO-Manager ist für Linux abgestimmt, was Sinn macht.

Mache ich hier wirklich dumm? Ich weiß, das Beispiel ist ziemlich trivial, aber hier läuft offensichtlich etwas schief.

Grüße, Chris

+0

ist, dass 20 oder 20000 in 20.000 – Satvik

+0

Es ist 20000, sry für das Missverständnis – Kr0e

+0

Wie viele aktuelle Themen hat Ihr Prozessor haben und wie viele echte Threads verwenden Sie (das Argument N) – Satvik

Antwort

1

Okay, ich denke, ich habe das Problem-halb gelöst, obwohl ich noch nicht sicher bin, wo der Fehler ist/war.

Ich verwende jetzt das Netzwerk-Paket, so dass die Annahme-Routine handle-based ist. Ich habe das versucht, weil ich nach ein paar Tests ein Speicherleck festgestellt habe.

So löste ich auf magische Weise zwei Probleme auf einmal, denn jetzt macht das Threading keinen Unterschied mehr. Ich weiß wirklich nicht, warum das passiert, aber die Griff-basierte Impl. ist einfacher und offensichtlich schneller/sicherer.

Vielleicht hilft dies anderen Menschen das gleiche Problem zu erleben.

+1

wie sieht dein Code jetzt aus? – chespinoza