See You At The Hackfest!

By Leah Silber | March 10th, 2010 at 10:03AM

A quick reminder to those of you attending MountainWest RubyConf this weekend: Engine Yard will be hosting it’s third annual MountainWest RubyConf Hackfest this Thursday night!

We’ll be back at the Hilton Salt Lake City Center, in the spacious and comfy Vice Presidential Suite. We’ll have food, drinks, wireless access, and of course, code, code and more code. If you’ve been to a previous MountainWest Hackfest you know that this is the not-to-miss Hacking event of the year, so be sure to stop in!

Members of the Engine Yard crew will be at the show all day, and hacking in the suite all night, so if you’ve got any questions, find one of us on-site Thursday, or shoot us an email. We’ll see you all there!

Share this post:
  • email
  • Digg
  • del.icio.us
  • Reddit
  • Slashdot
  • StumbleUpon
  • Technorati
  • Twitter
  • Google Bookmarks
  • Facebook
  • LinkedIn
Popularity: 4% |
Rate this post: 1 Star2 Stars3 Stars4 Stars5 Stars
Loading ... Loading ...

Making Ruby Fast: The Rubinius JIT

By Evan Phoenix | March 9th, 2010 at 3:03PM

In order to execute Ruby code as fast as possible, Rubinius has the ability to compile Ruby code all the way down to machine code when it detects that a method is heavily used. In Rubinius, the system that manages this process is its JIT.

In today’s post, I’ll be giving an overview of the various players involved in the path that code takes to get from source to machine code. Without further ado, I’ll jump right in.

Melbourne Parser

This is the first step. The parser takes Ruby source code as input and calls for each element to create an internal representation of the code: the AST. (lib/ext/melbourne)

Compiler

The compiler takes the AST that the parser created and analyzes it, creating bytecode in the form of a CompiledMethod and InstructionSequence. (lib/compiler)

Bytecode

A CompiledMethod object contains an InstructionSequence object, which is the raw bytecode which will perform the semantic actions of the Ruby source code. (lib/compiler/iseq.rb)

Virtual Machine

The VM itself then executes the bytecode using a simple interpreter. A key data structure used in this evaluation is the VMMethod, which is an internal mirror of a CompiledMethod, but translated into constructs that are easier to interpret. As the VM interprets a VMMethod, it uses InlineCache objects to speed up method dispatch. In addition, these InlineCache objects remember profiling information about what methods they have seen. This information is later used by the JIT.

The VM also increments a call counter on the VMMethod at a few critical points (on start and on backward branch). This call counter is what controls when the JIT kicks in. When the call counter reaches some predetermined value (controlled via -Xjit.call_til_compile), the first stage of the JIT kicks in.

Method Chooser

Now that a call counter has reached the proper level, the JIT is ready to kick in. The JIT could simply take the method whose counter has hit the level, but instead it starts the search. It’s looking for a good method to JIT, which it finds by looking up the call stack.

The reason it does this is because the JIT has the ability to inline methods into methods that call them. We’re exploiting the fact that the call stack shows not just one method heating up, but a whole chain of them (vm/llvm/jit:compile_callframe). So we walk up the call stack, looking at each method along the way, and asking ourselves: could this method be inlined into the one that called it? If the answer is yes, we move to the next method. By doing this, we’re able to inline methods along hot paths in code, which yields better speeds.

Compiler Thread

Now that we’ve picked a good method at which to start the JIT process, the method is placed into a queue. This queue is then automatically emptied by another native thread, which is always running. It’s in this background thread that the rest of the JIT process takes place.

Using a background thread means that the Ruby code is free to continue to run while the JIT runs in the background. This means that the JIT imposes virtually no slowdown, because it never stands in the way of running Ruby code (vm/llvm/jit:compile_soon).

The JIT thread pops an entry off the queue and begins compiling it. Because the JIT uses LLVM to perform low level optimizations and machine code generation, we need to translate the method into a structure that LLVM understands. This structure is the LLVM IR.

To convert the method, we walk through the bytecode and call methods on a JITVisit object (one for each kind of bytecode). The JITVisit class uses LLVM’s IRBuilder class to build a big tree data structure that represents the actions that should be taken.

