Archive for 2009

Rails and Merb Merge: Performance (Part 2 of 6)

By Yehuda Katz | December 29th, 2009 at 1:12PM

The next significant improvement that we hoped to bring to Rails from Merb was faster performance. Because Merb came after Rails, we had the luxury of knowing which parts of Rails were used most often and optimizing performance for those parts.

For Rails 3, we wanted to take the performance optimizations in Merb and bring them over to Rails. In this post, I’ll talk about just a few of the performance optimizations we’ve added to Rails 3: reducing general controller overhead and (greatly) speeding up rendering a collection of partials.

For our initial performance work, we focused on a few specific but commonly used parts of Rails:

  • General overhead (the router plus the cost of getting in and out of a controller)
  • render :text
  • render :template
  • render :partial
  • rendering a number (10 and 100) of the same partials in a loop
  • rendering a number (10 and 100) of the same partials via the collection feature

This was definitely a limited evaluation, but it covered most of the cases where performance might be at a premium and the Rails developer was unable to do anything about it.

General Controller Overhead

The first thing was improving the general overhead of a Rails controller. Rails 2.3 doesn’t have any way to test this, because you’re forced to use render :string to send back text to the client, which implicates the render pipeline. Still, we wanted to reduce it as much as possible.

When doing this work, we used Stefan Kaes’ fork of ruby-prof that comes with the CallStackPrinter (the best way I’ve ever seen to visualize profile data from a Ruby application.) We also wrote a number of benchmarks that could double as profile runs if I wanted to zero in and get more precise data.

When we looked at overhead, it was dominated by setting the response. Digging a bit deeper, it turned out that ActionController was setting headers directly, which then needed to be re-parsed before returning the response to get additional information. A good example of this phenomenon was in the Content-Type header, which had two components (the content-type itself and an optional charset). The two components were available on the Response object as getters and setters:

def content_type=(mime_type)
  self.headers["Content-Type"] =
    if mime_type =~ /charset/ || (c = charset).nil?
      mime_type.to_s
    else
      "#{mime_type}; charset=#{c}"
    end
end
 
# Returns the response's content MIME type, or nil if content type has been set.
def content_type
  content_type = String(headers["Content-Type"] || headers["type"]).split(";")[0]
  content_type.blank? ? nil : content_type
end
 
# Set the charset of the Content-Type header. Set to nil to remove it.
# If no content type is set, it defaults to HTML.
def charset=(charset)
  headers["Content-Type"] =
    if charset
      "#{content_type || Mime::HTML}; charset=#{charset}"
    else
      content_type || Mime::HTML.to_s
    end
end
 
def charset
  charset = String(headers["Content-Type"] || headers["type"]).split(";")[1]
  charset.blank? ? nil : charset.strip.split("=")[1]
end

As you can see, the Response object was working directly against the Content-Type header, and parsing out the part of the header as needed. This was especially problematic because as part of preparing the response to be sent back to the client, the Response did additional work on the headers:

def assign_default_content_type_and_charset!
  self.content_type ||= Mime::HTML
  self.charset ||= default_charset unless sending_file?
end

So before sending the response, Rails was once again splitting the Content-Type header over semicolon, and then doing some more String work to put it back together again. And of course, Response#content_type= was used in other parts of Rails, so that it was correctly set based on the template type or via respond_to blocks.

This was not costing hundreds of milliseconds per request, but in applications that are extremely cache-heavy, the overhead cost could be larger than the cost of pulling something out of cache and returning it to the client.

The solution in this case was to store the content type and charset in instance variables in the response, and merge them in a quick, simple operation when preparing the response.

attr_accessor :charset, :content_type
 
def assign_default_content_type_and_charset!
  return if headers[CONTENT_TYPE].present?
 
  @content_type ||= Mime::HTML
  @charset      ||= self.class.default_charset
 
  type = @content_type.to_s.dup
  type < < "; charset=#{@charset}" unless @sending_file
 
  headers[CONTENT_TYPE] = type
end

