Forum: Ruby New Optimization idea.

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
John C. (Guest)
on 2006-03-28 01:18
(Received via mailing list)
I have a couple of classes like so...

class Foo
   def step1
     @mine = Hash.new
     # Perhaps stuff things into @mine
   end

   def step2
     @mine.each_pair do |key,value|
       # Do stuff
     end
   end
end

Profiling with my "new" profiler shows that I'm creating many many Hash
objects, probably way more than I need. In fact most of them are empty.

Optimization trick...


class Object
   FROZEN_EMPTY_HASH = Hash.new.freeze
end

class Foo
   @@hash_cache = Hash.new

   def step1
     @mine = @@hash_cache
     # Perhaps stuff things into @mine
     if @mine.empty?
       @mine = FROZEN_EMPTY_HASH
     else
       @@hash_cache = Hash.new
     end
   end

   def step2
     @mine.each_pair do |key,value|
       # Do stuff
     end
   end
end


John C.                             Phone : (64)(3) 358 6639
Tait Electronics                        Fax   : (64)(3) 359 4632
PO Box 1645 Christchurch                Email : 
removed_email_address@domain.invalid
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.
Robert K. (Guest)
on 2006-03-28 23:00
(Received via mailing list)
John C. wrote:
>       # Do stuff
> class Object
>       @mine = FROZEN_EMPTY_HASH
> end
You could as well leave the member nil and change the getter
appropriately:

class Foo
   def mine() @mine || FROZEN_EMPTY_HASH end
   def step2() mine.each_pair ...
end

Or do the test in step2...
@mine and @mine.each_pair ...

Kind regards

	robert
John C. (Guest)
on 2006-03-29 01:47
(Received via mailing list)
On Wed, 29 Mar 2006, Robert K. wrote:

> You could as well leave the member nil and change the getter appropriately:
>
> class Foo
>  def mine() @mine || FROZEN_EMPTY_HASH end
>  def step2() mine.each_pair ...
> end
>
> Or do the test in step2...
> @mine and @mine.each_pair ...

I didn't want to do that since @mine.each and .has_key? was going to be
evaluated often and in many places.
ie. The optimization tweak would have been a shot gun
hack all over the place.

However, if anybody had listened to me about the Null Object pattern and
nil....

I wouldn't have had to do anything.


John C.                             Phone : (64)(3) 358 6639
Tait Electronics                        Fax   : (64)(3) 359 4632
PO Box 1645 Christchurch                Email : 
removed_email_address@domain.invalid
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.
Ryan D. (Guest)
on 2006-03-30 01:54
(Received via mailing list)
On Mar 27, 2006, at 1:18 PM, John C. wrote:

>       # Do stuff
>     end
>   end
> end
>
> Profiling with my "new" profiler shows that I'm creating many many
> Hash objects, probably way more than I need. In fact most of them
> are empty.
>
> Optimization trick...

Maybe it is just me, but I find your original code much easier to
read and intuitive than your second version. There is just too much
wonky-thought going on in the second version and it'll make it easy
to get it wrong (esp if this pattern spreads throughout a large code
base). I don't know what your real code is doing, but creating empty
hashes seems fine to me. They don't actually cost all that much:

% time ruby -e '1_000_000.times do Hash.new; end'

real    0m7.107s
user    0m3.432s
sys     0m0.101s

(with itunes running in the background no less)

One suggestion I could make is to recycle if you really are making
too many empty hashes. If you are running step1/2 in a loop and doing
a lot of such work, only reinstantiate @mine if you need to:

   def initialize
     @mine = Hash.new
   end

   def step1
     @mine = Hash.new unless @mine.empty?
     # ...
   end

That is only a teeny change to your original logic and meets your
goals of creating less hashes. I can read that and grok the intent
immediately. I can't with your rewrite.

--
_why: zenspider's most intense moments of solice are immediately
following the slaughter [...]
_why: that topknot's the only thing keeping a lid on the righteous anger
bricolage: yeah, that and his flagrant obsession with dvorak
John C. (Guest)
on 2006-03-31 10:32
(Received via mailing list)
On Thu, 30 Mar 2006, Ryan D. wrote:

> Maybe it is just me, but I find your original code much easier to read and
> intuitive than your second version. There is just too much wonky-thought
> going on in the second version and it'll make it easy to get it wrong (esp if

Like any optimization, premature, it's the root of all programming evil.
In my case profiling showed up a case where I was  starting to consume
significant non-GC'able memory resources which were slowing the system
as
a whole.

In this case it was worth it. In general, no.



John C.                             Phone : (64)(3) 358 6639
Tait Electronics                        Fax   : (64)(3) 359 4632
PO Box 1645 Christchurch                Email : 
removed_email_address@domain.invalid
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.
This topic is locked and can not be replied to.