Kr0*_*r0e 5 sockets performance networking multithreading haskell
我刚才开始使用Haskell,现在我专注于网络.我按照一些教程和源代码示例将一个非常简单的echo服务器放在一起:
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)
Run Code Online (Sandbox Code Playgroud)
现在,我已经使用ab-Tool测试了代码来对服务器进行基准测试.代码使用-O2和-threaded编译,程序使用+ RTS -N启动以使用多个OS线程.
代码为每个客户端创建一个新的轻量级线程,据我记得这些线程非常便宜,因为它们是由一堆真正的OS线程调度的.
运行该工具后,结果如下:
ab -n 10000 -c 1000 http://localhost:8080/
~500 - 1600 req/sec是的,它有时会在500到1600之间变化!
起初我认为好,不错.然后我运行程序没有"+ RTS -N",结果几乎每次~20000 req/sec.
显然,线程会严重影响性能,但为什么呢?我的猜测是,IO经理在处理大量连接时做得非常糟糕.
BTW:我使用的是Ubuntu 13.04和ghc 7.6,但是我已经在Windows 8下测试了代码,这给了我更糟糕的结果,但我认为IO管理器已经针对linux进行了调整,这是有道理的.
我在这里做些傻事吗?我知道,这个例子非常简单,但显然出现了问题.
问候,克里斯
好吧,我想我半解决了这个问题,尽管我仍然不确定错误在哪里。
我现在使用 Network 包,因此接受例程是基于句柄的。我尝试这样做是因为经过几次测试后我发现内存泄漏。
这样我一次神奇地解决了两个问题,因为现在线程没有任何区别。我真的不知道为什么会发生这种情况,但是基于句柄的实现。更简单,显然更快/更安全。
也许这可以帮助其他遇到同样问题的人。