A simple example is that a goto bytecode instruction is translated into a Branch object and inserted into the IR. For most bytecode, the process is fairly straight forward, there being a simple set of IR objects to generate per bytecode.

Method Inlining

The most complicated bytecodes to generate IR for are the send instructions. This is because it’s at these points that we have the opportunity to inline a method. When a method is inlined, the code to perform the method is inserted where the send instruction would normally be. This eliminates any calling overhead and allows LLVM to optimize more.

At this stage, control is handled to an Inliner object. The Inliner will only inline a method if it can see that the method was the the primary method called at a particular send instruction. Because Ruby is a dynamic language, any method call can always invoke a brand new method. But in reality, that happens rarely. Instead, most method calls always end up calling the same method over and over again. The profiling information that the InlineCaches have been gathering allows us to see that this is the case and perform inlining.

One constraint that the Inliner has is that it needs to avoid over-inlining. Over-inlining causes the generated function to become extremely large and slower than it would be if there were no inlining. To do this, the Inliner keeps track of the cost of a inlining. For every method that is inlined, the cost increases. When the cost reaches a threshold, no more inlining takes place.

Now that the Inliner has decided to go ahead and inline the method, it must insert a guard before the inlined code. This guard makes sure that the object is still of the type seen in the profiling information, and that therefore the inlined method code is the proper code to run. The generated IR for this looks something like:

if(obj->class == profiled_class) {
 result = inlined_code_for_method_name;
} else {
 result = obj->send method_name, …;
}
This allows Ruby code to continue to be dynamic, but exploits those points in the code where the dispatch is actually static.

After the JITVisit class has finished, control is handed off to optimize and generates machine code.

LLVM Optimization

Up to now, we’ve simply been constructing information to feed to LLVM. Now we actually hand over the IR to LLVM. The first thing LLVM does is run a number of optimization passes over the IR. This cleans up the IR and makes it quite a bit more efficient. At this stage, the IR can be reduced in size by five to ten times by remove redundancies and reordering.

LLVM Code Generation

Finally, the optimized IR is run through LLVMs code generator. This code generator is fairly complex, but its API is extremely simple. When the generator finishes, it returns a function pointer that can be called to execute the code. We put this function pointer into a special slot on the original CompiledMethod.

By default, this slot in the object holds a pointer to the function that implements the interpreter. By swapping them, any future calling of the method automatically uses the new JIT’d version.

Deoptimization

Because Ruby is so dynamic, there are cases where JIT’d code must be discarded. The primary example is where a method that was inlined is redefined. In this case, the VM keeps a table to know all methods that inlined the method being redefined. It resets those methods back to using the interpreter and tags the JIT’d code to be discarded. Because the method has been reset, it can now be JIT’d again later, incorporating the newly redefined method.

So those are the systems that interaction to make speed up Ruby. The process can achieve speeds up by as much as 10x over non JIT’d code. We’ve only begun to scratch the surface of the techniques we can use to strip even more dynamic aspects of the code away and make it faster. 2010 is going to be an exciting year.

Share this post:
  • email
  • Digg
  • del.icio.us
  • Reddit
  • Slashdot
  • StumbleUpon
  • Technorati
  • Twitter
  • Google Bookmarks
  • Facebook
  • LinkedIn
Popularity: 9% |
Rate this post: 1 Star2 Stars3 Stars4 Stars5 Stars
Loading ... Loading ...

March Madness!

By Leah Silber | March 2nd, 2010 at 10:03AM

Okay, so maybe madness is a strong word, but March is here, and Ruby and Rails events are back in full swing! We’ll be all over the globe talking Ruby, Rails, and for a special treat, Cloud Computing. If you’ll be in any of the areas we’re visiting, be sure to get in touch—we’d love to meet you!

MountainWest RubyConf

March 11-12 | Salt Lake City, UT

