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/