Ok, so there seems to be three basic methods of doing this, the most hairy
involves using class_eval to interpose an aspect on function call
and return.
Cute.
Scary.
Anywhoo, the other two are basically to make the invariant method empty or
to only invoke the invariant method if a global or const is true....
require 'benchmark'
DEBUG = false
$foo = false
class A
attr_reader :inv
alias_method :in_very_ant, :object_id
def invariant
end
def very
in_very_ant
in_very_ant
end
def empty_method
invariant
invariant
end
def if_const
invariant if DEBUG
invariant if DEBUG
end
def if_global
invariant if $foo
invariant if $foo
end
def attr_thing
inv
inv
end
def no_code
end
end
n = 1000000
a = A.new
Benchmark.benchmark(" "*7 + Benchmark::CAPTION, 7, Benchmark::FMTSTR, ">total:", ">avg:") do |x|
tf = x.report("empty_method:") { n.times do ; a.empty_method; end }
tt = x.report("if_const:") { n.times do ; a.if_const ; end }
tg = x.report("if_global:") { n.times do ; a.if_const ; end }
tu = x.report("no_code:") { n.times do ; a.no_code ; end }
tv = x.report("inv:") { n.times do ; a.attr_thing ; end }
tw = x.report("very:") { n.times do ; a.very ; end }
x.report("nothin:") { n.times do ; a ; end }
[tf+tt+tg+tu+tv+tw, (tf+tt+tg+tu+tv+tw)/6]
end
The results are using Ruby 1.8.2...
ruby-1.8.2 binch.rb;ruby binch.rb
user system total real
empty_method: 1.210000 0.000000 1.210000 ( 1.218951)
if_const: 1.010000 0.000000 1.010000 ( 1.016450)
if_global: 1.020000 0.000000 1.020000 ( 1.021371)
no_code: 0.580000 0.000000 0.580000 ( 0.584446)
inv: 0.850000 0.000000 0.850000 ( 0.852560)
very: 0.870000 0.000000 0.870000 ( 0.863922)
nothin: 0.260000 0.010000 0.270000 ( 0.261910)
>total: 5.540000 0.000000 5.540000 ( 5.557700)
>avg: 0.923333 0.000000 0.923333 ( 0.926283)
And a fairly recent version from CVS....
user system total real
empty_method: 1.160000 0.000000 1.160000 ( 1.159409)
if_const: 0.950000 0.000000 0.950000 ( 0.953207)
if_global: 0.970000 0.000000 0.970000 ( 0.972160)
no_code: 0.570000 0.000000 0.570000 ( 0.569933)
inv: 0.790000 0.000000 0.790000 ( 0.789721)
very: 0.800000 0.000000 0.800000 ( 0.796247)
nothin: 0.260000 0.000000 0.260000 ( 0.266906)
>total: 5.240000 0.000000 5.240000 ( 5.240677)
>avg: 0.873333 0.000000 0.873333 ( 0.873446)
notes:
1) I was surprised to find the if const and if global so close. Obviously
no constant folding is being done on branches.
2) Of course, no code is faster than no code. So the hairy class_eval
tricks would be faster. Although not tremendously faster than the
attr_reader trick.
3) Ruby 1.9 is noticably faster than 1.8.2
Thanks for all the responses and wild creativity.
4) Ruby-contract, Awesome and ruby-dbc are very very very scary modules.
I'm not sure I can cope with anything quite that clever near my code. It
has all (and more, much more) the horror of the C preprocessor, without
the gcc -E option to see the what it actually did.
John Carter Phone : (64)(3) 358 6639
Tait Electronics Fax : (64)(3) 359 4632
PO Box 1645 Christchurch Email : john.carter / tait.co.nz
New Zealand
Carter's Clarification of Murphy's Law.
"Things only ever go right so that they may go more spectacularly wrong later."
From this principle, all of life and physics may be deduced.