We’re back to sponsor MountainWest RubyConf for the third year running, and we couldn’t be happier. It’s a great event, organized by incredible coders, with an all around top-quality and intimate feeling. For the third year running we’ll also be hosting our (now annual) MountainWest RubyConf Hackfest! Food, drinks and code in a huge and comfy open-all-night suite at the Hilton. Surround yourself with top Rubyists and hack on your favorite projects well into the wee hours of the morning; what more can you ask for?

Last but not least, be sure to catch Yehuda Katz’s talk on writing modular code with Rails 3; this close to the release date, it’s sure to put you on the cutting edge.

South by Southwest

March 12-16 | Austin, TX

Yehuda will be hopping straight from MountainWest RubyConf to South by Southwest to represent us all at a ‘Web Frameworks Battle Royale.’ By battle, they mean panel, but still; if you’re at SXSW, be sure to drop in and show your support for Ruby and Rails!

CloudConnect

March 15-18 | Santa Clara, CA

Ezra Zygmuntowicz will be at CloudConnect, talking about what else? Cloud deployment. He’ll be joined by some Engine Yard customers, so do take advantage of the opportunity to get the real scoop!

RubyConf India

March 20-21 | Bangalore, India

This is the inaugural RubyConf India, and we’re glad to be participating! Nick Sieger will be on hand, talking about Rails 3—and JRuby, of course. Ruby luminaries like Chad Fowler, Ola Bini and the one and only Yukihiro Matsumoto will be in attendance; a serious roster for a first time show—congrats, guys!

Scottish Ruby Conference

March 26-27 | Edinburgh, Scotland

Formerly known as Scotland on Rails, the Scottish Ruby Conference is yet another annual calendar fixture. It’s organized by a great group of guys (here’s to you, Paul, Alan and Graham!), in an even greater city. If you’re extending a conference trip, this is the one; you definitely want to take a few days to explore the city!

New Rails Core Team member Carl Lerche will be there, talking about getting the most out of ActiveRecord 3 with Arel; be sure to check it out.

Ruby User Groups

Last but not least, Randall Thomas will be hitting up a few cities this month, talking about Machine Learning and demoing Engine Yard Cloud:

That’s it for March; hope to see you all out there!

Share this post:
  • email
  • Digg
  • del.icio.us
  • Reddit
  • Slashdot
  • StumbleUpon
  • Technorati
  • Twitter
  • Google Bookmarks
  • Facebook
  • LinkedIn
Popularity: 7% |
Rate this post: 1 Star2 Stars3 Stars4 Stars5 Stars
Loading ... Loading ...

No More Monthly Minimums on Engine Yard Cloud!

By Abheek Anand | March 1st, 2010 at 11:03AM

Moore’s law is a wonderful thing.

Users of cloud computing frequently talk about its advantages; those include lower capital expenditures, programmable infrastructure, and the power of agile deployment. One thing that doesn’t get as much attention though, is how the advantages of Moore’s law are significantly magnified by using the cloud. After all, while buying infrastructure might lock you into using the same equipment for the next few years, using a cloud service means that customers get standardized instance sizes, and the infrastructure provider can refresh the underlying hardware a lot faster.

Here at Engine Yard, we’ve been aggressively passing these savings along to our customers. A few weeks ago we dropped instance prices by aggregating a pool of EC2 reserved instances, and taking advantage of Amazon’s just-released consolidated billing feature to get reduced costs for these instances. Last week, we passed through a savings in outbound bandwidth pricing—upto a 20% decrease across all tiers of usage.

Today we’re announcing that we’ll be dropping the monthly minimum for using Engine Yard Cloud. This is a direct response to feedback from users working on projects that haven’t yet deployed to production. We now know that many of you are using Engine Yard Cloud for development and staging environments pre-launch, where costs don’t quite make it to $25. We agree—things should be different, and so starting today that minimum goes away, and we move to being a completely pay-as-you-go platform.

Run your instances only when you need them, and pay only for what you use, regardless of whether you’re a small group of developers building the next big Rails application, or a production environment serving millions of users.

So there you have it: three price decreases in just the last month. Stay tuned for more—there are exciting new features in the pipeline, and we can’t wait to tell you about them!

