3 ms·
Good piece - having built a big Rails application, I love the speed of development but your comments about scaling totally resonate. Actually it was even worse
by allertonm 16y ago
Good piece - having built a big Rails application, I love the speed of development but your comments about scaling totally resonate. Actually it was even worse in my case due to us using jruby (add VM overhead to the mix.) RAM is often the most expensive component of the cost to run an application and as things stand now Ruby/Rails makes it hard to keep those costs down.
I really like the idea of using Ruby's continuations to avoid the need to write all of your logic in CPS (ala node.js.)
From the memory standpoint, doesn't this just move your memory overhead from the machine stack to continuations on the heap? I do realize there are some advantages to heap over stack, especially on 32-bit architectures.
And in addition, what kind of overhead do you get when creating a continuation for the kind of deep call stacks you're dealing with?
- jules 16y agoContinuations are not the best solution for not writing asynchronous code in CPS, not in terms of ease of use and not in terms of performance. Coroutines are cheaper as they don't involve stack copying or elaborate compilation schemes, and they map more directly to the problem. That said continuations don't necessarily involve stack copying. The implementations of continuations that don't do make normal procedure calls slower as far as I know.
- barrkel 16y agoIf you consider the stack as an implicit closure-like argument, coroutines are continuations for the purpose of cps (no resuming from an earlier activation record).
- allertonm 16y agoPerhaps I'm being a bit dense, but it's hard to see how one could take an existing asynchronous API and adapt it to use co-routines, since the point where you'd ideally want to "yield" is behind the API - and it may not be desirable or even possible to change this (for example, changing a C-based driver to use Ruby-level co-routines would be a challenge.) OTOH it's fairly straightforward to store a continuation and resume it in a callback. So while I think you're correct, pragmatically I suspect this approach has more chance of being implemented.
- jules 16y agoWhy is yielding to a coroutine from inside a callback a problem, but calling a continuation isn't?
- allertonm 16y agoYielding inside a callback isn't a problem. I was actually thinking of the other yield you would need, the equivalent of suspending the continuation - but I later realize that this is no harder than the continuation case. So I was just being dense, my apologies. And it appears that Ilya (the OP) has been looking at this approach using fibers too http://www.igvita.com/2010/03/22/untangling-evented-code-with-ruby-fibers/ http://www.igvita.com/2010/03/22/untangling-evented-code-wit... - and the comment thread on the OP suggests that this is in use at Postrank.
- jules 16y agoThe problem with continuations is that they can be called multiple times, so before invoking it you have to copy the call stack associated with it and execute on the copy. Coroutines don't require copying anywhere. Creating a coroutine can be done in a couple of instructions (allocate a new stack and set the top of the stack to the code of the coroutine), and yielding is also just a few instructions (mov a new stack pointer and return). Essentially if you view coroutines and continuations as data structures then continuations are a functional / non destructive version of coroutines, and like other functional data structures they are slower. I don't know how Ruby fibers are implemented but they seem to have coroutine semantics, so they are probably very efficient as well. Adapting a callback library to coroutines is very simple. You have a user coroutine that runs the user code, and you have a system coroutine that manages the resource that calls you back when it's ready (e.g. downloading a web page). When the user code requests something from the resource it yields to the system coroutine. When the system has the response ready the system yields to the user code again. This is nearly the same for continuations, the difference is that yielding involves copying the stack of the continuation that is yielded to.