Questions for an Enterprise Architect
Erik Dörnenburg answers: What is Enterprise and Evolutionary Architecture?, discussing 4 issues: Turning strategy into execution, Ensuring conformance, Where do the architects sit? Buying or building?
The content has been bookmarked!
There was an error bookmarking this content! Please retry.
Posted by Werner Schuster on Aug 24, 2007
Here's how to use the new Fiber class (warning: class name may change) to generate an infinite sequence of Fibonacci numbers. I use "generate" in the sense of Python's generators. Ruby's new fibers are "semi-coroutines".
1 fib = Fiber.new doThe code prints out the first 20 Fibonacci numbers. The concept it uses is called Coroutine. Basically, invoking
2 x, y = 0, 1
3 loop do
4 Fiber.yield y
5 x,y = y,x+y
6 end
7 end
8 20.times { puts fib.resume }
Fiber.yield call stops ("suspends") execution of this code (don't confuse it with yield used for executing blocks). If you're familiar with debuggers, imagine hitting "suspend" on a thread or seeing the thread hit a breakpoint. fib is a handle to this Fiber, and can be used to manipulate it. The fib.resume call in line 8, does exactly what it says: it resumes the execution of the Fiber's code with the statement after the Fiber.yield call. Fiber.yield called with a parameter y. In a way, this can be thought of as similar to return y. The difference between a subroutine's return and a coroutine's Fiber.yield is what happens to the context of the code after the call. return means that, say, a function call's activation frame (or stack frame) is deallocated, which means all local variables are gone. With a Coroutine, yielding keeps the activation frame and all the data therein alive, so the code can use it when it'sresumed later. Fiber.yield A fiber is a unit of execution that must be manually scheduled by the application. Fibers run in the context of the threads that schedule them. Each thread can schedule multiple fibers. In general, fibers do not provide advantages over a well-designed multithreaded application.However, using fibers can make it easier to port applications that were designed to schedule their own threads.Sasada Koichi, developer of the Ruby 1.9 VM, formerly known as YARV, gives some more information on the ruby-core mailing list
These method names (resume/yield) are from Lua. "transfer" is from Modula-2. "double resume error" is from Python's generator. BTW, I'm thinking about name "Fiber". Current Fiber means Semi-Coroutine. Fiber::Core is Coroutine. Yes, name of Fiber is from Microsoft, but it's means Semi-Coroutine such as Lua's coroutine and Python's generator.Semi-Coroutines are asymmetric Coroutines which are limited in their choice of transfer of control. Asymmetric Coroutines can only transfer control back to their caller, where Coroutines are free to transfer control to any other Coroutine, as long as they have a handle to it.
It is still hot topic among core developers. But fibers (and external iterators) are likely to remain in the final 1.9, more likely than continuations.Note: Continuations, a feature long absent in the Ruby 1.9 branch, were added to Ruby 1.9 in May despite concerns about whether they were feasible with Ruby 1.9's kernel threads.
In modern concurrency settings they [Fibers] are becoming increasingly useful, however. Without them, or something like them (e.g. Rubinius Tasks), you must play some very ugly games to get lightweight concurrency -- see the use of explicit continuation-passing (functions, not Continuations) in Scala's actors library for an example of the best that can be hoped for in their absence.This last comment, brings up an important point. If Fibers get adopted in Ruby, this will create headaches for Ruby implementations targeting the JVM or CLR, such as JRuby, XRuby, Ruby.NET or IronRuby. None of them currently support Continuations because manipulating or reading the callstack is hard or impossible to do in these VMs. The lack of Continuations is a controversial issue, but doesn't seem to have caused problems with e.g. JRuby, because they are not widely used in Ruby. The only use of them in the Ruby 1.8 standard library is the Generator implementation, but, for instance, JRuby 1.0, solved this by implementing the same functionality without using Continuations.
Granted, Fibers will make things harder for JRuby
Improve Java Garbage Collection, Runtime Execution, and JVM visibility with Zing
Monitor your Production Java App - includes JMX! Low Overhead - Free download
Why NoSQL? A primer on Managing the Transition from RDBMS to NoSQL
Getting Started with Stratos - an Open Source Cloud Platform
This looks like a slightly overcomplicated version yield/generators in python. Not really sure what the difference is on first look.
Generators in Python only yield to their caller, whereas Coroutines can yield to any other Coroutine - these are the Asymmetric Coroutines mentioned in the article. So: Coroutines are the more general concept... from what I can gather from the ruby-core discussions, it seems like Fibers provide the functionality for all this. Everything else - Generators, External Iterators etc, is built on top of them. Again: this topics seems to be in flux, so things might change.
It's possible to emulate Coroutines with Generators to a certain degree:
www.oreillynet.com/onlamp/blog/2004/07/my_own_t...
www.ibm.com/developerworks/linux/library/l-pyge...
These articles describe a method where Coroutines are emulated by having a central scheduler which creates and calls the 'Coroutines', which are in fact Generators. One 'Coroutine' can yield to another 'Coroutine' by yielding to the scheduler with a handle to the 'Coroutine'; the scheduler then searches his list of 'Coroutines' and calls it's Generator next method on it.
There's all kinds of work being done on this in the Python space, with things like Stackless Python en.wikipedia.org/wiki/Stackless_Python
Can someone explain why (except for a bit of syntax sugar) this is different from just creating and using an object with persistent member values. For example, something like:
class Fibber
$x, $y = 0, 1
def fib
$x,$y = $y,$x+$y
$x
end
end
fib = Fibber.new
20.times { puts fib.fib }
This feels much more like Object-Oriented "business as usual" than any form of concurrency.
The following is I quoted from my course slide, it might help.
"A coroutine properly handles the class of problems that require state information to be re-
tained between successive calls (e.g. finite state machine)."
Did you miss the first zero in the sequence, so did the code in the article? Sorry if I'm wrong, I don't know Ruby.
It's tough to tell from the example [which was explaining the syntax of using them, not how they are used].
The basic difference is that you don't have to use those globals, as it saves context from call to call.
Think of it as if it were threaded, above. The benefit of threads being that they keep track of local variables and context [local to themselves] very nicely, so you don't have to worry about storing it away in a global then reloading it when you go back.
The other benefit of 'real' threads [that they automatically interleave] isn't present with these. But the variable storing stuff is.
The fib example isn't very good. Normally when a method yields it just starts a new invocation of a block that was passed to it. Execution continues after the yield when the block exits. It's just syntax sugar for calling a function, really. Ruby is a bit weird and allows blocks to force their caller to return, creating the illusion that the code passed to the iterator is in control. Actually it's not. Code passed to a '(1..10).each' iterator is just a function that must exit before the next iteration.
Unlike objects with regular yield you should think of Fibers as having their own stack. Here's a contrived example that counts down from 10 but uses recursion to do it. Notice that the while loop is a consumer of the @nester Fiber. There is no consuming block that must exit on each iteration. In your example the fib method must exit on each iteration and in #each loops the consumer must exit. Neither the consumer nor the producer must exit during iteration with Fibers.
def nest(a)
if a > 0
Fiber.yield a
loop(a - 1)
end
end
@nester = Fiber.new do
nest(10)
end
while (a = @nester.resume) do
puts a
end
Recursion is nice for some things (though counting isn't typically one of them) and that's the real advantage of a fiber. It allows you to put recursion in your generators and call them from your own possibly recursive code.
Oops. In nest(), change loop(a - 1) to nest(a - 1). I'm not very good with names.
Erik Dörnenburg answers: What is Enterprise and Evolutionary Architecture?, discussing 4 issues: Turning strategy into execution, Ensuring conformance, Where do the architects sit? Buying or building?
Sean Cribbs explains what Map-Reduce and Riak are, why and how to use Map-Reduce with Riak, and how to convert SQL queries into their Map-Reduce equivalents.
Chris Richardson shows how he ported a relational database to three NoSQL data stores: Redis, Cassandra and MongoDB.
Jean Tabaka challenges the audience to reflect on what Agile practices they are employing, how they are using them, ending with the questions “Why have their organization chosen to go Agile?
Andreas talks about the benefits of the Open Web and how it compares to proprietary stacks. He also talks about various projects that push the envelope like Boot to Gecko, Broadway and pdf.js.
Ron Bodkin discusses early adoption of Hadoop, NoSQL and describes MapReduce and related libraries and Frameworks. Other topics include Hive, Pig, multi tenancy, and security in a big data environment
Stephen Bohlen explains how Spring helps with interoperability between Java and .NET, demoing it with the help of a sample application.
Guilherme Silveira mentions some of the turning points in project development that may affect the quality of the code offering advice on avoiding writing crappy code.
7 comments
Watch Thread Reply