Archive for the 'ruby' Category

Ruby to C, a couple snippets

As you might already know, the Ruby interpreter you’re (probably) using (the one at ruby-lang.org) know as MRI, for Matz Ruby Interpreter, is written in C. The VM is entirely written in C and most of the standard libraries too. So it seems that the way to make code run faster is really to dive into C.

Now I hear you yell with your ears all red, eyes full of blood and a big vein pumping on your forehead: But it will be unmaintainable!

If everything seems under control, you’re not going fast enough

- Mario Andretti

The hard part is that the C API of MRI lacks documentation. You either have to search through the mailinglist, look at other people’s code or try to guess from Ruby’s code. I recently ported a bit of code from Ruby to C and here are a couple of Ruby to C mapping that might help you.

If you’ve never written or looked at a Ruby extension before, I suggestion reading Peter Cooper excellent and very simple tutorial on how to set things up.

Objects are VALUEs


my_var = nil
this_is_true = true
some_fixnum = 1


VALUE my_var = Qnil;
VALUE this_is_true = Qtrue;
VALUE some_fixnum = INT2FIX(1);

Strings


string = "hi"


VALUE string = rb_str_new2("hi");

/* Ruby string to C string */
char *s = RSTRING_PTR(str);
int len = RSTRING_LEN(str);

Object to C struct

In C you’ll probably want to store your data in a struct. Ruby provide some things to wrap a struct inside a Ruby object. Also, since Ruby is not aware of the stuff created in the C world we have to be pretty explicit about everything.


module MyModule
  class MyClass
    def close
      @closed = true
    end
  end
end

In C


/* This is called by Ruby GC when the object is freed
 * so free all resources used here. We hook it up
 * in the Data_Wrap_Struct call down there.  */
void my_obj_free(my_struct_t *s)
{
  free(s);
}

/* Called by Ruby then and instance of your class is created
 * hooked by rb_define_alloc_func. */
VALUE my_obj_alloc(VALUE klass)
{
  my_struct_t *s = ALLOC_N(my_struct_t, 1);
  /* This stores the struct inside the Ruby object, so you
   * can get the struct back on each method call. */
  return Data_Wrap_Struct(klass, NULL, my_obj_free, s);
}

/* The actual MyClass#close method, first argument is the
 * instance object. It’s hook into our class by the rb_define_method
 * call in Init_ */
VALUE my_obj_close(VALUE self)
{
  my_struct_t *s;
  /* This is where we get the struct back from the Ruby object */
  Data_Get_Struct(self, my_struct_t, s);

  s->closed = 1;
}

/* Init_* is magically called by Ruby to bootstrap your extension,
 * it’s like a main function if you will. */
void Init_name_of_your_extension()
{
  VALUE mMyModule = rb_define_module("MyModule");
  VALUE cMyClass = rb_define_class_under(mMyModule, "MyClass", rb_cObject);  

  rb_define_alloc_func(cMyClass, my_obj_alloc);
  rb_define_method(cMyClass, "close", my_obj_close, 0);
}

Calling methods


obj.method_name

result = begin
  obj.method_name(arg)
rescue
  #
end


rb_funcall(obj, rb_intern("method_name"), 0);
/* if an error is raised, result will be set to Qundef */
VALUE result = rb_funcall_rescue(obj, rb_intern("method_name"), 1, arg);

Blocks


my_ary.each do |i|
  puts i
end


VALUE i_each(VALUE elem, VALUE *obj)
{
  /* elem is the yielded object and obj is a pointer to obj down there v */
  printf("%d\n", FIX2INT(elem));
  return Qnil;
}
rb_iterate(rb_each, my_ary, i_each, (VALUE) obj);

Error handling


raise ArgumentError, ""


rb_raise(rb_eArgError, "");
/* To raise an error corresponding to the one in errno */
rb_sys_fail("oops");

Garbage Collection

When creating Ruby objects in the C world, Ruby is not aware of the scope of each, it’s impossible to track when a variable is used and when it’s not, so it’s impossible for the garbage collector to know when do leave or sweep objects. If you create a long live object, that will persist for several method calls you have to tell Ruby’s garbage collector when it’s in use and when you’re finished with it. If you don’t, Ruby GC will free your variable in the worst possible time, ending up with unthinkable tragedies around the world, leaving you homeless and poor for all eternity.


