Showing posts with label Ruby. Show all posts
Showing posts with label Ruby. Show all posts

Friday, June 05, 2009

More of the Ruby Mendicant: Gregory Brown's Back with Another Great Project

For the second interview in this morning's double-header, I've got a quick one with Gregory Brown (@seacreature) about his recently announced second run as the Ruby Mendicant. This is a big opportunity for the ruby community. I hope you'll pitch in a few bucks.


RubySpec has been a huge success in the Ruby Community, but it's just one part of a vision that Charlie Nutter put forward at the 2006 Ruby Implementors Summit. Another leg was an live look at how any given Ruby implementation performs against the spec . You're talking about making this happen as part of a second instance of Ruby Mendicant. Can you tell us a little more about your plan?

Greg I will essentially be trying to build and deploy a web based reporting system that aggregates RubySpec definitions and results across every Ruby implementation and language version we can get our hands on. This will help people know what the state of the specs actually are, and how well various implementations and Ruby versions run against them. I made a rough mockup of what one of these reports might look like. We already have some changes to MSpec and a skeletal database model in place that could facilitate this sort of report. The full scope of the project will emerge once I have a chance to talk with implementers and Ruby community members about what their needs really are, and I expect to have those details collected together by June 15th. But basically, I hope to support in one way or another most or all goals that Brian Ford mentioned in his idealized case of the needs RubySpec would serve.

Whatever the final roadmap is, I hope to implement something functionally useful to the community over the course of just 8 weeks. Based on the ideas I have in my head right now, if I just hacked on this in my spare time, it might be something I could put together over the course of 6 months or a year. But with me taking hard math classes in the fall, I'd rather fill my summer lows with some open source Ruby awesomeness. That's where a sustained, community funded effort, will come in handy. The cool thing is that it'll be easier to do it this time around than it was with Prawn....

What did you learn from being the Ruby Mendicant last time that will change your approach this time around?

Greg Okay, so there are two things that'll change pretty radically:
Funding and Organization.

When it comes to organization, I plan to have a fairly clear road map set up before I begin on this project. Ruby Mendicant collected 100% of funds before a *project* was even selected, which made it a very different proposal. The upshot here is that since we're mainly just building a web application to interface with an existing technology, there are less unknowns in the core needs for this project. While there are a ton of directions we could take it that may lead into dark spots, I think I can define with some accuracy a useful set of features from the get go. I don't want to get all ponies and rainbows about what you can expect from this project, things will change and I have no way of promising to deliver a certain set of features within a rigid time-line. But generally speaking, there are less variables here so long as we keep the project scoped tightly enough. This new way of organizing things might appeal more to those who were afraid of the ambiguity of purpose that the Ruby Mendicant project had.

Then there is funding, a topic I absolutely hate, but over the course of the years, I have realized is pretty important to successful free software development on all but the most sexy projects.

Since the time I finished up with Ruby Mendicant, I've been doing most of my work through a devshop called Madriska Media Group run by my friend Brad Ediger. Brad has been pretty cool about letting me spend some company time on open source here and there, which has been great. When I mentioned my ideas for what I wanted to do with RubySpec, it went over well. Madriska will be dedicating some time, money, and resources to helping me get this new project up and running. This will help out a lot, but due to the aggressive timeline and scale of the project, won't by any means cover the full costs.

So while I'm most of the way covered here in terms of funding and available resources, I'd like to help make sure I don't run into financial troubles again, like I did with Ruby Mendicant. That means I'll need to open up a donation drive even though I have the effort partially funded.

How can people contribute?

Greg Code help will definitely be desired a little later on, but right now, the community at large can most easily help out by throwing me some money. I am asking for $500 a week, for which I will guarantee at least 10 hours of dedicated work on this project. This will of course be in addition to any work that Madriska pays for, and not just a weekly minimum.

I plan to do the funding in two drives, one that's already open, and another that will open July 15th. Each of these cover 4 weeks of funding (a total of $2000). This time around, I won't need to raise all the money up front before I start, so those who are skeptical can wait to pitch in if they'd like. While work will begin officially on June 15th, I'll be preparing a roadmap and doing some preliminary planning over the coming weeks. Unlike Ruby Mendicant, you'll have a pretty clear sense of at least what my goals are before this project kicks into full gear.

Of course, those who trust me to do good work, and enjoy what I did with Prawn can go ahead and donate now if they'd like. I'm not going to complain :)

Have any organizations stepped up to help?

Greg When it comes to funding, MountainWest Ruby will match dollar for dollar on the first $500, so thanks for that Mike :) Obviously, if other organizations want to sponsor this effort monetarily, I'd be happy to take their money, so do get in touch if you're interested. What I do need to let folks know is that this is not a bounty, or a hired job, so consider your donation a gift if you make one at all. I can do my best to make sure you won't regret it, but be sure to ask questions if you don't know what to expect.

When it comes to slinging code, I've been having good conversations with the JRuby and Rubinius guys, and will reach out to the other implementations soon as well. I mostly just need their guidance on certain aspects of things because I'm fairly new to the happenings of Ruby implementations. While there is no formal agreement, I think that these guys will help out when they can, and that alone will be huge.

I think the best bet is to spend a few weeks getting together a function alpha for the community to poke at, test, and provide feedback for. After that happens, regular contributions via github would be most welcome. BTW, the working title of this project is Unity, which hopefully describes its intentions pretty well :)

Click here to Tweet this

Feedzirra and Typhoeus: an Interview with Paul Dix

With my recent Questions Five Ways series, I've gotten away from my regular interviews. To break that dry spell, I've get a pair great interviews for you. The first is with Paul Dix (@pauldix), the developer of Typhoeus and Feedzirra and budding author (see below). He and I had a good talk about how he builds such great libraries. Read on, I think you'll like it.


What kind of hacking do you do when you're not building cool new libraries for the community to play with (and argue about)?

Paul At work I'm doing Ruby stuff with a mix of Sinatra and Rails. The company I work for, kgb, is building a new web product in the aggregation/search space. That area has been my primary focus for the last three years so I get to play with stuff like machine learning and natural language processing. We're a bit early on so I'm not sure yet what I'll be using for that. Probably Java because of the availability of libraries, more speed, and better memory use/cleanup. Six months ago I did some research into the methods being used by competitors in the Netflix prize. It was really interesting stuff and I did most of the heavy lifting in Java. I started out with Ruby, but found that I needed a little more efficiency. However, I still did all my scraping and data preparation in Ruby.

During my free time I'm not getting to do much hacking these days. I'm working on a new book for Pearson called Service Oriented Design in Ruby and Rails so that takes up quite a bit of time. However, things like my new library are directly related to my day job and the topic of the book. So free time hacking along those lines will probably be fair game for the next six months.

Have you considered using JRuby to make the bridge between Ruby's accessiblity and Java's libraries easier to cross?

Paul I definitely have considered JRuby and I've used it before. However, with machine learning tasks it really comes down to speed of execution. Some jobs can take minutes to hours even with Java. Speedup factors of 2-10 times or more matter in these cases. Of course, I pulled that number completely from thin air. Depending on how it's used JRuby may be a viable competitor to Java, but I'd have to test to make sure. For interfacing with Java libraries it's definitely a good option.

How did you discover Ruby? Why have you stuck with it?

Paul In 2005 I was working as a C# programmer at McAfee. I had arranged to quit work and go back to school in the fall. Since I didn't have to worry any longer about making my living day to day in the Microsoft world, I decided it was a good time to switch up languages. My first stop was Python. I read Mark Pilgrim's Dive Into Python [now available for Python 3] and wrote a few scripts. I thought it was ok, but kept looking around and found Ruby.

