[web-devel] Making throughput much better
Kazu Yamamoto ( 山本和彦 )
kazu at iij.ad.jp
Tue Jul 23 02:32:55 CEST 2013
Hi,
I confirmed that the throughput of acme-http is also improved:
No yield: 60,772 req/s
Yield: 133,478 req/s
I will try the internal-state branch of Warp next.
--Kazu
> On Mon, Jul 22, 2013 at 11:28 AM, Kazu Yamamoto <kazu at iij.ad.jp> wrote:
>
>> Hi all,
>> Cc: Simon Marlow
>>
>> I think I found a way to make the throughput of web servers much
>> better. In short, put "Control.Concurrent.yield" after sending
>> something. This enables good schedule shaping.
>>
>> Typical code for web servers is like this:
>>
>> loop = do
>> keepAlive <- receiveRequest
>> sendResponse
>> when keepAlive loop
>>
>> With this code, a typical sequence of system calls is like this:
>>
>> recvfrom(13, ) -- thread A
>> sendto(13, ) -- thread A
>> recvfrom(13, ) = -1 EAGAIN -- thread A
>> epoll_ctl(3, ) -- IO manager
>> recvfrom(14, ) -- thread B
>> sendto(14, ) -- thread B
>> recvfrom(14, ) = -1 EAGAIN -- thread B
>> epoll_ctl(3, ) -- IO manager
>>
>> Since each thread calls recvfrom() immediately after sendto(), the
>> possibility that recvfrom() can receive a request is low. This
>> involves the IO manager.
>>
>> To make the possibility higher, I put "yield" after sendResponse:
>>
>> loop = do
>> keepAlive <- receiveRequest
>> sendResponse
>> yield
>> when keepAlive loop
>>
>> Yield pushes its Haskell thread onto the end of thread queue. So,
>> another thread can work. During the work of other threads, a request
>> message would arrive.
>>
>> recvfrom(13, ) -- thread A
>> sendto(13, ) -- thread A
>> recvfrom(14, ) -- thread B
>> sendto(14, ) -- thread B
>> recvfrom(13, ) -- thread A
>> sendto(13, ) -- thread A
>>
>> I tested this idea on SimpleServer and confirmed that its throughput
>> is doubled:
>>
>> No yield: 58,152 req/s (
>> https://gist.github.com/AndreasVoellmy/4184520#file-simpleserver-hs)
>> Yield: 113,048 req/s (
>> https://gist.github.com/kazu-yamamoto/6051118#file-simpleserver2-hs)
>>
>> Unfortunately, yield does not work for Warp as expected. It does not
>> pass control to another thread and behaves as used to be.
>>
>> So, my question: why doesn't "yield" work in Warp? ResourceT is doing
>> a special thing? My code change is here:
>>
>>
>> https://github.com/yesodweb/wai/commit/4e9c6316ad59cd87be13000d15df8e6fd7c311f1
>>
>> I'm using GHC head (with multicore IO manager). I'm sure that the
>> following patch to fix the bug of yield is applied:
>>
>>
>> https://github.com/ghc/ghc/commit/66839957a376dbe85608822c1820eb6c99210883
>>
>> P.S.
>>
>> It would be appreciated if someone tries this idea on other web
>> servers.
>>
>> --Kazu
>>
>> _______________________________________________
>> web-devel mailing list
>> web-devel at haskell.org
>> http://www.haskell.org/mailman/listinfo/web-devel
>>
>
> Hey Kazu,
>
> As usual, exciting work on the I/O manager.
>
> I've had an experiment in the back of my mind for a while now, and after
> this email I decided to finally try it out. On the internal-state branch[1]
> of the WAI repo, I've implemented a relatively simple change:
>
> * All operations that used to live in ResourceT IO now live in IO.
> * The Request value has a new field called resourceInternalState, which
> holds onto the internal state of the ResourceT transformer. Using the
> internal state API[2], it should be trivial to convert ResourceT code to
> live in the IO monad instead.
>
> Would you be able to see if this change makes any meaningful impact on the
> benchmarks you're running? If so, we can discuss the proper way to make
> this transition. Note that, as I've implemented it, this is a breaking
> change in WAI, though we could take less extreme measures and get most of
> the performance benefit most likely.
>
> Michael
>
> [1] https://github.com/yesodweb/wai/tree/internal-state
> [2]
> http://hackage.haskell.org/packages/archive/resourcet/0.4.7.1/doc/html/Control-Monad-Trans-Resource.html#g:10
More information about the web-devel
mailing list