Live data from Hacker News

"DCI" in Ruby is completely broken

tonyarcieri.com

11–20 of 67 posts

Re: "DCI" in Ruby is completely broken

#11
post #6

Erm what? Changing the class hierarchy invalidates all method caches? That sounds a bit brutal, and totally unnecessary. Can't you just have a method cache per metaclass (or class; I'm not very familiar with how Ruby factors this stuff)? Since mixing in a role into an instances creates a new metaclass anyway, there's no need to affect instances of the original class.

It's because MRI's implementation is simple. They haven't bothered caching per class because it takes more code. However JRuby caches per class.

Re: "DCI" in Ruby is completely broken

#12

Ruby is a language, not an implementation. DCI in ruby seems just fine, but all implementations are not friendly to the dynamic mixin hell hole.

Completely agreed, the title is a red herring and serves to dissuade people from giving DCI a try. "It's too slow" is not an argument against DCI, it's an argument for fixing a slow implementation of #extend or using a different method.

Why not do DCI with delegation instead of mixins?

Re: "DCI" in Ruby is completely broken

#13
post #6

Erm what? Changing the class hierarchy invalidates all method caches? That sounds a bit brutal, and totally unnecessary. Can't you just have a method cache per metaclass (or class; I'm not very familiar with how Ruby factors this stuff)? Since mixing in a role into an instances creates a new metaclass anyway, there's no need to affect instances of the original class.

It's because MRI's implementation is simple. They haven't bothered caching per class because it takes more code. However JRuby caches per class.

If you have to go and look up the method again (even ifcached) because the callsite cache is invalid then you already lost. Constantly invalidating the callsites is the real problem with this pattern.

Re: "DCI" in Ruby is completely broken

#14
post #8

I'm not a Ruby programmer, so maybe I'm misunderstanding the article -- are we talking about changing an object's class hierarchy after it's already been instantiated and then complaining that it's the performance aspect that is pathological? That sounds like a completely insane way of writing software to me.

Yes, that is what the author is talking about. I'm not sure what his point is though. This fact has been known for years and if you care about performance, you simply don't modify the class hierarchy.

It doesn't seem to be widely acknowledged amongst people promoting this particular style of DCI as a good way to write Rails app, so I'd say the point is valid, even if the title is overstated. There are also quite a few other very popular gems that make heavy use of #extend at runtime for non-DCI purposes, including Haml and Paperclip, so I don't think it's fair to say that this is common knowledge (at least in the Ruby community).

Re: "DCI" in Ruby is completely broken

#15
I've had similar experiences profiling Ruby code, replacing dynamic constructions with static ones, and seeing orders of magnitude bumps in performance.

One thing I've learned never to do is to "customize" short-lived objects at runtime with new methods. If you create an object in response to user input, it should be born with all the methods it's going to need.

Re: "DCI" in Ruby is completely broken

#16
I was curious what Tony's graph would look like using a SimpleDelegator as he suggested.

    (using ruby 1.9.3p194)
    Calculating -------------------------------------
             without dci     68507 i/100ms
                with dci     24409 i/100ms
          with delegator     46945 i/100ms
    -------------------------------------------------
             without dci  2240202.4 (±3.2%) i/s -   11235148 in   5.020463s
                with dci   412445.3 (±3.7%) i/s -    2074765 in   5.037113s
          with delegator  1018928.4 (±1.6%) i/s -    5117005 in   5.023310s

RUBY:

    require 'rubygems'
    require 'benchmark/ips'
    require 'delegate'

    class ExampleClass
      def foo; 42; end
    end

    module ExampleMixin
      def foo; 43; end
    end

    class ExampleProvisioner 

Re: "DCI" in Ruby is completely broken

#18
post #5

Ruby is a language, not an implementation. DCI in ruby seems just fine, but all implementations are not friendly to the dynamic mixin hell hole.

While this is true, it currently has pathological performance on all major Ruby implementations.

When you say "it" you mean using extend, not DCI in general, right?

Re: "DCI" in Ruby is completely broken

#19

I was curious what Tony's graph would look like using a SimpleDelegator as he suggested. (using ruby 1.9.3p194) Calculating ------------------------------------- without dci 68507 i/100ms with dci 24409 i/100ms with delegator 46945 i/100ms ------------------------------------------------- without dci 2240202.4 (±3.2%) i/s - 11235148 in 5.020463s with dci 412445.3 (±3.7%) i/s - 2074765 in 5.037113s with delegator 1018…

Which is pretty much what you'd expect... making two objects instead of one cuts the speed approximately in half.

Re: "DCI" in Ruby is completely broken

#20
post #5

Earlier quoted context omitted.

While this is true, it currently has pathological performance on all major Ruby implementations.

When you say "it" you mean using extend, not DCI in general, right?

Yes, hence why I was constantly calling it "DCI" in quotes
Post reply on HN