VALUE var = rb_str_new2("prout");

/* Tell Ruby GC this object is in use */
rb_gc_register_address(&var);
/* when you’re finished you can unregister it and let the GC free it */
rb_gc_unregister_address(&var);

Note that you don’t need to do this if you only use the variable in one method since the control is not given back to Ruby during the method call. Also if the variable is still referenced in the Ruby world it’s already registered.

More, more, more!

This is far from a complete guide to MRI, you’ll need to dig the Ruby doxygen doc if you need more info.

Now lets port Rails to C!

Staying Alive with Thin!

TravoltaThin was the first Ruby server to be able to connect to UNIX domain sockets, giving you a little more speed, so you could spend less time browsing and more time dancing and eating ice creams with the people you love. But only Nginx (that I know of) supports UNIX domain sockets. Non-Nginx users might like to have time to dance and eat ice creams with the people they love too!

So that’s why Thin new release (0.7.0 codename Spherical Cow) supports persistent connections (aka Keep-Alive).

Apache & mod_proxy

Under Apache, if a client requests a persistent connection, the connection to the backend server (through mod_proxy) will also be persistent. That means the time spent opening the connection is saved for subsequent requests because all requests will be sent through the same connection. Even cooler is that you have nothing to do to set that up, unless you’ve turned Keep-Alive off.

I ran some benchmarks and I got an average gain of 200 req/sec with Keep-Alive on.
Keep-Alive benchmark

Sadly Nginx doesn’t support persistent connections to backends yet, but it seems to be a highly requested feature, and Igor Syseov (author of Nginx) said he’s working on it several times on the mailing list. Imagine that: UNIX sockets + persistent connections, oooh man!

Hey we develop too, sometimes!

Of course you’ll take advantage of that feature when it’s just you, Thin and your browser. And we can also benchmark it, just for fun, to get numbers that are sure to give you goose bumps: 7800 req/sec is just, ooooh, aaahh, hummm, yeah, that was good!

Keep-Alive

Swiftiply

If you’re looking for even more speed. Thin can now be used as a Swiftiply client. And it’s very easy to use:

thin start --servers 3 --port 8000 --swiftiply

Just add the --swiftiply option. This also means that any Rack adapter can be run through Swiftiply (using the --rackup option) including Ramaze, Camping, Merb, YourCrazyLittleFrameworkThatIsSoooBetterThenAllTheOtherAndThatSupportRack etc.

Speed with control

But what is speed with no control, right?

The main reason why Mongrel couldn’t support persistent connections was because of Ruby 1024 file/socket descriptors limitation. If you don’t close the connection (keep them alive) it’s one less descriptor you can use to process another connection or open a file.

Although EventMachine doesn’t have an infinite number of file descriptors it was reported to handle more then 20 000 concurrent connections.

You can now tune the number of connections a Thin server can handle.

--max-conns: This sets the maximum number of concurrent connections your Thin server can handle. Setting higher then 1024 might require superuser privileges on some system.

--max-persistent-conns: This sets the maximum number of persistent connections your Thin server can handle at the same time. If resource usage is important, you might want to turn that down. You can turn Keep-Alive support off by setting to 0.

Get it!

Spherical Cow also comes with a couple bug fixes and tweak.
As usual, you can get the latest version from RubyForge:

sudo gem install thin

If you have any question, join the Google Group or the #thin channel on freenode.

I hope you like it!

MoR7 Presentation

ImageHere’s the code and slides of my presentation : http://github.com/macournoyer/mor7/

Hope you liked it!

Presenting Thin at Montreal on Rails

MoRIf you’re in Montreal next Tuesday, be sure to come to the next Montreal on Rails. I’ll be presenting Thin with a fanfare, dancers and pizza.

RSVP if not already done and do it fast, places are limited.

I’ll present some new features in the upcoming Spherical Cow release and recode Thin like I did at DempCampCUSEC, but this time, with one hand tied being my back and someone yelling at me in Russian. All this in 2 minutes (I don’t know yet what I’ll do with the remaining time).

