J
John Carter
...or when a language design level optimization is a pessimization.
Ruby allows destructive string operations. String instance methods
with a "Bang!" at the end.
Consider this code.
a = ['froot']
b=a.first
c = {"d"=>b}
Now a[0], b, c["d"] refer to _exactly_ the same string instance
So if I do a destructive operation on any of them, all are clobbered.
a.last.sub!(/oo/,"ui")
=> "fruit"
irb(main):009:0> b
=> "fruit"
irb(main):010:0> c
=> {"d"=>"fruit"}
Traditionally destructive ops have been allowed in languages such as
Lisp etc. as an optimization. You don't have to "new" a new object
instance if you don't want to.
The other day I was optimizing my code, when I decided to hunt
unnecessary object allocation.
I used my MemoryProfiler snippet to find that String's were by far the
most common object I was generating.
http://rubyforge.org/snippet/detail.php?type=snippet&id=70
So I extended that to find _which_ was the most common string I was
generating.
def MemoryProfile::string_duplicates
Dir.chdir "/tmp"
ObjectSpace::garbage_collect
sleep 10 # Give the GC thread a chance
tally = Hash.new(0)
ObjectSpace.each_object do |obj|
next if obj.class != String
tally[obj]+=1
end
open( LOG_FILE, 'a') do |outf|
outf.puts '='*70
outf.puts "
String Duplicates report for #{$0}
"
tally.keys.find_all{|s| tally > 1}.sort_by{|s| tally}.each do |s|
outf.puts "#{s}\t#{tally}"
end
end
end
The answer, by a long shot, was "U".
Somewhere in my code I had the line
symbols_needed[symbol_name] = 'U'
I could replace that with the symbol :U, but other places that had
Good Reasons of using strings would break.
Now I have a class CONSTANT...
UNDEFINED = 'U'.freeze
and
symbols_needed[symbol_name] = UNDEFINED
Of course, if anywhere I apply a destructive op to one of those
thousands of references, my code will die.
Bit at least the "freeze" will cause a loud and messy death, not a
subtle and hidden bug.
So as I said at the start, the optimization to allow the occasional
destructive op to a string... can be a pessimization in every case where
you assign a string literal.
a= "froot"
=> "froot"
irb(main):002:0> a.object_id
=> -605331808
irb(main):003:0> a= "froot"
=> "froot"
irb(main):004:0> a.object_id
=> -605352198
John Carter Phone : (64)(3) 358 6639
Tait Electronics Fax : (64)(3) 359 4632
PO Box 1645 Christchurch Email : (e-mail address removed)
New Zealand
Ruby allows destructive string operations. String instance methods
with a "Bang!" at the end.
Consider this code.
a = ['froot']
b=a.first
c = {"d"=>b}
Now a[0], b, c["d"] refer to _exactly_ the same string instance
=> -605300798a[0].object_id => -605300798
b.object_id => -605300798
c["d"].object_id
So if I do a destructive operation on any of them, all are clobbered.
a.last.sub!(/oo/,"ui")
=> "fruit"
irb(main):009:0> b
=> "fruit"
irb(main):010:0> c
=> {"d"=>"fruit"}
Traditionally destructive ops have been allowed in languages such as
Lisp etc. as an optimization. You don't have to "new" a new object
instance if you don't want to.
The other day I was optimizing my code, when I decided to hunt
unnecessary object allocation.
I used my MemoryProfiler snippet to find that String's were by far the
most common object I was generating.
http://rubyforge.org/snippet/detail.php?type=snippet&id=70
So I extended that to find _which_ was the most common string I was
generating.
def MemoryProfile::string_duplicates
Dir.chdir "/tmp"
ObjectSpace::garbage_collect
sleep 10 # Give the GC thread a chance
tally = Hash.new(0)
ObjectSpace.each_object do |obj|
next if obj.class != String
tally[obj]+=1
end
open( LOG_FILE, 'a') do |outf|
outf.puts '='*70
outf.puts "
String Duplicates report for #{$0}
"
tally.keys.find_all{|s| tally
outf.puts "#{s}\t#{tally
end
end
end
The answer, by a long shot, was "U".
Somewhere in my code I had the line
symbols_needed[symbol_name] = 'U'
I could replace that with the symbol :U, but other places that had
Good Reasons of using strings would break.
Now I have a class CONSTANT...
UNDEFINED = 'U'.freeze
and
symbols_needed[symbol_name] = UNDEFINED
Of course, if anywhere I apply a destructive op to one of those
thousands of references, my code will die.
Bit at least the "freeze" will cause a loud and messy death, not a
subtle and hidden bug.
So as I said at the start, the optimization to allow the occasional
destructive op to a string... can be a pessimization in every case where
you assign a string literal.
a= "froot"
=> "froot"
irb(main):002:0> a.object_id
=> -605331808
irb(main):003:0> a= "froot"
=> "froot"
irb(main):004:0> a.object_id
=> -605352198
John Carter Phone : (64)(3) 358 6639
Tait Electronics Fax : (64)(3) 359 4632
PO Box 1645 Christchurch Email : (e-mail address removed)
New Zealand