I kind of picked up Rails and Ruby at the same time. I read through the first edition of Agile Web Development with Rails [now on it's third edition] and Programming Ruby [also in a third edition]. I liked it, but that's not what really made me stick with the language. I had also moved to NYC and started going to the NYC Ruby users group (nyc.rb). I think that's what really made me stick with Ruby. I enjoyed the community and how passionate everyone was. The development gains with Rails didn't hurt either.

It sounds like finding Ruby was something of a process. I'd assume you're still looking at languages as the process continues. What other languages have moved beyond catching your eye and are holding your attention?

Paul I try to be deliberate about what skills I focus on and languages are definitely a part of that. At this point nothing is really holding my attention as a replacement for Ruby. I haven't seen anything that fits that bill so instead I'm looking for supplemental languages that cover Ruby's weak spots. I'm playing around with Scala at the moment and using it to implement a something at work that will be very high traffic. I think languages geared towards parallel, distributed computing are very interesting right now.

How did you get interested in Ruby's http and xml performance?

Paul I've had an idea to build a feed aggregator/search service for a while now. Most of my free time hacking has revolved around playing with different pieces of that system. Obviously, a big part of that is pulling stuff down from the web and parsing it. I got even more into the HTTP thing with my current job. The system we're building is based on different HTTP services written in a variety of languages. I want Rails to be the front end for these, so a performant HTTP library is the first step.

Could you walk us through your process for building highly performant code?

Paul Generally I start out with something that I think is slow. Like feed parsing, for instance. I didn't like the existing libraries and I thought it could be done better. Luckily, Nokogiri had recently been released and was touted as super fast (which it is). From there I wrote a spike. No TDD, no elegant design. Just some ugly spaghetti that will actually do the basic task. That's enough to wrap a benchmark around so I run that. If my approach is fast then I figure I've proved out the concept. From there I rewrite the whole thing using TDD and and take a little more thought for API design.

On Typhoeus it was a bit different. There were already some speedy options out there, they just didn't work perfectly for me for one reason or another. So I built on the work that Curb had done and completely relied on libcurl for the real speed.

With any of my libraries where speed is a concern, some of the processing is done in C. It's just a fact of developing with Ruby. If you want performance you need to drop down to C. The truth is that my libraries really just piggyback on other developers' awesome code. It's just a matter of bringing it into Ruby or exposing it through another API.

There are two other things that I generally do as I write libraries. One is quantifiable and the other, not so much. I try to think about how many times certain sections of code will be run. This is the standard stuff you find in a data structures and algorithms course. Is an algorithm O(n) or n^2 or whatever. I'm not actually doing the big-oh calculation but I definitely think about if I'm executing something in an inner loop. It helps to have it in your head what the performance of a hash vs. .include? on array, if you're doing an eval, or any of the other things that might have an impact on speed.

The more quantifiable approach is to use ruby-prof for checking how long different calls are taking and where your memory is going. That library is crazy awesome.

Other than profiling, what kinds of code analysis are you doing, and what tools are you using to do it?

Paul I like to bounce ideas off of other people in the Ruby community. We have regular hackfests and I'll break out my code to see if people can point out where it sucks. I think code review is something that more people need to focus on.

I probably should be using things like flay, flog, and reek, but I haven't started yet. Another thing I really want to check out is Aman Guptu's perftools.rb. He gave a lightning talk on Google Perftools and his Ruby bindings to them at GoRuCo [ed. Aman's talk isn't posted yet, but hopefully will be soon].

Since you work TDD style on production code, which testing/mocking libraries do you use and why? Have you looked at tools like cucumber to guide your test writing?

Paul I've been using Rspec for a while now. I'm not a ninja, but it does what I need. I prefer the general style of 'describe' blocks and 'it' calls. I like the built in matchers and how the test code reads. I also used FactoryGirl for fixture data for a while, but my current work isn't backed by ActiveRecord so it's not applicable.

I looked at cucumber briefly, but I honestly don't like it. I know I'm in the minority in the Rails community right now for that opinion, but I found that I spent way too much time writing regexes and building up my test suite to work. I've talked to people that do client consulting that find value for client communication with cucumber style stories. I just don't have that need. I find that the regular test code is plenty readable for the people I work with and I don't have to spend a bunch of extra time testing. Ultimately, testing is just a means to an end. It's easy to get bogged down and spend an inordinate amount of time writing tests. I like to focus more on the implementation and what the code can actually do.

What approach do you use in working with C — Ruby-Inline, FFI, the traditional C API, or something completely different?

Paul I use the traditional C API. I should probably move over to FFI to make things play nicely with JRuby and Rubinius, but I haven't gotten to that yet.

What approaches to concurrency/parallelism are you finding most useful?

Paul I think the reactor pattern is really good. It's what EventMachine and libcurl multi use to fake parallelism. I find that's much easier than trying to deal with managing a thread pool and running IO through that. It's fine to run single threaded since we're using multiple processes anyway. You'll get a chance to peg your CPU even with multiple cores.

Are there other patterns (not necessarily concurrency related) that you find especially powerful/useful in Ruby?

Paul Ruby lends itself well to creating DSLs. ActiveRecord, DataMapper, and many other libraries have great little DSLs for building up classes with complex behavior with fewer lines of code. I copied those styles when creating SAXMachine and Typhoeus.

The other thing that I really like is the use of proxy objects for lazy evaluation. I used this technique in Typhoeus to avoid making HTTP calls until absolutely necessary. When you make a call it actually puts it on hold and gives you a proxy object. The remote HTTP call isn't actually made until you access something on the proxy. That style made it much easier to hide the details of gathering a group of HTTP calls before calling out to libcurl-multi.

Earlier, you mentioned that you are working on a book. When should we be looking for it to hit the shelves?

Paul I'm really early on in writing the book so it won't be out for a while. The pre-release chapters will be put on Safari Bookshelf as I finish them. The first bit should be there in the next month or so. The final version of the book probably won't be in dead tree form until March of 2010.

Click here to Tweet this

Thursday, May 07, 2009

Diamondback Ruby Interview

After the announcement of Diamondback Ruby on ruby-talk a bit ago, I decided to contact the developers to learn more about what they're doing. Two of the team members, Mike Hicks and Mike Furr, and I ended up having quite a conversation that I'm posting as the interview below.


What do you hope to learn from this project?

Mike Hicks There is a long-discussed tension between statically (or explicitly)-typed languages like Java and dynamically (or implicitly)-typed languages like Ruby. My hope has been to discover how to include the best aspects of static and dynamic typing in a single language. We all really like Ruby's design and features, and felt it was the right language to start with. As we go, we'll look to derive general design principles that make sense for most any programming language, to simplify and improve the process of programming generally.

Static types are useful at catching bugs early, and serving as useful documentation (indeed, RDoc includes a pseudo-type for Ruby library methods). But static type systems may reject perfectly correct programs due to their imprecision, and thus can "get in your way," particularly when doing rapid prototyping. On the other hand, dynamic types suffer no imprecision, but delay discovery of some bugs until run-time. It can be particularly annoying to mistype the name of a method in a call deep within your complicated program, and have the program fail after running for a long while with "method not found," when having a type checker would have immediately revealed the mistake. The challenge is to incorporate the best bits of both approaches, e.g., to not reject programs prematurely (as a static type system could) while finding as many certain errors in advance as possible.

Mike Furr We had two main hypotheses going into this research. The first hypothesis was that development in dynamic languages doesn't scale. This is a hard thing to measure, but there is a fair amount of anecdotal evidence that seems to support this. For example, just recently a few of the Twitter developers were interviewed about moving their infrastructure from Ruby to Scala, and static typing was mentioned as one of their advantages of Scala over Ruby.

The second hypothesis we had was that most people "think in types" even if they don't write them down while programming. Thus, we imagined that most code written by Ruby programmers would be accepted by a sufficiently advanced static type system. Thus we hoped to be able to design a type system for Ruby that was powerful enough to handle the kind of Ruby code people actually write, but not be so complex that it was impossible to use.

What made you choose Ruby as your test implementation instead of Python, Perl, or one of the other widely used dynamicly typed languages?

Mike Furr When we were first throwing around ideas about analyzing a scripting language, Ruby seemed to be the language with the most momentum, likely because of the release of Ruby on Rails a few years earlier. Ruby is also a rather young language and its syntax and semantics are continuing to evolve. Ideally, we hope that our research could influence future directions for Ruby, although much of our research would be applicable to other languages as well.

Mike Hicks I really like Ruby's design, particularly the principles that "everything is an object" and "duck typing." We also liked that Ruby was the core of Ruby on Rails, whose popularity was increasing. In our exploration of Perl, an early contender, we became frustrated with the huge number of overlapping language features, and quite surprising behavior in many instances. We didn't see how to write a useful static analysis for Perl programs without a lot of difficulty. We thought about Python only cursorily, and I don't recall any particular downsides that came up.

Why did you choose OCaml as your implementation language?

Mike Hicks Our group at Maryland uses OCaml almost exclusively for writing static analysis tools, particularly for analyzing C code, so it was natural to want to use it for this project, too. OCaml, in my view, is the perfect language for writing a compiler or analysis: its syntax is terse (as compared to Java, say), and features such as first-class functions and pattern matching very much simplify the process of writing tools that analyze or manipulate structured data, like abstract syntax trees. We followed the lead of the CIL (C Intermediate Language) project, also written in OCaml, in designing RIL (Ruby Intermediate Language), the core of DRuby. For example, both CIL and RIL syntax trees are simplified after parsing to make analysis more manageable.

Mike Furr OCaml is my favorite language to program in and I have been using it throughout my time in graduate school. However, I also think it is the right tool for the job. The quintessential example for functional programming languages is writing a compiler, and DRuby is essentially a compiler front-end. OCaml's type system is also a real asset in developing a complex code that manipulates abstract syntax trees.

A lot of folks seem to think that you've written a Ruby implementation in OCaml instead of a type analyzer for the existing 1.8 Ruby. Do you think an OCaml implementation of the language would be a good thing? Why or why not?

Mike Hicks This is a hard question to answer. Why might one want to implement an interpreter in one language vs. another? I can imagine several reasons: performance, portability, maintainability, and reliability, among others. Developers often implement interpreters or VMs in C/C++ for reasons of performance and portability. But C and C++ encourage programming "on the edge of safety," so mistakes can lead to crashes, security vulnerabilities, etc., hurting reliability and maintainability. By contrast, coding in a high level language, e.g., Java or OCaml, avoids many reliability problems, thanks to type safety and garbage collection, but at the cost of some performance. (For grain-of-salt interlanguage performance metrics, check out the Computer Language Benchmarks Game, http://shootout.alioth.debian.org.) And the language is really well-suited to writing compilers and interpreters, thanks to its rich structured datatypes and pattern matching. In my experience, an OCaml-based compiler or interpreter is much more succinct than one written in Java. So I think it's a good option.

Mike Furr There are already several implementations of Ruby available and so adding another one simply because it was implemented in OCaml doesn't seem like a good idea to me. Maintaining an implementation of Ruby is a lot of work since the language continues to evolve its syntax and semantics from version to version and the API of Ruby's standard library is also tied to particular Ruby versions. Instead, there would need to be a fundamental new feature that an OCaml implementation would provide that developers would find useful. For example, it might be interesting to explore compiling Ruby programs to native code using the OCaml bindings of LLVM and doing type driven optimizations based on Diamondback Ruby's type system. This would be a lot of work and I have no idea if the resulting code would be any faster than some of the newer Ruby virtual machines, but it could be a fun project.

How does static type inference affect the balance between testing and debugging? How does it affect the testing and debugging processes?

Mike Hicks Type inference is a debugging aid, I suppose. It is meant to help identify bugs that could come up, and do so without requiring you run your program. It is not a replacement for testing, though. Essentially it finds out whether you are programming with a certain level of consistently; if in one place you declare your method to take three arguments but elsewhere call the method with four, that's an inconsistency. But it doesn't prove that your code does "the right thing," e.g., whether you formatted your output string correctly. You need tests for that. Our approach allows one to help the other. When you write tests, DRuby will profile their execution to provide information that helps type inference. And type inference helps you identify some bugs without having to run tests.

Mike Furr Static typing is a tool just like testing frameworks and debuggers. All of them are meant to improve the quality of the software being developed, and each has their own advantages and disadvantages. The major advantage of static analysis is that it is able to reason about every path through your program simultaneously (and before it is run). Static types also provide terse, verified documentation. If you method has a type annotation that says it returns a Fixnum, that annotation will never become stale and can be trusted by any other developer who is calling your method.

However, static typing isn't perfect and is not meant to replace other QA techniques such as testing. One of the goals for DRuby is to allow programmers to incrementally add static types to their code bases, allowing them to benefit from extra checking where they want, without requiring changes to the entire code base.

You mentioned that you've found several potential errors in Ruby libraries and prgrams as a result of type inference analysis. What kinds of problems are you finding? How could the Ruby community take advantage of these kinds of discoveries?

Mike Furr The Ruby community has accepted test driven development as a standard practice and so we didn't expect to find a large number of errors. However, getting 100% testing coverage is often difficult and, not surprisingly, many of the bugs we found were in error handling code that was not exercised by any test cases. Some of these bugs were extremely simple, like misspelling a variable name, or referencing a method that did not exist.

One bug that I found particularly interesting was where a program called a method in the "File" class that didn't exist. This code was covered by the test suite and didn't cause a test failure. The reason for this was because the testing code monkey patched the File class to add the method before running the test suite. Thus, you would only encounter the problem if you executed the code outside of the test suite.

We hope that DRuby will develop into a tool that developers can run on their projects as part of their own quality assurance process. In the mean time, we have been filling bug reports for the errors we discovered so that the authors can fix them. For example, we found 2 errors in the Rubygems package manager that have already been fixed in their latest release.

What kind of feedback are you getting from Rubyists?

Mike Furr We've gotten some very encouraging feedback so far. In fact, despite the legendary flame wars between static and dynamic typing, I haven't received any negative comments about the idea of bringing static types to Ruby. A lot of people are using Ruby these days and so a tool that can help improve their development process through finding bugs or improving documentation is clearly appealing. Diamondback Ruby still needs some polishing so that programmers can begin using it on their own projects, and this is something we are going to continue to work on. Eventually, we'd like to perform some user studies to measure the effectiveness of the various features of Diamondback Ruby, and so its usability is certainly important to us.

How well do Ruby programs perform under Diamondback Ruby?

Mike Furr Diamondback Ruby uses a combination of static and dynamic checks to ensure that Ruby programs are well typed. Programs that can be checked purely statically (which we hope will be most of the time) will have no overhead at all since the programs can be safely run by a traditional Ruby interpreter unchanged. However, if the program does require a runtime check, then individual objects or methods may be instrumented to ensure they don't violate their types. When dynamically checking objects, we instrument the eigenclass of the individual object so that only methods calls to that object must be checked (not every object of the same class). Thus the checks are pay-as-you-go: the more objects that require dynamic checks, the higher the overhead. Therefore, it's hard to use a single measurement to quantify the overhead as it can vary from execution to execution of an application. I have run some micro-benchmarks and observed a 15% slowdown in one case, but this data point should be taken with a grain of salt, as it was merely to convince myself that the instrumentation was working and not egregiously slow. An application that rarely uses a dynamically checked object may see almost no overhead, but if the application calls methods on that object in a tight loop, it could be significantly higher.

Are you using the RubySpec framework to drive your implementation?

Mike Furr DRuby includes a dynamic analysis that allows us to reason more precisely about features such as eval(). This analysis requires us to parse the original Ruby program into our intermediate language, add any instrumentation code, and then write the transformed program out to a separate location on disk to be executed by the Ruby interpreter. This whole process was rather tricky to get right, and we used the RubySpec test suite to ensure that our transformations were correct. It was definitely a great help to have such a comprehensive test suite.

We haven't used the RubySpec tests to drive any type analysis for the standard library just yet, but I can definitely see using it in the future as we continue our research.

I'd really like to see OCaml get more play, but I keep seeing books like this and wonder when a good OCaml book for non-Math/CS types is going to hit the shelves. What will it take to get OCaml in front of more developers?

Mike Hicks My observation is that languages take off when library or framework support for some important set of applications hits critical mass. Then developers interested in that application intuit that it's easiest to build that app in a certain language or framework, and then go off and learn what they need to learn. Then those developers start building more libraries and the language is used for other things. I think we can see this trend in Perl (first killer app: text processing), Java (first killer app: applets), Ruby (first killer app: Rails), etc. We're starting to see more adoption of Erlang, thanks to the rise in multi-core and high-availability commercial systems, and we're seeing a growth in Haskell, at least in part because of all of the code you can get for it (though I can't speculate on what its killer app is).

When I first started doing work in static analysis, C/C++ were the languages of choice, oftentimes building on gcc or other existing tools. But then George Necula and his students wrote CIL (C Intermediate Language). Nowadays many, many tools are written using CIL as the front end and intermediate language, by people who never were "functional programming people." CIL was just so much better, clearly, than anything else, that people flocked to it. (As of today there are 297 citations to the CIL paper, according to Google Scholar — esp. noteworthy for an "infrastructure" piece of work.)

Of course, C analysis and other "symbolic computations" on programming languages are a niche area, and not likely to bring in the masses. OCaml still needs that breakthrough use-case of great interest that will push it over the top. It remains to be seen what that will be. But once it's found, the books, tools, etc. will all follow.

Mike Furr I agree with everything that Mike (Hicks) said but would also add that OCaml needs to overcome its branding as an "academic language". I have found that a lot of programmers think of functional programming as a fringe concept, perhaps invoking bad memories of struggling with it as an CS major. At the same time, one of the features people really seem to love about Ruby are blocks, which of course are a functional programming technique. I think that Ruby's syntax plays an important role here: programmers don't have to understand what a higher order function is to be able to use a block and yet they can immediately see the usefulness of the technique. However, a functional programmer might find this syntax restrictive (why only one block per method?). Perhaps the road to OCaml's adoption will be through Ruby which gives a gentler introduction to some of the same ideas used in ML.

Click here to Tweet this article

Monday, May 04, 2009

Author Interview - The Well-Grounded Rubyist

Now that Manning has released The Well-Grounded Rubyist by David A. Black, I wanted to sit down with him and talk a bit about his new book.

His Ruby for Rails set an awfully high bar as the book that really explained Ruby to Rails users (and everyone else). The current title has been called a "reworking" of the former to remove the Rails bias.

Hopefully this interview will whet your appetite for David's new book.


Ruby For Rails has been one of my favorite Ruby books since it came out. I'm really excited that The Well-Grounded Rubyist is coming out in a matter of weeks. What do you think is the best thing about your new book?

David The title! Well, not really, but I do love the title, if I say so myself. (In case anyone's wondering: I did take inspiration from "The Well-Tempered Clavier".)

As for the material and the content: I have always found it gratifying when readers single me out as someone who can explain things clearly and in depth, and I believe TWGR delivers in that area pretty much throughout. I spent thirteen years as a full-time teacher, and I've been writing all my life. I love helping people understand things, and that's what I've worked most diligently at doing in this book.

You call your new book a repurposing of Ruby For Rails. How do you approach Ruby differently when writing for a Ruby developer rather than a Rails developer?

David To a large extent it's a question of the topic coverage. TWGR maintains, I think, very much the same "voice" as R4R, and addresses the reader at the same level. But the topic coverage has changed a lot — and not just because Rails has been taken out. There's material in TWGR on threads, sockets, sets, encoding, enumerators, and more, none of which were in R4R, and greatly expanded coverage of other built-in functionality include file I/O, ranges, system command invocation, and others.

Selecting topics was a big part of writing R4R, since the whole point was to write a Ruby book that was "optimized" for a certain readership. And as I started thinking about TWGR, I realized that careful, creative topic selection was important for a "just Ruby" book too. I wasn't setting out to write a complete language reference, so the process of deciding what to include loomed large.

You take a pretty different approach to explaining Ruby than most other authors/speakers. What do you think makes your approach work so well?

David Typically, introductions to class-based object-oriented languages, including Ruby, start by talking about classes, and then bring objects into the picture as instances of classes. I look at it the other way around. The object is the fundamental unit of meaning in Ruby, so why not start with that?

Furthermore, I like to put object dynamism on the radar very early — the fact that objects are not constrained to the abilities they derive from their class but can take on behaviors of their own. Of course most Ruby developers don't spend their days writing


 def dog.bark
and such. But I do believe that if you learn about objects and their life-cycle first, and then look at classes as a kind of convenience macro for spawning objects, your knowledge of Ruby will build smoothly from there, and you won't think that object-specific behavior is a blemish.

A lot of publishers are opening up the book writing process these days. How much did Manning's MEAP affect your book?

David I'll do a bit of soul-searching here, if I may. I'm a very territorial writer. I don't like the idea of leaving a paper trail showing what I later decided to change or fix or improve; I think of writing as a basically closed-door artistic process, and just as I don't want people listening in while I'm practicing the cello, my instincts run strongly against releasing unfinished written work.

I have, nonetheless, tried to get into the spirit of the open-process era, because I think it's been proven effective for technical writing and because I know that a lot of my territoriality comes from my temperament rather than from concern about the final product. Besides, even without the MEAP, the manuscript is sent out to a body of twelve or fifteen outside reviewers at three points along the way — so the "closed door" isn't all that closed anyway!

So I've participated in the MEAP, probably not plunging into it as fully as some authors do but trying to meet it at least halfway, even if it means leaving (or, perhaps, expanding) my own comfort zone. I think the book has benefited from it.

So far, you've had your hand in books, videos, conferences, and training. What does the future hold for you?

David Don't forget programming :-) As for the future: I've started working on a book for the Pragmatic Bookshelf, entitled "Rails Choices". It's a guide to how to think about and make sense of the many possible choices (and it's an increasing number, as we get toward Rails 3) that face developers and project managers when they're starting a Rails project: everything from which version of Ruby to use to which templating system to which session storage mechanism. I'm not going to try to cover every possible choice; I'm sticking to the out-of-the-box defaults plus a select handful of proven, popular alternatives. I want to get beyond the personal preference level, and help people examine the needs and priorities of their specific projects, and make choices based on that.

In general, I'm finding my multi-faceted Ruby-oriented life very rewarding and enjoyable, and I'm hoping for more of the same.

A lot has changed in the Ruby world since R4R, which of those changes are you most happy about?

David I'm happy about the fact that Ruby is in much wider and more prolific use, and that we have so many books and events. (I don't think any other language has as vibrant a world-wide conference culture as Ruby.) It's also great to see that the "What's this 'Ruby' thing?" syndrome among Rails developers has dwindled; just about everyone seems to understand from the outset how Ruby and Rails related to each other (and if R4R helped bring that about, I'm delighted).