The Flexible Thin Anorexic Gymnast that Democratized Deployment

ImageThin is fast, I don’t think I need to prove that again. But what I’d like to showcase now is Thin extensibility. Most of it is due to Thin being based on Rack. It’s also why lots of framework are supporting Thin already.

Can Thin replace all images with LOLCAT pics on my site when it’s my birthday of a leap year, plz, plz, plz? Cause it should!

Yeah, you’re not the first one to ask.

config.ru file


require open-uri
require hpricot

class LolCater
  def initialize(app)
    @app = app
  end

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

    if iz_ma_burdae? && leap_year?
      doc = Hpricot(body)
      doc.search(img).set(src, lolcat_pic_url)
      body = doc.to_html
    end

    [status, headers, body]
  end

  private
    def iz_ma_burdae?
      Date.today.month == 11 && Date.today.day == 7
    end

    def leap_year?
      Date.today.leap?
    end

    def lolcat_pic_url
      doc = Hpricot(open(http://icanhascheezburger.com/?random#top))
      pic = (doc/div.snap_preview img).first.attributes['src']
    end
end

use LolCater
run Rack::Adapter::Rails.new(:root => /path/to/my/app)

That’s called a Rack middleware. It must have a call method and receive a Rack app as the first argument of new. You then tell Thin to use this middleware when running your Rails application. Save the file as config.ru. .ru is for Rackup config file.

You can now launch your application with the thin script:

thin start --rackup config.ru

Wai-wai-wait that means I can built my own framework in like 30 LOC and use all of Thin goodness?

Right! You can start a cluster of your lolcat image replacer app like you would for a Rails app, but specify the --rackup option, which tell Thin to load your application from this file instead to go with the default Rails adapter.

thin start --rackup config.ru --servers 3

In fact, here’s a lil’ framework I built in 35 LOC:


# Start w/ thin start -r invisible.rb
require tenjin

module ::Invisible
  class Adapter
    def initialize
      @template = Tenjin::Engine.new(:postfix=>.rbhtml, :layout=>../layout.rbhtml, :path=>home)
      @file = Rack::File.new(public)
    end
    def call(env)
      path = env['PATH_INFO']
      if path.include?(.)
        @file.call(env)
      else
        _, controller, action = env['PATH_INFO'].split(/)
        Invisible.const_get("#{(controller || home).capitalize}Controller").new(@template, env).call(action || index)
      end
    end
  end
  class Controller
    def initialize(template, env)
      @template, @status, @env, @headers = template, 200, env, {Content-Type => text/html}
    end
    def call(action)
      send(action)
      render(action) unless @body
      [@status, @headers.merge('Content-Length'=>@body.size.to_s), [@body]]
    end
    protected
      def render(action=nil)
        @body = @template.render(action.to_sym, instance_variables.inject({}) {|h,v| h[v[1..-1]] = instance_variable_get(v); h})
      end
  end
end

require app/controllers
run Invisible::Adapter.new

Full code on my github repo: http://github.com/macournoyer/invisible/.

And you know I can’t help myself but benchmark it:
merb-core: 1865.19 req/sec
invisible: 2428.17 req/sec

(Disclamer: it’s just for fun, don’t use that framework ever or I will come in your house when you sleep and steel all your left foot socks).

Democratizing deployment

I don’t know if you get what this means? It means, deploying a Rails or Merb, Ramaze, etc, app is just a matter of writing a simple Rack config file and playing w/ the –rackup option. It’s all the same script and tools now!

For example, there’s a Ramaze Rack config file in example/ramaze.ru, so to deploy your Ramaze application you would.

thin start --servers 3 --rackup ramaze.ru

Or create a config file and install it as a runlevel script:

thin config --rackup ramaze.ru -C myapp.yml
edit myapp.yml
sudo thin install # Installs the runlevel script
mv myapp.yml /etc/thin/myapp.yml

Or if you prefer to monitor your clusters with God, check out the sample God config file that is a drop-in replacement for Thin’s runlevel script.

Happy hacking & deploying!

Get intimate with your load balancer tonight!

The Thin Cheesecake release is out (v0.6.1)!

sudo gem install thin

That tasty and sweet Cheesecake release comes with some new sweet topping: config file support, uses less memory, some speed tweaks, but that’s nothing new regarding what we all know and use from other web servers. Nothing very innovative, breath taking, crazy, revolutionary or surprising you say.

You’re right!

Almost …

There’s another feature that as never been seen amongst Ruby web servers (indeed, haven’t found any that does that). But first, lets explore the typical deployment of a Rails app.

Let’s deploy Rails shall we?

Typically you’d deploy your rails application using mongrel like this:

mongrel_rails cluster::configure -C config/mongrel_cluster.yml --servers 3 --chdir /var/www/app/current ...
mongrel_rails cluster::start -C config/mongrel_cluster.yml

Then on your web server / load balancer configuration file (Nginx in this case):
nginx.conf

upstream  backend {
  server   127.0.0.1:5000;
  server   127.0.0.1:5001;
  server   127.0.0.1:5002;
}

Now with Thin, it’s the same

thin config -C config/thin.yml --servers 3 --port 5000 --chdir ...
thin start -C config/thin.yml

That will start 3 servers running on port 5000, 5001 and 5002.
And your web server configuration stays the same.

Really, between you and me, is Thin really really faster?

Benchmarks
Simple “hello world” app, running one server

And uses less memory too:
Memory
Mesured after running: ab -n5000 -c3

What about that new, crazy, amazing feature you mentioned?

Ever wanted to keep closer to you web server? Sometimes connecting through a TCP port on 127.0.0.1 feels a bit … disconnected. What if we’d get closer to it, get intimate with it, share some feelings, exchange toothbrush?

Introducing UNIX socket connections

When using more then one server (a cluster) behind a load balancer, we need to connect those servers to the load balancer through dedicated ports like in the previous “Let’s deploy Rails” section. But this connect the 2 using a TCP socket which means, all requests have to go though the network interface stuff twice! Once from the client (browser) to the load balancer / web server and again to the backend server.

There’s a better approach to this. Some load balancer (like Nginx) support connecting to a UNIX domain socket.

nginx.conf

upstream  backend {
  server   unix:/tmp/thin.0.sock;
  server   unix:/tmp/thin.1.sock;
  server   unix:/tmp/thin.2.sock;
}

Then start your Thin servers, like this:
thin start --server 3 --socket /tmp/thin.sock

And yes, it is faster:
Socket
3 servers running a simple Rails application, behind Nginx

DemoCampCUSEC follow-up

Yesterday I presented Thin at DemoCampCUSEC. Things went pretty well and the other presentations were very good too.

My demo was basically re-coding a simplified version of Thin live, here’s the final result:


%w(rubygems eventmachine thin thin_parser rack).each { |f| require f }

class Connection < EventMachine::Connection
  attr_accessor :app

  def initialize
    @parser = Thin::HttpParser.new
    @data = 
    @nparsed = 0
    @env = {}
  end

  def receive_data(data)
    @data << data
    @nparsed = @parser.execute(@env, @data, @nparsed)

    process if @parser.finished?
  end

  def process
    status, headers, body = @app.call(@env)

    body_output = 
    body.each { |l| body_output << l }

    send_data "HTTP/1.1 #{status} OK\r\n" +
              headers.inject() { |h, (k,v)| h += "#k: #v\r\n" } +
              "\r\n" +
              body_output

    close_connection_after_writing
  end
end

welcome_app = proc do |env|
  [
    200,                                  # Status
    {'Content-Type' => 'text/html'},      # Headers
    [
      '<html><body>',
      '<h1>Welcome</h1>',
      '<p>Welcome to my server!</p>',            # Body
      '<p><a href="/rails">My Rails app!</a></p>',
      '</body></html>'
    ]
  ]
end

rails_app = Rack::Adapter::Rails.new(:root => /Users/marc/projects/refactormycode, :prefix => /rails)

app = Rack::URLMap.new(/ => welcome_app, /rails => rails_app)

EventMachine.run do
  EventMachine.start_server 0.0.0.0, 3000, Connection do |con|
    con.app = app
  end
end

Hope you enjoyed it!

Thin : A Followup!

Thin was launched a week ago and it instantly became a huge success in the Ruby community!

  • Someone created the #thin IRC channel on freenode where around 7-10 ppl hang around
  • Lots of people are active on the Google Group, asking questions and submitting patches (more then 50 members already!)
  • Thin was featured on Ruby Inside blog
  • and on the Rails Envy podcast in which Jason and Gregg are having a hard time saying my name
  • Joao Pedrosa switched to Thin on his blog
  • Daniel Fisher had a great review
  • Mat, Heri and Carl said some very nice words about it too
  • Thin support was added to Ramaze and Vintage frameworks (Kevin William also submitted a patch to Merb but has not been accepted yet)
  • A couple of days ago, the LOLCAT release of Thin fixed a couple of bugs and introduced Ruby 1.9 support under which Thin is even faster!

Also Julie Hache, asked me to present Thin at the upcoming DemoCampCUSEC2 and I accepted with pleasure! See you there!

Thin - A fast and simple web server

ThinThin is a web server that glues together 3 of the best Ruby libraries in web history:

  • the Mongrel parser: the root of Mongrel speed and security
  • Event Machine: a network I/O library with extremely high scalability, performance and stability
  • Rack: a minimal interface between web servers and Ruby frameworks

Which makes it, with all humble humility, the most secure, stable, fast and extensible Ruby web server ever built, bundled in an easy to use gem for your own pleasure.

Why another web server ?

Thin started as an experiment to build an 100% Ruby singled threaded server which ended up being as fast as Mongrel on some cases (like handling Rails request). But that was not enough. Using the EventMachine library the performance and stability are now more impressive then a 500 pounds guy standing on one hand and juggling with the other.

It’s less then 500 lines of clean and fully tested Ruby code plus the customized Mongrel parser (written in C).

Faster then Mongrel ? Yeah right !

Yes it is! It’s even faster then the patched version of Mongrel that uses EventMachine (aka Evented Mongrel).
Request / seconds comparison

Try it

sudo gem install thin

Then in your Rails application directory:

thin start

You can also use it with any framework that support Rack. More info on the usage page.

More alpha then the Greek letter

This is the first experimental release. If you use this on your production server right now you are stupid, looking for extreme sensations or trying to find a way to get fired.

If you’d like to help, have fun or report a bug, join me in the project Google Group and get the code:

git clone http://code.macournoyer.com/git/thin.git

Stay tuned for updates and releases!

If you care, Digg it or Reddit it !

Has Zed jumped the shark ?

Zed ShawZed Shaw, the creator of Mongrel, is writing some pretty nasty stuff about the Rails community, ThoughWorks and Ruby.

There’s no work for a smart man in a town full of stupid.

Although I agree there might be some stupid people working with Rails right now, there’s a huge number of very bright people too. And there’s stupid people working with PHP, Java, Python, C, Haskell, Erlang, Lua and Factor too.

The thing is: Rails is getting easier to use every day, lowering the barrier of entry for new coders. The Rails community is growing beginner level coders a lot faster then expert coders now. A year ago, it was probably the opposite. Alpha geeks like being credited for doing hard stuff, solving impossible puzzle, and making code run faster then you could think possible. But Rails is so standardized now, there’s little place for innovation. So new coders to make a name in that community by changing the game again. It was easy before, nothing was there, everything was to be done!

Congratulations because all the idiots who paid ThoughtWorks 6x times salary for junior ass wipes got taken and simply paid to train ThoughtWorks’ new crew

And maybe ThoughWorks is taking advantage of Rails popularity, but it will happen for other cool framework/language too if they get some day to that level of popularity. There’s no reason to shit on the Rails community for this.

After revitalizing myself, getting out of the Rails business (or trying), distancing myself from Mongrel, disconnecting from the Ruby community

Is this the end of Mongrel ?

Zed rant was pretty excessive and even though he claims that everything he writes on his blog is an act, I’m sure he’ll regret this as this will get quoted out of that so-called humorous context.

Strange way to start the new year!

What’s your thoughts on this ?

Next Page »


RefactorMyCode.comrefactorer Recommend Me

Top Clicks

  • None
Top Blogues page counter Add to Technorati Favorites

Blog Stats

  • 84,394 hits