Feature #8119

more efficient version of Rinda::TupleSpaceProxy.take

Added by Joel VanderWerf about 1 year ago. Updated about 1 year ago.

[ruby-core:53512]
Status:Closed
Priority:Normal
Assignee:Eric Hodel
Category:lib
Target version:-

Description

=begin

The purpose of Rinda::TupleSpaceProxy is to avoid losing tuples when a client disconnects during a #take call. This is implemented by sending the result value ((twice)): first by pushing it to a client-side array, second by returning the result as a DRb response. If the first fails, then the #take is aborted, so that the tuple is not lost. In case of success, the client only uses the pushed value, not the response value.

This involves a total of ((three)) marshal operations by DRb: the push argument, the push return value (which is an array containing the push argument), and the #take return value. Only the first is necessary.

The following patch adds Rinda::TupleSpaceProxy#take_fast, which avoids the two redundant marshal operations. The unit tests in the ruby source pass when calling this method instead of #take.

The improvement is small when the object is simple. However, for complex objects, eliminating the redundant marshalling reduces network traffic and increases speed by a factor of 2. See the attached bench.rb.

diff --git a/rinda/rinda.rb b/rinda/rinda.rb
index 18e284a..5d280f4 100644
--- a/rinda/rinda.rb
+++ b/rinda/rinda.rb
@@ -206,6 +206,13 @@ module Rinda
# TupleSpaceProxy allows a remote Tuplespace to appear as local.

 class TupleSpaceProxy
  • class Port
  • attr_reader :val
  • def push val
  • @val = val
  • nil # so that val doesn't get marshalled again
  • end
  • end

    ##
    # Creates a new TupleSpaceProxy to wrap +ts+.
    @@ -223,6 +230,17 @@ module Rinda

    ##
    # Takes +tuple+ from the proxied TupleSpace. See TupleSpace#take.

  • # This is sometimes a bit faster than #take bacause it uses a version

  • # of TupleSpace#move that never marshals the result more than once.
    +

  • def take_fast(tuple, sec=nil, &block)

  •  port = Port.new
    
  •  @ts.move_fast(DRbObject.new(port), tuple, sec, &block)
    
  •  port.val
    
  • end
    +

  • ##

  • # Takes +tuple+ from the proxied TupleSpace. See TupleSpace#take.

    def take(tuple, sec=nil, &block)
    port = []
    diff --git a/rinda/tuplespace.rb b/rinda/tuplespace.rb
    index ba494aa..042c605 100644
    --- a/rinda/tuplespace.rb
    +++ b/rinda/tuplespace.rb
    @@ -480,6 +480,43 @@ module Rinda
    end

    ##

  • # Moves +tuple+ to +port+, returning nil
    +

  • def move_fast(port, tuple, sec=nil)

  •  template = WaitTemplateEntry.new(self, tuple, sec)
    
  •  yield(template) if block_given?
    
  •  synchronize do
    
  •    entry = @bag.find(template)
    
  •    if entry
    
  •      port.push(entry.value)
    
  •      @bag.delete(entry)
    
  •      notify_event('take', entry.value)
    
  •      return nil
    
  •    end
    
  •    raise RequestExpiredError if template.expired?
    

    +

  •    begin
    
  •      @take_waiter.push(template)
    
  •      start_keeper if template.expires
    
  •      while true
    
  •        raise RequestCanceledError if template.canceled?
    
  •        raise RequestExpiredError if template.expired?
    
  •        entry = @bag.find(template)
    
  •        if entry
    
  •          port.push(entry.value)
    
  •          @bag.delete(entry)
    
  •          notify_event('take', entry.value)
    
  •          return nil
    
  •        end
    
  •        template.wait
    
  •      end
    
  •    ensure
    
  •      @take_waiter.delete(template)
    
  •    end
    
  •  end
    
  • end
    +

  • ##
    # Moves +tuple+ to +port+.

    def move(port, tuple, sec=nil)

=end

bench.rb Magnifier (1.14 KB) Joel VanderWerf, 03/19/2013 06:13 AM

rinda.rb.8119.patch Magnifier - Backwards compatible version of original patch (783 Bytes) Eric Hodel, 03/24/2013 11:20 AM

Associated revisions

Revision 39923
Added by Eric Hodel about 1 year ago

  • lib/rinda/tuplespace.rb: Only return tuple entry once on move, either through port or regular return, not both. This results in a 120% speedup when combined with #8125. Patch by Joel VanderWerf. [ruby-trunk - Feature #8119]

History

#1 Updated by Zachary Scott about 1 year ago

  • Status changed from Open to Assigned
  • Assignee set to Masatoshi Seki

#2 Updated by Eric Hodel about 1 year ago

  • Priority changed from Low to Normal

I think #8125 should be applied, then a new patch be generated for this issue.

#3 Updated by Eric Hodel about 1 year ago

With #8125, your benchmark is 40% faster +/- 2% at 95% confidence.

With #8119, your benchmark is ~120% faster, but the tests fail as #take calls #move with a port of nil, so local users of the TupleSpace will experience a behavior change.

I've updated your patch to have the same speed gains while maintaining backwards compatibility. The attached patch, compared to rinda in ruby 2.0.0p57 is 120% faster +/- 2.4% at 95% confidence.

#4 Updated by Masatoshi Seki about 1 year ago

  • Assignee changed from Masatoshi Seki to Eric Hodel

#5 Updated by Yui NARUSE about 1 year ago

Just FYI, you can use =begin ... =end for commenting out multiline

#6 Updated by Eric Hodel about 1 year ago

  • Status changed from Assigned to Closed
  • % Done changed from 0 to 100

This issue was solved with changeset r39923.
Joel, thank you for reporting this issue.
Your contribution to Ruby is greatly appreciated.
May Ruby be with you.


  • lib/rinda/tuplespace.rb: Only return tuple entry once on move, either through port or regular return, not both. This results in a 120% speedup when combined with #8125. Patch by Joel VanderWerf. [ruby-trunk - Feature #8119]

Also available in: Atom PDF