I'm excited about the new Ruby implementations — JRuby, Rubinius, and others — and the coming-of-age of Ruby 1.9. As everyone knows who's followed along, the path to 1.9 has not always been smooth, but 1.9.1 is stable (that's the version the book addresses) and as I've delved into it more and more deeply, I've been more and more impressed by it.

Which ones impacted The Well-Grounded Rubyist the most?

David I choose:
(e) All of the above!

It's true actually. The wider adoption of Ruby is largely responsible for my enthusiasm about writing the book. The way the Rails world has come to terms with Ruby, to the mutual benefit of both, made me realize that a general Ruby book could also be "for Rails", in the sense that many Rails developers are eager to learn as much about Ruby as they can, whether every bit of it pertains directly to the bulk of their Rails work or not. The new Ruby implementations don't enter into the book directly, but it's hard not to feel inspired by that work, and many discussions surrounding implementations have touched on topics in ways that have probably affected TWGR.

And Ruby 1.9 — that's the main event, the major impetus for writing the book. It's been hard, because things have been changing a lot. Sometimes I'd write an example and then discover a few weeks later that the wording of some error message had been altered, or that something had shifted in the core class hierarchy. (I'm thinking specifically of Enumerable::Enumerator becoming just Enumerator.) It's been a moving target at times; but all the more thrilling is it for the book's timing to be such that it can address a stable 1.9 version while there's still a lot to learn and a lot of excitement.

What do you see happening in the Ruby world over the next year or so?

David Very hard to say — which is probably good, since it keeps it interesting. I think that Ruby 1.9 will achieve a lot more penetration. And we're seeing a lot more Ruby implementations, some based on the Matz code and some not. I don't just mean JRuby and Rubinius, but MacRuby, Ruby Enterprise Edition, MagLev, and others. I suspect we'll start seeing specialized subsets and forks of Ruby that aren't necessarily compatible but that are "Rubies", the way different LISP dialects are "LISPs". I think Diamondback Ruby is in that category, though I haven't looked at it closely.

In any case — here's hoping we have many more years of Ruby practice, experimentation, collegiality, and productivity. It's certainly been fun so far!

Click here to Tweet this article

Tuesday, March 10, 2009

Reia - A New Dynamic Language on the Erlang VM

Tony Arcieri (@bascule) Has been in the Ruby community for quite a while. He's got a strong interest in concurrency, which lead to my interviewing him about Revactor last year.

When he couldn't get the performance he was looking for with Revactor, he started working on a new Ruby inspired language on the Erlang VM — Reia. Tony was kind enough to sit down and spend some time with me doing an interview about his new project.


Besides yourself, who's using or working on Reia?

Tony A number of people have made minor contributions. One of the main people involved is Phil Pirozhkov, who is working on a web framework for Reia called Ryan.

The other contributors can be seen on the Github impact graph.

Who should be looking at it?

Tony Anyone interested in using dynamic languages to develop web or other network services, particularly people with a background in object oriented languages but not a lot of background in functional languages.

Other than web and network programming, what problem domains do you think Reia will do well in?

Tony Most of the areas I think it will stand out will be subsets of web or network programming, but perhaps I can go into some of those more specifically.

I think Reia will be good at the sorts of interfacing and automation tasks that Ruby has become incredibly successful at, but will carry with it all the distributed computing power offered by the Erlang/OTP framework. In that regard I think Reia will do well at automating tasks on distributed systems, particularly in regard to handling failures.

Cloud computing services offer a great platform for building distributed systems for automating tasks. I think Reia will do quite well there. It's the same problem domain that EngineYard is trying to address with Vertebra, but with Reia I hope the solution offered can be accomplished using one language rather than two (Erlang and Ruby in the case of Vertebra). I also expect Reia could work quite well in conjunction with the Erlangy parts of Vertebra.

I also think Reia could work quite well as a language for Erlang testing as it has full access to the Erlang runtime and excellent Erlang interfacing. I have strong hopes of seeing a tool like RSpec implemented in Reia and being able to use it for Erlang testing. RSpec has seen great success in the Ruby community and I hope I can bring it to the Erlang world.

In your blog post Reia and Object Aware Concurrency you talk about object concurrency, is this enough?

Tony No, and for that reason Reia also has processes and the full power of Erlang's actor model exposed at the language level.

Objects are there for cases that can be solved primarily with RPCs.

What about the FP claims that idempotency and freedom from side effects are the high road to concurrency?

Tony While Reia has a number of side effects not present in Erlang, great care is taken to localize side effects. All operations with side effects use copy on update semantics, and side effects are actually manifest in the form of destructive rebinding (which is in turn compiled to a pure functional form with single assignment). In most cases the side effects are completely localized to the function they occur in.

What is typing like in Reia — Static or Dynamic, Strong or Weak? Why?

Tony Reia has strong dynamic typing. This is largely inherited from Erlang which also has strong dynamic typing, although Reia does add automatic type coercion in a few places (e.g. interpolated strings)

I'm a fan of dynamic languages in general and this is one of the characteristics which drew me to Erlang in the first place. I find dynamic typing less obtrusive than static typing and believe it helps programmers develop more quickly.

How are you managing the differences between types in Reia and the Erlang VM?

Tony Reia's built-in types which do not have direct Erlang equivalents, such as strings and regexes, are represented internally as tagged tuples. Reia tuples are also tagged to distinguish them from the other types.

Calling out to Reia from Erlang is done through a "reia" module which automatically converts types which have direct equivalents, however types which can't be natively represented in Erlang such as regexes and tuples are left untouched.

Reia has first class syntax for calling out to Erlang which also automatically handles type conversions.

In The cutting edge of VM design you wrote:

"Approaches like software transactional memory ... are better suited to certain types of concurrency problems than Erlang's shared-nothing process approach."

What spaces are the Erlang VM and Reia not well suited for?

Tony Problems which are intrinsically tied to a central shared state are difficult to address in Erlang (and by virtue of that, Reia as well), because Erlang bends over backwards to avoid shared state. There are tools like ETS and Mnesia to model shared state, but solutions offered in Erlang are generally going to be much slower than solutions which can concurrently operate on the same state with tools like STM.

Ulf Wiger brought up the chameneos-redux problem from the Programming Language Shootout as a benchmark where Erlang does quite poorly.

It would be difficult to add Erlang-style fault tolerance to a system which uses STM (and for that reason it would be difficult to add STM to Erlang). Erlang isolates state per process, so when errors occur the process can simply crash and get restarted in a clean state by its supervisor. With STM unless you're able to completely blow away the shared state and start over in the event of a fault, you're left with trying to handle the error and roll back to a clean state, which is a far more difficult problem than Erlang's "just crash and start over clean" approach to fault tolerance.

Problems with a strong sense of time and a need for global synchronization are probably ill-suited for Erlang/Reia and better addressed by a dataflow language. Erlang/Reia are inherently asynchronous and designed with a work-at-your-own-pace approach in mind, using mailboxes to synchronize execution. If you want all processes to work in sync with a global clock your best bet would be to look elsewhere.

Numerical computing is another area where Erlang is somewhat weak, although Erlang/HiPE actually does significantly better at this than many other languages, like Perl, PHP, Python, or Ruby. As I intend to add various degrees of operator overloading to Reia it is only going to get worse in this regard.

All that said, the core Erlang developers are largely focused on making the existing model work better and not particularly willing to try to incorporate other concurrency paradigms like STM into the language. I'm quite happy they're doing that and are focused on the problem of improving this like the SMP scheduler. However, I'm much more open to experimenting with other approaches to concurrency in Reia. I would love to see a hybrid STM/actor system and may eventually try to pursue that with Reia.

What resources (books, blog posts, papers, etc.) are you looking at as you design/build Reia?

Tony Other people have been my biggest resource. I'm constantly soliciting feedback on IRC, mailing lists, and elsewhere. Some of the best feedback I've received has come from big names in the Erlang community like Joe Armstrong, Ulf Wiger (@uwiger), and Robert Virding (@rvirding).

This is a particulary interesting set of questions I went through and tried to make sure I had decent answers for. I've thought about trying to put together a blog post answering some of the questions from the lightweight languages list.

I've read Open Reusable Object Models as well, although I'm not sure I can really fulfill its requirements in Reia.

The The Definitive ANTLR Referencefrom the Pragmatic Programmers was what got me started with language development, although I no longer use ANTLR.

If someone wants to get involved with Reia development, where should they start?

Tony Signing up for the Google group and asking that same question would be a great place to start.

Three areas where contribution from outside developers would be most welcome are: testing, built-in types, and the standard library. All of these are largely self-hosted in Reia. Just writing simple tests for presently untested parts of the language would be immensely helpful. In addition, the built-in types still lack a lot of the functionality present in Ruby and Python. Dive in, look for a feature from your favorite language which seems to be missing, and add it.

Most people don't try to develop 'the one true language'. What are your goals with Reia? How will you know if you've succeeded?

Tony Reia is certainly intended to be a niche language. If there's one area I'd like to see it succeed in it would be web application development, particularly for cloud computing applications. While I think Erlang's process model should make it immensely successful in the area of rendering web pages glued together from a variety of backend services (e.g. memcache, db, search index, queues) Erlang has struggled to succeed in this area. A number of frameworks are available but none of them have gained widespread adoption. I don't think anyone sees Erlang as becoming a language recognized for web development any time in the near future.

If a web framework for Reia were to become as popular as Rails I'd certainly regard that as a success :)

Click here to Tweet this article

Monday, December 29, 2008

Rails and Merb -- Better Together?

After my quick interview with Yehuda and Kevin the other day. I wanted to post a couple more points of view. David Heinemeier Hansson (@d2h) and Jeremy McAnally (@jeremymcanally) were good enough to weigh in as well.


What kinds of benefits do you think the Rails and Merb communities will see from this merger?

DHH We get to join the best ideas into Rails and make those ideas available to a much larger audience. We also get a bigger, better team for pushing Rails forward.

Jeremy I think this can only be good for the two communities. Bringing Merb's sensibilities to a large, mature codebase will not only open up a lot for the framework technically, but I honestly think we'll see a lot of innovation and, dare I say, *synergy* between the two cultures. There's been a lot of squawking about monoculture this or competition that, but honestly I think we'll see a lot more come out of working together rather than arguing with one another.

I have my own apprehensions about adding someone to the core team who isn't actually *using* Rails every day. One of the biggest wins for Rails was the fact that was and still largely is extracted from working code changes, additions, and extensions from working production code. Adding people to the team who aren't "practitioners" will create a slightly different dynamic I think, but I'm not sure if it'll end up being a good or a bad thing. I trust Yehuda is a very competent Ruby programmer, so I don't doubt that the code will be good, I just think the perspective may not be the same.

The competition for the top spot in the Ruby Web Framework space has been good for everyone. What's going to happen now that the two big fish represent one, even bigger, fish?

Jeremy As for non-Rails web frameworks, I don't think it means much. Frameworks such as Sinatra, Ramaze, Mack, and so on all have something fundamentally different to offer technically, whereas Merb and Rails were very technically similar in their goals. This similarity is why it makes sense to merge Merb and Rails, but it's also why it won't affect the other frameworks.

DHH You should look as competition being much larger than just the Ruby sphere. There are a TON of choices in this world to do web-based software. Everything from .NET to J2EE to PHP. In that picture, Rails is still a comparably small fish.

Where else in Rubyspace do you think this kind of merger would be possible and helpful?

Jeremy I'd like to see the ORM's become a little more singular. I'm hoping something like Active Relation or (a much simpler version of) Sequel or Ambition will create a Rack-like interface for ORM's to hook into so we can have different DSL's on top of a core that's super fast and tight.

I'd also like to see a merge in the realm of these extension libraries. There's Active Support, facets, extlib, this, that, etc. etc. that all do basically the same thing on one scale or another. Ideally we'd have some sort of core library that lazy loads/installs each piece or something so you don't have to have a gigantically huge library but still get the same functionality. Having a ton of hands working on these libraries will speed them up and make them more efficient.

Thursday, December 04, 2008

Benchmarking and Refactoring

This blog post is pulled from the Profiling and Optimizing Ruby tutorial I wrote for IBM DeveloperWorks a couple of years ago. If you'd like to reuse it, please note that the initial publication at DeveloperWorks must be attributed. I've reposted it here so that people can access it without signing up for a DeveloperWorks account.

Another section of this tutorial, Benchmarking Makes it Better was posted yesterday, it might be worth reading first if you don't have a good handle on benchmarking already.

If you're not comfortable with profiling Ruby Code, you might want to look at some of my other articles on Profiling:

The original tutorial used the following "shell_grabber.rb" script as example code:


file = "/etc/passwd"
File.open(file).each do |line|
  if line.match('/bin/bash') then
    print line.split(':')[0]
    puts " uses bash"
  end
end

Following the profiler

Now that you've seen profiling and benchmarking at work, it's time to put it all together. Back in listing 5 () you saw the profiling results of profiling our shell_grabber.rb script. The first five lines provide the biggest opportunities for optimization, so I've repeated them in


  %   cumulative   self              self     total
 time   seconds   seconds    calls  ms/call  ms/call  name
 42.55     0.20      0.20        1   200.00   470.00  IO#each
 21.28     0.30      0.10      690     0.14     0.20  Kernel.puts
 10.64     0.35      0.05     2070     0.02     0.02  IO#write
  8.51     0.39      0.04      690     0.06     0.07  Kernel.print
  6.38     0.42      0.03     1242     0.02     0.04  String#match

We've got two different places to try to improve our code; the use of a print and a puts in the same block (lines 4 and 5 in listing 12) which take up .69 seconds and 1380 total calls in our profiling run, and the construction of our match method which we call 1242 times (using .42 seconds). If we can clean either of these up, it will represent a win. If we can get both — so much the better.

Since these are two separate refactorings, we'll walk through them individually. Both follow the same pattern though:

  • Isolate the code you want to change
  • Benchmark your options
  • Make the change (if appropriate)
  • Test your new version
  • Check in your changes
In this tutorial, you actually get to cheat a little bit. Since this example only has one method, there's only a very small isolation step to worry about.

Cleaning up your match method

One way to explain the pain you're feeling here is that line.match('/bin/bash') is being repeated on each iteration. Ruby allows you to build a Regex object once then refer to it multiple times later. Listing 13 shows a benchmarking script which tests the impact of building our Regex prior to instead of during our loop. Here's what the benchmarking code would look like:


require 'benchmark'

n = 1000
file = "fixture"

Benchmark.bmbm(25) do |x|

x.report("build regex in block") do
    for i in 1..n do
      File.open(file).each do |line|
        if line.match('/bin/bash') then
          $stderr.print line.split(':')[0]
          $stderr.puts " uses /bin/bash"
        end
      end
    end
  end

  x.report("build regex prior") do
    for i in 1..n do
      re = /\/bin\/bash/
      File.open(file).each do |line|
        if line.match(re) then
          $stderr.print line.split(':')[0]
          $stderr.puts "uses /bin/bash"
        end
      end
    end
  end

end

Here are the results:


$ ./re_bench.rb 2> /dev/null
Rehearsal ------------------------------------------------------------
build regex in block      17.610000   0.820000  18.430000 ( 18.692166)
build regex prior          5.100000   0.340000   5.440000 (  5.465884)
-------------------------------------------------- total: 23.870000sec

                               user     system      total        real
build regex in block      17.550000   0.790000  18.340000 ( 18.421562)
build regex prior          5.170000   0.340000   5.510000 (  5.514125)

There's a pretty obvious win (over a 70% reduction in running time) by moving the algorithm out of the loop. This is a simple change to make:


file = "/etc/passwd"
File.open(file).each do |line|
  re = /\/bin\/bash/
  if line.match(re) then
    print line.split(':')[0]
    puts " uses bash"
  end
end

Having made the change, we need to test it to verify that everything works as planned. The simplest way to test a script this short is to verify the output. In this case we can run the output of the original script and of our new version through diff to ensure that they're the same. diff orig_output new_output exits with a return value of 0, meaning that there's no difference between the output of our two versions. For any real code, you really want to run a real test suite written with Test::Unit or Rspec.

While it's not strictly necessary, we can also look at the real time reductions in our new version. Using time with both versions of the script shows 0m0.064s of real time for the original version and 0m0.046s of real time for the new version — not quite the 70% reduction we saw in the benchmark, but a healthy change nonetheless. (Don't worry too much about not seeing the full improvement, time results can have hidden impacts from all manner of system activity — see also my blog post Benchmarking, Lies, and Statistics.)

Once you've run your tests and timed the results again, you should check it into your source control system so that you can revert if you later find a better way to do things.

Another Approach

It turns out that there's another way to improve the performance of your regex handling. Using the match method turns out to be slower than the using =~, but don't take my word for it. Let's benchmark it. A good benchmarking script is here:


require 'benchmark'

n = 1000
file = "fixture"


Benchmark.bmbm(25) do |x|

  x.report("build regex in block") do
    for i in 1..n do
      File.open(file).each do |line|
        if line.match('/bin/bash') then
          $stderr.print line.split(':')[0]
          $stderr.puts " uses /bin/bash"
        end
      end
    end
   end
  
   x.report("build regex prior") do
      for i in 1..n do
        re = %r{/bin/bash}
        File.open(file).each do |line|
          if line.match(re) then
            $stderr.print #{line.split(':')[0]
            $stderr.puts "uses /bin/bash"
          end
        end
      end
    end
  
    x.report("use =~ pattern") do
      for i in 1..n do
        File.open(file).each do |line|
          if line =~ /\/bin\/bash/ then
            $stderr.print #{line.split(':')[0]
            $stderr.puts "uses /bin/bash"
          end
      end
    end
  end
end

And the results are:


Rehearsal ------------------------------------------------------------
build regex in block      17.480000   0.470000  17.950000 ( 17.997961)
build regex prior          4.980000   0.330000   5.310000 (  5.322441)
use =~ pattern             4.230000   0.300000   4.530000 (  4.629864)
-------------------------------------------------- total: 27.790000sec

                               user     system      total        real
build regex in block      17.590000   0.480000  18.070000 ( 18.620850)
build regex prior          5.090000   0.310000   5.400000 (  5.498786)
use =~ pattern             4.300000   0.310000   4.610000 (  4.652404)

Our first change represents a 70% improvement over the original, but our new change is a 75% improvement according to the benchmark — a change worth making. Since you're keeping everything in some kind of source control, your first step will be to revert your last change. Then you can change the script to match this:


file = "fixture"
File.open(file).each do |line|
  if line =~ /\/bin\/bash/ then
    print line.split(':')[0]
    puts " uses bash"
  end
end

Once the changes have been made, you can go back and verify that the scripts generate the same output using diff as shown previously. Once they pass, you can check your new code in and move on.

Printing Once

We have two issues to consider with the way we're printing lines in our script. First, we we've got two distinct printing calls. Second we're using puts which adds the overhead of adding a \n to every line that doesn't already have one. Before we start making changes, let's do some benchmarking. Here's a banchmarking script:


require 'benchmark'

n = 50_000

Benchmark.bmbm(15) do |x|

  x.report("original version") do
    for i in 1..n do
      $stderr.print i, "and"
      $stderr.puts i
    end
  end

  x.report("single puts") do
    for i in 1..n do
     $stderr.puts "#{i} and #{i}"
    end
  end

  x.report("single print") do
    for i in 1..n do
     $stderr.print "#{i} and #{i}\n"
    end
  end

end

And here are the results:


$ ./print_benchmark.rb 2> /dev/null
Rehearsal ----------------------------------------------------
original version   0.370000   0.050000   0.420000 (  0.443343)
single puts        0.210000   0.020000   0.230000 (  0.240232)
single print       0.170000   0.020000   0.190000 (  0.181233)
------------------------------------------- total: 0.840000sec

                       user     system      total        real
original version   0.230000   0.030000   0.260000 (  0.272866)
single puts        0.220000   0.020000   0.240000 (  0.239319)
single print       0.170000   0.010000   0.180000 (  0.180624)
$

Both single printing call versions show an improvement over the original two printing calls version of the code, but the print version is significantly better than the puts version (a 33% improvement versus a 12% improvement). That makes your decision easy, the new version of the script (using a single print method) is shown below:


file = "fixture"
File.open(file).each do |line|
  if line =~ /\/bin\/bash/ then
    print "#{line.split(':')[0]} uses bash\n"
  end
end

Again, run your tests so that you know you've got a good version. time let's us see just how well our optimization worked. In this case, we got 0m0.046s for the previous version and 0m0.026s for our new version — a 40% speedup.

Refactoring Roundup

In the course of this post, we've made a reverted one change, and made two others. Our code is a bit more than 10% smaller, and runs much faster. Although you don't have unit tests, you've been able to verify your output using a functional test (and thus verified your code quality) at every step of the way, so you also know that you've not introduced any new errors.

While the script that you've been working on is small, you'll follow the same methodology on bigger programs.

