Boost logo

Boost :

Subject: Re: [boost] Boost.Fiber review January 6-15
From: Niall Douglas (s_sourceforge_at_[hidden])
Date: 2014-01-10 11:36:57


On 7 Jan 2014 at 23:07, Oliver Kowalke wrote:

> my focus was to address the one-thread-per-client pattern used for C10K.
> the pattern makes code much more readable/reduces the complexity but
> doesn't scale. if you create too many threads on your system your
> overall performance will be shrink because at a certain number of threads,
> the overhead of the kernel scheduler starts to swamp the available cores.

If you replace "doesn't scale" with "doesn't scale linearly" then I
agree. The linearity is important.

> > I appreciate and understand this. However, I must then ask this: is
> > your library ready to enter Boost if you have not done any
> > performance testing or tuning?
>
> is performance testing and tuning a precondition for a lib to be accepted?

No. I do think the question ought to be asked though, even if the
answer is no. It helps people decide the relative merit of a new
library.

> I did some tuning (for instance spinlock implementation) but it is not
> finished.

Even my own spinlock based on Intel TSX is unfinished - I had no TSX
hardware to test it on, and had to rely on the Intel simulator which
is dog slow.

I mentioned this in another reply, but I really think you ought to at
least measure performance and supply some results in the docs. You
don't need to performance tune, but it sure is handy to know some
idea of performance.

> - io_service::post() pushes a callable to io_service's internal queue
> (executed by io_service::run() and related functions)
> - fibers::asio::spawn() creates a new fiber and adds it to the
> fiber-scheduler
> (specialized to use asio's io_service hence asios's asyn-result feature)
> - yield is an instance of boost::fibers::asio::yield_context which
> represents the fiber
> running this code; it is used by asio's async result feature
> - yield[ec] is put to an async-operation in order suspending the current
> fiber and pass an
> error (if happend during execution of async-op) back to the calling code,
> for instance EOF if socket was closed

Ok, let me try rewriting this description into my own words.

To supply fibers to an io_service, one creates N fibers each of which
call io_service::run() as if they were threads. If there is no work
available, the fiber executing the run() gives up context to the next
fiber. If some fiber doing some work does an io_service::post(), that
selects some fiber currently paused waiting inside run() for new
work, and next context switch that fiber will be activated to do
work.

This part I think I understand okay. The hard part for me is how
yield() fits in. I get that one can call yield ordinarily like in
Python

def foo:
  a=0
  while 1:
    a=a+1
    yield a

... and every time you call foo() you get back an increasing number.

Where I am finding things harder is what yield means to ASIO which
takes a callback function of specification void (*callable)(const
boost::system::error_code& error, size_t bytes_transferred) so fiber
yield must supply a prototype matching that specification. You said
above this:

> - yield is an instance of boost::fibers::asio::yield_context which
> represents the fiber running this code; it is used by asio's async result
> feature
>
> - yield[ec] is put to an async-operation in order suspending the current
> fiber and pass an error (if happend during execution of async-op) back to
> the calling code, for instance EOF if socket was closed

So I figure that yield will store somewhere a new bit of context, do
a std::bind() encapsulating that context and hand off the functor to
ASIO. ASIO then schedules the async i/o. When that completes, ASIO
will do an io_service::post() with the bound functor, and so one of
the fibers currently executing run() will get woken up and will
invoke the bound functor.

So far so good. But here is where I fall down: the fibre receives in
an error state and bytes transferred, and obviously transmits that
back to the fiber which called ASIO async_read() by switching back to
the original context. I would understand just fine if yield()
suspended the calling fiber, but it surely cannot because yield()
will get executed before ASIO async_read() does as it's a parameter
to async_read(). So I therefore must be missing something very
important, and this is why I am confused. Is it possible that yield()
takes a *copy* of the context like setjmp()?

It's this kind of stuff which documentation is for, and why at least
one person i.e. me needs hand holding through mentally groking how
the ASIO support in Fiber works. Sorry for being a bit stupid.

Niall

-- 
Currently unemployed and looking for work.
Work Portfolio: http://careers.stackoverflow.com/nialldouglas/



Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk