Monthly Archives: March 2007

Ruby: Date / Rational / Fixnum#gcd hack increased app performance by 15%

UPDATE: Fixnum#gcd was accepted int MRI 1.8: See http://devdriven.com/2010/02/ruby-fixnumgcd-accepted-into-mri/ .

Ruby Date uses Rational heavily, which calls Integer#gcd for every new Rational. The Integer#gcd method is generic to handle Bignums, but performs terribly for Fixnum#gcd(Fixnum), which is probably the most often case.

This RubyInline hack saved 15% execution time in a large Rails application:

require 'inline'

class Fixnum
  inline do | builder |
    builder.c_raw '
    static
    VALUE 
    gcd(int argc, VALUE *argv, VALUE self) {
      if ( argc != 1 ) {
        rb_raise(rb_eArgError, "wrong number of arguments (%d for %d)",
                 argc, 1);
      }
      /* Handle Fixnum#gcd(Fixnum) case directly. */
      if ( FIXNUM_P(argv[0]) ) {
        /* fprintf(stderr, "Using Fixnum#gcd(Fixnum)\n"); */
        long a = FIX2LONG(self);
        long b = FIX2LONG(argv[0]);
        long min = a < 0 ? - a : a;
        long max = b < 0 ? - b : b;
        while ( min > 0 ) {
          int tmp = min;
          min = max % min;
          max = tmp;
        }
        return LONG2FIX(max);
      } else {
        /* fprintf(stderr, "Using super#gcd\n"); */
        return rb_call_super(1, argv);
      }
    }
    '
  end
end

Update:

Sorry for the late reply. If the code above does not work via cut-and-paste, download it from here.

This will be released soon as a gem dynamic library called speedfreaks,
with other performance-enhancing snippets.

Thanks for the feedback!