First, make sure that your spending your time wisely. Going through a lot of work to speed up a script that takes under a second to run is probably not worth your time for a command line tool, but if you're building a program that will be part of a heavily used website even nano-seconds may count. (You'll need to make sure you've got enough iterations to be able to measure differences above the nano-second scale though, since any variation that small is liable to be caused by outside factors.) time and profile are the tools you'll rely on here.

Second, isolate the code you want to change. In our case, the isolation was pretty simple, but when you're working with an object with multiple public and private methods it will be a bit more difficult. There aren't really any tools to help with this, it all comes down to reading the profiler output and knowing your code.

Third, benchmark your options. Once you've identified and isolated the code your going to change, you can move it (perhaps with small changes) into a benchmarking script. Once you have benchmarking output you can make informed decisions about how to change your code.

Fourth, make the changes indicated by profiling and benchmarking. Now that you know what works the best, you can transplant that back into your actual code.

Fifth, test your new code. Always test your code. Test your code before every commit. Unit tests make this better and easier for almost all of your code, but be ready to roll your own tests in the odd cases that it doesn't.

Sixth, check in your changes. Revision control is incredibly important, it will save you at some point.

Wednesday, December 03, 2008

Benchmarking Makes it Better

This blog post is pulled from the Profiling and Optimizing Ruby tutorial I wrote for IBM DeveloperWorks a couple of years ago. If you'd like to reuse it, please note that the initial publication at DeveloperWorks must be attributed. I've reposted it here so that people can access it without signing up for a DeveloperWorks account.

The original tutorial used the following "shell_grabber.rb" script as example code:


file = "/etc/passwd"
File.open(file).each do |line|
  if line.match('/bin/bash') then
    print line.split(':')[0]
    puts " uses bash"
  end
end

A Benchmarking Primer

Before you can start replacing code, you should have an idea about how your intended replacement performs. The benchmark library is the tool you'll want to use for this. It's simple to use, you'll just write a benchmarking script that includes the methods you want to compare. The Benchmark class provides three instance methods to make this easier; bm, bmbm, and measure.

The bm method provides basic interface to the benchmark method. It takes an optional label_width argument. Each method tested can have a label specified. You define individual methods to be tested as shown here:


require 'benchmark'
n = 5_000_000
Benchmark.bm(15) do |x|
  x.report("for loop:")   { for i in 1..n; a = "1"; end }
  x.report("times:")      { n.times do   ; a = "1"; end }
  x.report("upto:")       { 1.upto(n) do ; a = "1"; end }
end

The bmbm method runs your benchmarking code twice, to help avoid complications from garbage collection. The first run is called the rehearsal, and is reported as part of the results. bmbm methods are defined in the same way that bm methods are. The only thing you'd need to change from listing 6 to use bmbm instead of bm methods is the call itself. To use the bmbm method, you'd write Benchmark.bmbm(15) do |x|.

Simple timing information can also be gathered using the measure method. (measure is used behind the scenes by the bm and bmbm methods to create their reports.) measure returns a Benchmark::Tms object that can be converted to a string for printing. A Ruby script using Benchmark#measure to time the construction of a string is shown below:


require 'benchmark'
puts Benchmark.measure { "foo" << "bar" << "baz" }

Comparing Code With benchmark.rb

Now that you've seen the basics of benchmarking, let's put it to use. We've already used time and profile.rb to look at the performance of our shell_grabber.rb script. Just pull out the part that does the work and put it into a new script like this.


require 'benchmark'
Benchmark.bm(25) do |x|
  x.report("original code") { 
    File.open("/etc/passwd").each do |line|
      if line.match('/bin/bash') then
        $stderr.print line.split(':')[0]
        $stderr.puts " uses /bin/bash"
      end
    end
  }
end

There's a change to the original code that are worth discussing. Instead of using bare print and puts statements, they're replaced with $stderr.print and $stderr.puts. You don't want to clutter the screen with hundreds or thousands of lines of output. This has the potential to dramaticly affect the timing of your code, so if you make a change like this to one method in your benchmark you'll want to make the same change to all of them. Take a look at just how much of a difference doing a $stdout.puts instead of a puts can make:


$stdout.puts  0.320000   0.040000   0.360000 (  0.396455)
naked puts    0.200000   0.020000   0.220000 (  0.262174)

Using Benchmarking Results

Using benchmarking results will help you choose which algorithm or which idiom will give you the best results. It's important that you don't throw off your results with improper tests though. For example, the benchmarking script shown above with the for, upto, and times methods only measured the performance of different forms of iteration. In this case it really doesn't matter what we do inside the iteration, as long as it's the same for each version. The output from that code modified to use Benchmark#bmbm is shown here:


Rehearsal --------------------------------------------------
for:             2.210000   0.030000   2.240000 (  2.288663)
times:           2.570000   0.060000   2.630000 (  2.623878)
upto:            2.530000   0.050000   2.580000 (  2.650115)
----------------------------------------- total: 7.450000sec

                     user     system      total        real
for:             2.080000   0.050000   2.130000 (  2.148350)
times:           2.590000   0.010000   2.600000 (  2.743801)
upto:            2.550000   0.030000   2.580000 (  2.588629)

Reading the output, you can see that there's a small but noticeable difference between the upto and times constructions, but that the real win comes with the for version — in this case, nearly a 20% improvement.

Notes

The importance of large datasets and/or multiple benchmarking runs was underscored while I was writing this section. In my initial version of the script, I only iterated through the loop 50,000 times. Then, when I ran the results the first time, the upto version of the iterator ran the fastest. When I moved up to 5,000,000 times through the loop and ran it a couple more times, I ended up hitting a consistent result of the for loop being better.

Dropping the number of iterations down to 5,000 continued to yield the best results for the for loop version in the non-rehearsal run, but would occasionally show the upto version as faster. Dropping down to 500 iterations showed near random results. In these cases, it appears that the time required for the run was so small (0.000606 seconds in one run) that external processes had an overwhelming influence on the benchmark.

Another dirty secret of benchmarking is that you'll need to do some extra work to ensure that your results are statistically valid. Please see my blog post Benchmarking, Lies, and Statistics for more details about this.

Finally, tune in tomorrow for a follow-up post on Benchmarking and Refactoring.

Tuesday, December 02, 2008

Tuesday Ruby Tab Sweep (2 Dec 2008)

As I rode in on the bus this morning, I found that I'd popped up a number of tabs about things Ruby. Here are the ones most worth sharing with some short notes:

What are you reading about today?

Monday, December 01, 2008

Ruby Refactoring Workbook Interview

I've recently heard that Bill Wake and Kevin Rutherford are working on a Ruby version of his excellent Refactoring Workbook. Bill and Kevin were kind enough to spend a little time with me talking about their upcoming book.


First there was Design Patterns in Ruby, then a Ruby Edition of Refactoring, and now your reworking of Ruby Refactoring. What makes Ruby different enough to warrant these new editions?

Kevin A few things, I think.

Firstly, Fowler's original Refactoring book — which I hold in very high esteem — is predicated on the GoF design patterns, and on the constraints imposed by a statically typed language. Ruby's dynamic aspects — notably duck typing and open classes — mean that patterns such as Strategy, say, can be expressed differently, and the forces for applying those patterns will also subtly alter. That, in turn, means that some of the "moves" in Refactoring may need to be revised for Ruby. And in some cases, it means we need completely new refactorings.

Second, Ruby is now over ten years old, and during that time there's been little attention paid to code quality, maintainability and "habitability". Yet there must now be a growing body of "legacy" Ruby code out there. I think that's why we're seeing a rash of new code analysis tools for Ruby right now: the time is right.

Finally, Rails has lowered the bar for entry into the world of web services development, and brought Ruby programming to a more diverse audience. So there's a strong need for good books about coding practices, to support the Ruby-powered development newbie.

Bill Ruby's dynamic typing means that you have to adjust the mechanics of refactorings (and sometimes lean on the tests more). But for me, the driver was more the community than the language. It's a way for me to contribute, focusing on something like refactoring in a way that a reader familiar with Ruby can focus on the idea at hand, not constantly also having to translate from Java et al.

You mentioned that Ruby needs some completely new refactorings. Can you describe one of these?

Kevin Your best source for these is Jay Fields and Shane Harvie'sRefactoring: Ruby Edition, which is a re-working of Martin Fowler's original Refactoring. One of the new refactorings they introduce is "Replace Loop With Collection Closure Method". (Their book is available as a Safari rough-cut:.)

I'm also intrigued by your call for good books on coding practices for Ruby-powered newbies. Have you seen Gregory Brown's Ruby Best Practices? Are there other books you'd like to see written (or translated into Ruby)?