Share this post:
  • email
  • Digg
  • del.icio.us
  • Reddit
  • Slashdot
  • StumbleUpon
  • Technorati
  • Twitter
  • Google Bookmarks
  • Facebook
  • LinkedIn
Popularity: 7% |
Rate this post: 1 Star2 Stars3 Stars4 Stars5 Stars
Loading ... Loading ...

Memoization and id2ref

By Evan Phoenix | February 26th, 2010 at 10:02AM

This article was originally included in the February issue of the Engine Yard Newsletter. To read more posts like this one, subscribe to the Engine Yard Newsletter.

In this series, Evan Phoenix, Rubinius creator and Ruby expert, presents tips and tricks to help you improve your knowledge of Ruby.


The performance of a library or application is one of the key factors into getting it accepted, so it should come as no surprise that Ruby programmers have many different tricks they use to squeeze more performance out of their code.

One of the most common is memoization. This is the technique of calculating a value once, then saving the result and transparently substituting it for the code that calculated the original value.

Here’s a short example:

def size_of_universe
 @size ||= Universe.find.size
end
Here, we’ve calculated the size of the universe and then saved the result into the @size ivar. This way, the next time size_of_universe is called, the previously calculated value is returned.

We’ve already gone over one of the simplest and most basic techniques, above. This technique uses the ||= operator to run the right hand side if, and only if, the left hand side is not true. It’s short and sweet, rarely confusing the user.

Another technique that has been seen in production code uses ObjectSpace._id2ref. While this is becoming a common technique, it has a number of problems that we’ll look at today.

Here is an example of using this technique:

obj = Universe.find.size
 
eval <<CODE
def size_of_universe
 ObjectSpace._id2ref(#{obj.object_id})
end
CODE
This technique is used frequently with metaprogramming, when you want to embed a specific object directly into a generated method. People use this technique because, at first glance, it removes any kind of data dependency on the generated code and obj. There is no ivar to make sure is in scope, no constant, etc. But, in fact, this technique masks some rather terrible bugs.

This technique basically uses the whole Ruby process as a big table, leveraging the ability to easily get the table index for an object and convert that table index back into the object.

The primary issue stems from the fact that Ruby is a garbage collected language. Even though the code has requested the object_id for an object, that is not enough to keep the object alive. So if the only reference to the return value from #size was obj, when this method returns, obj becomes garbage.

So what happens when you run #size_of_universe and obj has been garbage collected? Well, a few things can happen:

  1. id2ref will raise a RangeError, saying that the id no longer points to an object.
  2. A random object will be returned.

The second scenario is probably the strangest, but this can be observed. This bizarre _id2ref behavior occurs because the return value from #object_id is actually the address in memory of the object itself. This means that when the GC runs and collects the object, and then the allocator puts another object in the same place (which is exactly what an GC does), whatever object happens to be there is returned. This is essentially the same as a hanging pointer bug in C.

Lastly, the implementation of #_id2ref varies wildly between different Ruby implementations, each having different performance and different potential bugs. Due to these factors, using #_id2ref in production is even more nebulous.

So what’s a simple alternative?

UNIVERSE_SIZES = [ ]

idx = UNIVERSE_SIZES.size UNIVERSE_SIZES << Universe.find.size

eval <<-CODE def size_of_universe UNIVERSE_SIZE[#{idx}] end CODE This seems silly if there is just a single value in UNIVERSE_SIZES, but the expectation here is that you might be generating many methods with values that need to memoized. In the example above, we’re storing methods in an Array that is in a constant, which will keep the value alive from a GC standpoint. This avoids the bugs that #_id2ref has.

So hopefully if you need to memoize, you won’t use _id2ref. There are a number of alternatives, most of them are better than worrying about the bugs that #_id2ref can easily introduce.

Share this post:
  • email
  • Digg
  • del.icio.us
  • Reddit
  • Slashdot
  • StumbleUpon
  • Technorati
  • Twitter
  • Google Bookmarks
  • Facebook
  • LinkedIn
Popularity: 8% |
Rate this post: 1 Star2 Stars3 Stars4 Stars5 Stars
Loading ... Loading ...