So now, we’re just looking up instance variables and creating a single String. A number of changes along these lines got overhead down from about 400usec to 100usec. Again, not a huge amount of time, but it could really add up in performance-sensitive applications.

Render Collections of Partials

Rendering collections of partials presented another good opportunity for optimization. And this time, the improvement ranked in milliseconds not microseconds!

First, here was the Rails 2.3 implementation:

def render_partial_collection(options = {}) #:nodoc:
  return nil if options[:collection].blank?
 
  partial = options[:partial]
  spacer = options[:spacer_template] ? render(:partial => options[:spacer_template]) : ''
  local_assigns = options[:locals] ? options[:locals].clone : {}
  as = options[:as]
 
  index = 0
  options[:collection].map do |object|
    _partial_path ||= partial ||
      ActionController::RecordIdentifier.partial_path(object, controller.class.controller_path)
    template = _pick_partial_template(_partial_path)
    local_assigns[template.counter_name] = index
    result = template.render_partial(self, object, local_assigns.dup, as)
    index += 1
    result
  end.join(spacer).html_safe!
end

The important part here is what happened inside the loop, which could occur hundreds of times in a large collection of partials. Here, Merb had a higher performance implementation which we were able to bring over to Rails. This is the Merb implementation.

with = [opts.delete(:with)].flatten
as = (opts.delete(:as) || template.match(%r[(?:.*/)?_([^\./]*)])[1]).to_sym
 
# Ensure that as is in the locals hash even if it isn't passed in here
# so that it's included in the preamble.
locals = opts.merge(:collection_index => -1, :collection_size => with.size, as => opts[as])
template_method, template_location = _template_for(
  template,
  opts.delete(:format) || content_type,
  kontroller,
  template_path,
  locals.keys)
 
# this handles an edge-case where the name of the partial is _foo.* and your opts
# have :foo as a key.
named_local = opts.key?(as)
 
sent_template = with.map do |temp|
  locals[as] = temp unless named_local
 
  if template_method && self.respond_to?(template_method)
    locals[:collection_index] += 1
    send(template_method, locals)
  else
    raise TemplateNotFound, "Could not find template at #{template_location}.*"
  end
end.join
 
sent_template

Now this wasn't perfect by a long shot. There was a lot going on here (and I'd personally like to have seen the method refactored). But the interesting part is what happened inside the loop (starting from sent_template = with.map). Unlike ActionView, which figured out the name of the template, got the template object, got the counter name, and so on, Merb limited the activity inside the loop to setting a couple of Hash values and calling a method.

For a collection of 100 partials, this could be the difference between overhead of around 10ms and overhead of around 3ms. For a collection of small partials, this could be significant (and a reason to inline partials that were appropriate to be partials in the first place).

In Rails 3, we've improved performance by reducing what happens inside the loop. Unfortunately,there was a specific feature of Rails that made it a bit harder to optimize this generically. Specifically, you could render a partial with a heterogenous collection (a collection containing Post, Article and Page objects, for instance) and Rails would render the correct template for each object (Article objects render _article.html.erb, etc.). This means that it was not always possible to determine the template to render up front.

In order to deal with this problem, we haven't been able to optimize the heterogenous case completely, but we have made render :partial => "name", :collection => @array faster. In order to achieve this, we split the code paths, with a fast path for when we knew the template, and a slow path for where it had to be determined based on the object.

So now, here's what rendering a collection looks like, when we know the template:

def collection_with_template(template = @template)
  segments, locals, as = [], @locals, @options[:as] || template.variable_name
 
  counter_name  = template.counter_name
  locals[counter_name] = -1
 
  @collection.each do |object|
    locals[counter_name] += 1
    locals[as] = object
 
    segments < < template.render(@view, locals)
  end
 
  @template = template
  segments
end

Importantly, the loop is now tiny (even simpler than what happened in Merb inside the loop). Something else worth mentioning is that in improving the performance of this code, we created a PartialRenderer object to track state. Even though you might expect that creating a new object would be expensive, it turns out that object allocations are relatively cheap in Ruby, and objects can provide opportunities for caching that are more difficult in procedural code.

For those of you want to see the improvements in pictures, here are a few things to look at: first, we have the improvement between Rails 2.3 and Rails 3 edge on Ruby 1.9 (smaller is faster).

And here it is for more expensive operations:

Last we've got a comparison of Rails 3 across four implementations (Ruby 1.8, Ruby 1.9, Rubinius, and JRuby):

You can see that Rails 3 is significantly faster than Rails 2.3 across the board, and that all implementations (including Rubinius!) are significantly improved over Ruby 1.8. All in all, a great year for Ruby!

Next post, I'll talk about improvements in the Rails 3 API for plugin authors—keep an eye out, and as always, leave your comments!

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

Rails and Merb Merge: The Anniversary (Part 1 of 6)

By Yehuda Katz | December 23rd, 2009 at 2:12PM

A year ago today, we announced that Rails and Merb would merge. At the time, there was much skepticism about the likelihood of the success of this endeavor. Indeed, The most common imagery invoked by those who learned about our plans was a unicorn. At RailsConf last year (well into the effort), both DHH and I used unicorns in our talks, poking fun at the vast expectations we’d set, and the apparent impossibility of achieving everything we’d said we wanted to achieve for 3.0.

A year has gone by, so it’s a good time to reflect on how well we’ve done at achieving those expectations. Over the next few days, I’ll take each bullet point that I provided in my original post, and go into detail about the progress we’ve made on that front.

I’ve given a few recent talks on these topics, so some of you may already have seen some of this, but I wanted to get it down in writing for those who hadn’t. I’ve also added new information, some of which was omitted because it was difficult to explain in a talk, and some of which is too current for any of my recent talks.

Modularity

Rails will become more modular, starting with a rails-core, and including the ability to opt in or out of specific components. We will focus on reducing coupling across Rails, and making it possible to replace parts of Rails without disturbing other parts. This is exactly what Merb means when it touts “modularity”.

We’ve spent a significant amount of time on this step which has been really fruitful. I’ll give a few specific examples.

ActiveSupport

First, we’ve gone through ActiveSupport, making it viable to cherry-pick specific elements. This means that using ActiveSupport’s inflector, time extensions, class extensions, or anything your heart desires is now possible without having to personally track the dependency graph. Here’s an example of what I mean, from the to_sentence method in ActiveSupport from Rails 2.3:

module ActiveSupport 
  module CoreExtensions 
    module Array 
      module Conversions 
        def to_sentence(options = {})           ...           options.assert_valid_keys :words_connector, :two_words_connector, :last_word_connector, :locale           ...         end       ...     end   end end
As you can see, to_sentence has an implicit requirement on assert_valid_keys, which means that in order to cherry-pick active_support/core_ext/array/conversions, you are forced to work through the file, find any unsatisfied dependencies, and be sure to require them as well. And of course, the structure of these dependencies could easily change in a future version of Rails, so relying on what you’d found would be unsafe. In Rails 3, the top of that same file looks like:
require 'active_support/core_ext/hash/keys'
require 'active_support/core_ext/hash/reverse_merge'
require 'active_support/inflector'
This is because we’ve gone through the entire ActiveSupport library, found the unsatisfied dependencies, and made them explicit. As a result, you can pull out the specific libraries you want for a small project, and not get the full weight of ActiveSupport.

Even better, other parts of Rails now explicitly declare the dependencies they have on ActiveSupport. So for instance, the code that adds logging support to ActionController has the following lines on top:

require 'active_support/core_ext/logger'
require 'active_support/benchmarkable'
This means that all of Rails knows what parts of ActiveSupport are needed. For simplicity, Rails 3 ships with all of ActiveSupport still provided, so you’ll be able to use things like 3.days or 3.kilobytes without interruption. However, if you want more control over what gets included, that’s possible. You can declare config.active_support.bare = true in your configuration and we’ll pull in only the parts of ActiveSupport explicitly needed for the parts of Rails that you use. You’ll still need to include the fancy parts if you want to use them – 3.days wont work out of the box with bare enabled.

ActionController

Another area that really needed an overhaul was ActionController. Previously, ActionController had a number of disparate elements all in one place. When we looked closely, we found that there were really three discrete components masquerading as one.

First, there was the dispatching functionality. This included the dispatcher itself, routing, middleware, and rack extensions. Second there was generic controller code that was meant to be reused elsewhere, and was in fact reused in ActionMailer. Finally, there was the subset of controller code that brought those two concerns together: code that handled requests and responses through a controller architecture.

In Rails 3, each of those components has been separated out. The dispatcher functionality has been moved into ActionDispatch, with the code inside tightened up and really made a conceptual component. The parts of ActionController that were meant to be reused by non-HTTP controllers was moved into a new component called AbstractController, which both ActionController and ActionMailer inherit from.

Finally, ActionController itself has gotten a significant overhaul. Essentially, we’ve isolated every standalone component, and made it possible to start with a stripped-down controller and pull in just the components you want. Our old friend ActionController::Base simply starts with that same stripped-down controller and pulls everything in. For instance, take a look at the beginning of the new version of that class:

module ActionController
  class Base < Metal
    abstract!

include AbstractController::Callbacks
include AbstractController::Logger

include ActionController::Helpers
include ActionController::HideActions
include ActionController::UrlFor
include ActionController::Redirecting
include ActionController::Rendering
include ActionController::Renderers::All
include ActionController::Layouts
include ActionController::ConditionalGet
include ActionController::RackDelegation
include ActionController::Logger
include ActionController::Benchmarking
include ActionController::Configuration</pre>

All we're doing here is pulling in every available module, so the default experience of Rails is the same as before. However, the real power of what we've done here is the same as what we've done in ActiveSupport: every module declares its dependencies on other modules, so you can pull in Rendering, for instance, without having to wonder what other modules need to be included and in what order.

The following is a perfectly valid controller in Rails 3:

class FasterController < ActionController::Metal
  abstract!

# Rendering would be pulled in by layouts, but I include # it here for clarity include ActionController::Rendering include ActionController::Layouts

append_view_path Rails.root.join("app/views") end

class AwesomeController < FasterController def index render "so_speedy" end end And then, in your routes, it would be perfectly valid to do:

MyApp.routes.draw do
  match "/must_be_fast", :to => "awesome#index"
end
Essentially, ActionController::Base has become just one way to express your controllers. Think of it like Rails Classic, with the ability to roll your own if you're not so into that taste. It's really easy to mix and match too: if you wanted to pull in before_filter functionality to FasterController, we could simply include AbstractController::Callbacks.

Note that without doing anything else, including those modules pulled in AbstractController::Rendering (the subset of rendering functionality shared with ActionMailer), AbstractController::Layouts, and ActiveSupport::Callbacks.

This makes it really possible to trivially pull in just the specific functionality you need in performance-sensitive cases without having to use an entirely different API. If you need additional functionality, you can easily just pull in additional modules or eventually upgrade to the full ActionController::Base without needing to rip anything apart along the way.

This, in fact, is a core idea of Rails 3: there are no monolithic components, only modules that work seamlessly together in a great package of defaults. This allows people to continue using Rails as they have used it successfully in previous versions, but really leverage the codebase for alternative uses. No more functionality locked away in non-reusable forms.

One nice immediate benefit of all of this is that ActionMailer gets all of the functionality of ActionController in a clean, intentional way. Everything from layouts and helpers to filters is using the identical code that ActionController uses, so ActionMailer can never again drift away from the functionality of ActionController (as ActionController itself evolves).

Middleware gets a helping hand too. ActionController::Middleware, which is middleware with all of the powers of ActionController, allows you to pull in whatever ActionController features you want (like Rendering, ConditionalGet, robust Request and Response objects, and more) as needed. Here's an example:

# The long way
class AddMyName < ActionController::Middleware
  def call(env)
    status, headers, body = @app.call(env)
    headers["X-Author"] = "Yehuda Katz"
    headers["Content-Type"] = "application/xml"

etag = env["If-None-Match"]
key = ActiveSupport::Cache.expand_cache_key(body + "Yehuda Katz")
headers["ETag"] = %["#{Digest::MD5.hexdigest(key)}"]
if headers["ETag"] == etag
  headers["Cache-Control" = "public"]
  return [304, headers, [" "]]
end

return status, headers, body

end end

# Using extra Rack helpers
class AddMyName < ActionController::Middleware
  include ActionController::RackDelegation

def call(env) self.status, self.headers, self.response_body = @app.call(env)

headers["X-Author"] = "Yehuda Katz"

# but you can do more nice stuff now
self.content_type = Mime::XML # delegates to the response
response.etag = "#{response.body}Yehuda Katz"
response.cache_control[:public] = true

self.status, self.response_body = 304, nil if request.fresh?(response)

response.to_a

end end

# Using ConditionalGet helpers
class AddMyName < ActionController::Middleware
  # pulls in RackDelegation
  include ActionController::ConditionalGet

def call(env) self.status, self.headers, self.response_body = @app.call(env)

headers["X-Author"] = "Yehuda Katz"

self.content_type = Mime::XML
fresh_when :etag =&gt; "#{response.body}Yehuda Katz", :public =&gt; true

response.to_a

end end In all, I really think we've delivered on our promise to bring significant modularity improvements to Rails (and then some). In fact, I think the level of success that we've had with this version exceeds most people's expectations of a year ago, and is solidly in golden unicorn territory. Enjoy!

Next, I'll talk about bringing performance improvements to Rails 3. Hopefully it won't go by too fast. :)

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

LDAP Directories: The Forgotten NoSQL

By Michael Mullany | December 17th, 2009 at 10:12AM

When most Rails developers encounter LDAP, it’s usually for user authentication. And most of the time, there’s no choice, they’re working under a dictate that requires them to use it. Usually, this means Active Directory, but very occasionally something like OpenLDAP or the Sun Java Systems Directory Server.

It’s hard to imagine now, but there was once great excitement about the potential for LDAP based directory servers to become more than just authentication servers and morph into general purpose datastores.  LDAP directories promised a single, scalable, high performance data store that could be queried for common information across multiple applications. After all, directories had a lot of virtues:

  • Fast Queries: LDAP directories were heavily indexed, so query speeds were truly impressive—reliably 10x what a relational database could manage. (Write speed was much slower for the same reason: lots of indexes to update when a write happened)
  • Replication: LDAP directories were an “eventually consistent” data store long before Dynamo or Cassandra. Multi-master replication allowed a distributed network of directories to accept writes at any node, and then relay these updates around the directory network. The last update in time always won.
  • Partionable: directories were giant tree structures, and branches could be picked up and moved to another server if the directory got too big. There was built-in referential linking from each amputation point to the correct server, and these servers could be easily geographically distributed.
  • Standardized and efficient: coming from a telecom heritage, LDAP was an efficient wire protocol. It was globalized and cross-system. LDAP queries and responses were binary encoded using distinguished encoding rules, using ASN.1 as the data representation syntax.

In addition to these benefits, directories like Netscape Directory Server and Microsoft Active Directory had a seemingly endless list of other features like rich, complex configurable access control rules and permissions; multiple ways to define groups; rich query semantics and more.

And yet, when we look around today, it’s not LDAP directories that have the NoSQL buzz; it’s the far looser and simpler key-value stores like Cassandra, MongoDB and Redis. So where did LDAP fall down, and is there anything to be learned from its (relative) failure? Here is my own take on why LDAP didn’t take over the world, colored by my (brief) tenure as a product manager for Netscape Directory Server.

  1. Telecom protocols FTL: LDAP, in my own humble opinion, was fatally crippled by its telecom parentage. Just reading the first page of the ASN.1 data structure specification could make your eyes bleed. Debugging a badly behaved LDAP client or query was basically a job for experts wielding binary to text crackers. There was a separate format—LDIF—for converting LDAP into human-readable code, but this was a friction point. Compared to ASN.1, JSON (as an example) is severely limited and incomplete, and yet… about 1000x more popular as a result.
  2. Access control that exceeded human brain capacity: LDAP directories provided lots of rope for people who cared about security to firmly and irrevocably tie themselves in knots. Time and again, I’d see customers with five or more layers of access control rules they found to be confounding, with counter-intuitive effects. Better yet, this level of complexity was indecipherable by anyone without drawing five dimensional set diagrams. Sometimes, there are features you shouldn’t put into a product no matter how much people ask you. They know not what they do.
  3. Interesting data wanted to be relational: it was a simple, but sad truth. Data that’s interesting and important enough to be accessed often by your applications, seems to want to be compared and operated on in the context of your other interesting data; that sounds a lot like the right case for a relational database. Directories, as a hierarchical data store, couldn’t easily accommodate the kinds of queries that customers ended up wanting to do, once they were storing enough interesting data. So the solution was to patch in “relationy” features like aliases which soft-linked two values in different parts of the tree—but these were patchwork solutions. In their worst (over-used) incarnation, they turned a directory server into a weird hard-to-maintain mutant hybrid of relational and hierarchical database.

There were other downsides to LDAP directories of course. The learning curve could be steep for LDAP since it was a truly novel technology for most people used to RDBMS’s and SQL. And probably most importantly, most directories weren’t open source, and so they missed the opportunity to fully leverage a community of interested developers and administrators.

Lessons for this Generation of NoSQL (?)

I hesitate to speculate on the lessons from LDAP for this generation of NoSQL stores, since open source has changed the game considerably in the last ten years. That said, I do think LDAP got a lot of things right (fast, distributable, scalable and standardized). It’s arguable whether custom binary protocols (aka MongoDB’s) will really hurt adoption as long as the data structure specifications are reasonably readable, but Couch’s JSON/REST/HTTP combo is certainly a little easier on the eyes.

I do know one thing: keep the access control simple. Your users will thank you later!

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

Your Pages Will Load Faster with Rails!

By Yehuda Katz | December 15th, 2009 at 10:12AM

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

In Inside Rails, Yehuda Katz, Rails expert and core team member, and Carl Lerche, Rails expert and full-time contributor, present expert advice and insight on the Rails platform and Rails development.


Server-side programmers tend to spend a lot of time tuning their server-side code. Studies show, however, that 90 percent of the user’s perceived performance is on the client-side. The YSlow recommendations are the gold standard for client-side performance, providing a list of things web developers can do both on the server-side and the client-side to improve performance. If you use Rails, a large number of these recommendations are baked into the framework.

These tips are not extremely difficult to implement yourself with another framework, but Rails and Engine Yard believe in making your websites fast by default: you shouldn’t need to understand all the minutiae in order to get a snappy application.

Use Far-Future Expires

YSlow recommends using an Expires header set far into the future. This means that browsers don’t need to ask for your images, JavaScripts, or stylesheets again after the first request. However, this introduces an additional problem: what happens if you (inevitably) change these files? Won’t the browser be stuck with the old file forever?

The solution is to include a last modified timestamp with your assets (something like <img src="/images/myasset.png?84392578943" alt="" />). When the file is modified, you update the timestamp, and the browser will know to ask for the file again.

When you use Rails, this timestamping is the default behavior; all you need to do is set up your Nginx or Apache config to serve with far future headers and you’re ready to go. And if you use Engine Yard, we set up those headers for you.

GZip Components

YSlow recommends GZipping JavaScript, CSS, and other textual assets. This reduces their file size over the wire by approximately 70%. This can be set up using your Apache or Nginx config. If you use Engine Yard, your assets are gzipped by default.

Split Components Across Domains

Some older browsers (cough IE cough) will only download two assets from a host at a time. This means that if you have two JavaScript files, two CSS files, and 10 images, these assets will come down two at a time, even though the user’s connection is fast enough to download them in parallel. The solution to this problem is to split your assets up over multiple hosts (assets0.yourapp.com, assets1.yourapp.com, and so on).

When using server-side code you wrote yourself, or using a traditional server-side framework, you’d be forced to go through all of the URLs you generated and update them to use an asset host helper. With Rails, you can simply add a single line to your configuration and all of your generated URLs will start using the asset hosts you specified:

config.action_controller.asset_host = "assets%d.yourapp.com"

Of course, you’ll need to set up a CNAME entry in your DNS to point the new virtual domains at the same host, but that should be trivial.

This also single-handedly resolves another YSlow recommendation: “Use Cookie-free Domains for Components.” If your assets and application are on the same domain, the browser and server will pass cookies back and forth (up to 4k worth) for each asset request. By using asset hosts for your assets, you get the side benefit of cookie-free domains for your assets.

When comparing frameworks, it’s tempting to look at something like “Etag Support” as a feature and simply check it off. Looking under the surface, however, there are complex considerations that are begging to be abstracted away from you. Why should you have to know what the header name in the request is called (If-None-Match) or how to generate a reliable unique key for an object?

Rails makes working with these ideas drop-dead simple, so you won’t have to stress about following web best practices; when using Rails, you’ll be doing the right thing and hardly know it. Stay tuned for a follow up post on more of the ways Rails makes fast page loading a breeze.

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

Ready, Set, Go!

By Dave Astels | December 9th, 2009 at 10:12AM

Google recently publicly released their new programming language, Go. I’ve known about this for some time, having worked for the big G while it was in development, although not directly involved.

Google has plenty of special purpose languages, but this is the first general purpose language to come out of Mountain View. That fact alone makes it quite interesting. Add to it that some of the original C and UNIX people are involved, and it becomes something that requires investigation.

What’s it for?

Go is a systems programming language. Think of it as a modern C. It’s meant for the same types of programming: operating systems, compilers, infrastructure pieces.

What’s it all about?

To summarize the main golang.org page, Go is: simple, fast, safe, concurrent, open source, and fun.

Simple Go is designed to be easy to work with, requiring minimal overhead and boilerplate. This is reminiscent of dynamic languages such as Python and Ruby.
Fast Go’s syntax is designed well, being regular and relatively simple. In addition to its dependency management features, this leads to fast compile times. Go provides the runtime efficiency of a statically typed, compiled language such as C or Java without the bloated syntax of those languages.
Safe Go also provides the type safety of a statically typed, compiled language, without the syntax bloat usually associated with a statically typed language. In addition to type safety, Go is memory safe: you should never get a null pointer error when you use an invalid memory address. Additionally, it is a garbage collected language, which eliminates even more overhead code and memory related errors.
Concurrent Go is modern language, designed for use in multi-core environments. Concurrency is built into the language, as is inter process communication (via channels).
Open Source Go is released under a BSD style license.
Fun You’ll have to decide that for yourself… but we’re enjoying it here at least.

What’s so different about it?

Go is an object-oriented language, but not in the style that most programmers these days think of OO. Go doesn’t have implementation inheritance (more on that later). Also, you don’t define methods inside a class as in many OO languages. Instead, you add methods to types in a way somewhat reminiscent of CLOS . For example:

func (file *File) Read(b []byte) (number_bytes_read int, err os.Error) { ... }
  if file == nil {
    return -1, os.EINVAL
  }
  r, e := syscall.Read(file.fd, b);
  if e != 0 {
    err = os.Errno(e);
  }
  return int(r), err
}

Here we have a function Read that’s being attached to the type *File. Read takes a single parameter, b, which is an array of byte and it returns an int named number_bytes_read and an Error named err. There are three things to note here. Read, is (1) being attached to an existing type (a pointer type at that), and (2) returns multiple values which (3) are named. Even if the return value names aren’t used, they are superb for documenting what the returned values mean.

Points of Note

Fast compilation

Go compiles fast. Really fast. This means no long waits for builds. More importantly, it means a tighter test-code development cycle.

(more…)

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