Kevin Yes, I've seen RBP; I think the time seems to be right — the Ruby community seems ready to step up to the next level of maturity in terms of "professionalism" in software development.

As for other moves, top of my list would be a Ruby-specific version of Michael Feathers' brilliant Working Effectively with Legacy Code.

What did you learn about refactoring as you started to apply the ideas in the Workbook to Ruby?

Bill I've become much less focused on the mechanics and more focused on the philosophy of small safe steps. Writing this book gave me the incentive to go back through and try to discern the overall patterns that refactorings use, as well as the small internal steps that have varying forms.

Kevin Lots! Coming from a C/C++/Java background back in the mists of time, I hadn't really appreciated just how much the GoF patterns and the Refactoring moves depend on the forces that arise from static typing. But when that static typing is removed, some of the concepts work less well.

Here's an interesting example: Fowler introduces the Feature Envy smell, which tells you to move a code fragment into the class whose fields it accesses the most. In the C family of languages this works well, because ints and strings can't acquire new methods: they act as a brake on the process. But in Ruby, you need a lot more static analysis in order to discern whether a particular object could become the new home of your code. The smell, and the refactorings that fix it, are easier to express and apply in a static language.

Another: Modules offer the Ruby programmer the opportunity to simulate a kind of multiple inheritance, using a mix-in style. Java doesn't have that, and so there's a whole set of possibilities open to us that Martin Fowler didn't have.

And from the Gof patterns: The shape of the Strategy pattern (someone once said that everything boils down to Strategy in the end) includes an abstract class/interface representing the common "type" of the actual strategies. It isn't needed in Ruby, due to duck typing.

All this meant that we spent a large portion of our time checking whether the patterns, code smells and refactorings still applied in Ruby, and what might be different or unnecessary.

What did you learn about Ruby as you applied the ideas?

Kevin Less, I think. I've been a semi-serious Rubyist since 2004, and during that time I had done a lot of refactoring. So I was used to working in Ruby as a malleable medium, even though I'm sure there are corners of ruby-fu I've never seen. We have also deliberately avoided delving too far into the details of any particular Ruby library — this is mostly a book about technique, rather than knowledge.

My principal concern about the new workbook is whether we have noticed all of the places where GoF, Refactoring and the original workbook depend subtly on static typing or other aspects of the Java experience. Which is why we've recruited an awesome band of reviewers to help us weed out those hidden assumptions.

Bill Two things hit me. The first is that I still seem to fight too many "environment" battles, trying to get tools and dependencies just right. But the second is that once I'm in the program and using Ruby, the language feels very expressive, with just the right thing ready-to-hand.

Friday, November 28, 2008

Three Hours to Ruby

Hey, lazyweb, I need your help.

I'm just about done teaching a series of classes on Bash scripting for the systems admins where I work (mostly junior types). I present a one hour course every other week, focusing on the basics that they'll be able put to use right away.

Pretty soon, I'm going to start a series of similar courses about Ruby. Here's the rub though, I'm not sure where to start. If you had three or four one hour blocks to get a group of junior to mid-level sys admins going on Ruby, what would you focus on?

Thursday, November 27, 2008

Ruby Best Practices: mini-Interview 1

In case you hadn't heard, O'Reilly has recently released Ruby Best Practices, by Gregory Brown, as a rough cut. This is one of those book that the Ruby community has been waiting for, even if we didn't know we were waiting for it. Not only is Gregory a great person to write a book like this, he's the kind of person who's going to make sure it does the most good for the community as a whole (read below and you'll see what I mean). Right now, we have the opportunity to grab a rough cut and help make it better, it seems like the least that we can do.

This is the first in a series of short interviews I'll be doing with Gregory as the book progresses. Please feel free to drop a question or two in the comments, They might make it into the next interview.


Let's start by talking a bit about code and community. You've been a GSoC student and mentor, you were the Ruby Mendicant, and now you're writing Ruby Best Practices for O'Reilly. Tell us a bit about this journey and what you've learned along the way.

I can sum it up very briefly, I'm simply doing my part to pay it forward. I've had wonderful mentors and teachers along the path I took to get me where I am today. Each one of these folks has never asked me for anything in return, but I'd feel far too selfish if I just kept their knowledge for my own personal gain. So GSoC, Ruby Mendicant, and RBP are all ways for me to turn whatever good things I've gained from the people who have helped me over the years into something I can pass forward onto others to enjoy.

I've learned so much over the last few years that it's very difficult to summarize. If you want to know about technical details, I'd say: Writing reporting software is harder than I thought (GSoC), writing a PDF generator is harder than I thought (Mendicant), and writing a book is harder than I thought (RBP). So these things lead me to believe that a strong ability to underestimate complexity combined with an obsessive compulsion with tackling certain problems can turn into a virtue in the right circumstances.

One of the things that I really like about your plans for the book is that it will eventually become a Creative Commons resource to the Ruby community. Why did you decide to do this, and how did you caonvince O'Reilly to go along with it?

I don't see how anyone could write a book that claims to curate "Best Practices" without making it as accessible to the community as possible. Rather than writing this book with the intention of having people think "Hmm, Greg is pretty smart, I will trust what he says", I wanted to foster a healthy level of skepticism so that the work can grow beyond its original content. What might be considered a best practice now won't necessarily be that way a few years from now, and getting the book under a Creative Commons license will help facilitate the necessary changes and make it easier for people to openly question and improve the work.

Because we chose the by-nc-sa license variant, O'Reilly was pretty easy to convince. My editor (Mike Loukides) was more than happy to go down that road. It felt as if there would be some tension if I removed the non-commercial restriction, but this is where you get into the murky territory of whether that removes an incentive for the publisher to support the work, and that's a whole other can of worms.

I'm just happy that 9 months after the book is on the shelves, if you want to print a few copies of it to share among folks you work with, or if you want to translate the book and put it on the web so more people can read it, or if you want to quote a large section of it so that you can pick me apart on your blaag, you can. This will make the book better, and prevent it from losing value over time.

Of course, if during the Rough Cuts period or first 9 months of the book sales people really get behind it and buy copies, that'll make me happy. Not only will I benefit directly from purchases, it'll help encourage O'Reilly to do more of this in the future. But no matter how good or bad it sells, it will certainly have more value once it goes out of print than most books under traditional licensing schemes do.

As part of your own Ruby best practices, I'm sure that you've built a set of tools that you rely on. Can you name a handful of them and why you think they're so important?

Using the right tools for the job definitely makes a difference, but I tend to be a bit of a minimalist. I get by fine with extensive use of rake, irb, and a decent text editor (TextMate on OS X, vim elsewhere). Having nearly everything in revision control under either svn or git means that I've got a large library of my own code (often complete with tests/specs) to refer to which helps me remember how to solve particular problems without having to refer to books. Of course, having written a lot of mailing list posts, API documentation, O'Reilly OnLAMP articles, and the Ruport Book, I've got a lot of explanations in my own words reminding me how to do the things I need to as well, and I tend to use these resources extensively.

Also, when my tools come up short, I can always stand to learn more from some of the smartest folks in our community. Since I'm not bashful about asking for help, my two most valuable tools in software development are email and my IRC client.

This answer may be a little boring, because I'm not one to hunt down the next latest and greatest tool to fix a problem I never knew I had. A simple work flow that involves doing some custom scripting when necessary seems to work best for me.

I've been going back through Code Reading by Diomidis Spinellis recently. I'm really sold on the idea that reading great code can us write better code. What are some code bases you'd recommend readin and what can we learn from them?

I've not yet picked up that book, but I imagine I should. Reading code is a great way to learn, and this is exactly what folks will be doing when they read Ruby Best Practices. I'm picking from my favorite bits of real code, some of them are my own, but many are not. For the functional programming chapter I'm writing right now, I had a fun read through MenTaLguY's lazy.rb and picked up some really interesting ideas for writing transparent proxy objects. I've also been digging through JEG2's Higher Order Ruby blog series and learning a ton.

Because Prawn was written concurrently with my initial work on Ruby Best Practices, and will continue to be a source for examples and case studies throughout the book, I recommend folks to check it out. PDF is a low level domain and that means there is some necessary complexity, and because things are moving fast they get rough around the edges at times. However, many parts of Prawn represent my best effort at writing masterful Ruby code, and I think people might pick up some tricks by reading through its source.

But really, people don't need to be so picky about what they read. Look through the source of every gem you've got installed if you have the time to! Try to think about why people wrote their code the way they did, and pick up some of the stuff you think you can groove with and try it in your own code. Sometimes, the coolness is lost in translation, other times, it's exactly the sort of inspiration you need.

If you want to get stronger at Ruby, don't just read API documentation, read the source of the code you use. You'd be surprised how much cool stuff there is out there.