Improve performance of Marshaler::Base#find_template

38 views
Skip to first unread message

Nash Bridges

unread,
Sep 5, 2018, 3:14:36 PM9/5/18
to transit-format
Hello guys!

According to https://github.com/cognitect/transit-ruby/blob/master/CONTRIBUTING.md I'm posting this pull request here.

In our Rails application we see Marshaler::Base#find_template as a hotspot.

The problem is that the method looks up all the inheritance chain for an encoded object:


def find_handler(obj)
  obj
.class.ancestors.each do |a|
   
if handler = @handlers[a]
     
return handler
   
end
 
end
 
nil
end


Here's Hash `ancestors` in Rails

[4] pry(main)> {}.class.ancestors
=> [ActiveSupport::ToJsonWithActiveSupportEncoder,
 
Hash,
 JSON
::Ext::Generator::GeneratorMethods::Hash,
 
Enumerable,
 
ActiveSupport::ToJsonWithActiveSupportEncoder,
 
Object,
 PP
::ObjectMixin,
 JSON
::Ext::Generator::GeneratorMethods::Object,
 
ActiveSupport::Tryable,
 
ActiveSupport::Dependencies::Loadable,
 
Kernel,
 
BasicObject]


Here's String `ancestors`:

[5] pry(main)> "".class.ancestors
=> [ActiveSupport::ToJsonWithActiveSupportEncoder,
 
String,
 JSON
::Ext::Generator::GeneratorMethods::String,
 
Comparable,
 
ActiveSupport::ToJsonWithActiveSupportEncoder,
 
Object,
 PP
::ObjectMixin,
 JSON
::Ext::Generator::GeneratorMethods::Object,
 
ActiveSupport::Tryable,
 
ActiveSupport::Dependencies::Loadable,
 
Kernel,
 
BasicObject]

Basically all core objects are prepended with ToJsonWithActiveSupportEncoder.
That makes Ruby to spend two loop iterations on each encoded key or value to get a handler for Array, Hash or String.

Performance implications are quite severe if encoded object is big enough:

# benchmark.rb

require "bundler/setup"


$LOAD_PATH
<< File.expand_path("../../lib", __FILE__)


require "transit"


require "active_support"
require "active_support/core_ext"


require "benchmark"


example
= Array.new(1_000) do
 
{
    ids
: [1, 2, 3, 4],
    translations
: {
      can
: {
        be
: {
          nested
: "value"
       
}
     
}
   
}
 
}
end


module Transit
 
module Marshaler
   
class OrigJson < Json
     
def find_handler(obj)
        obj
.class.ancestors.each do |a|
         
if handler = @handlers[a]
           
return handler
         
end
       
end
       
nil
     
end
   
end


   
class PerfJson < Json
     
def find_handler(obj)
        handler
= @handlers[obj.class]
       
return handler if handler


        obj
.class.ancestors.each do |a|
         
if handler = @handlers[a]
           
return handler
         
end
       
end
       
nil
     
end
   
end
 
end


 
class OrigWriter < Writer
   
def initialize(format, io, opts = {})
     
@marshaler = Marshaler::OrigJson.new(io, {:handlers => {},
                                             
:oj_opts => {:indent => -1}}.merge(opts))
   
end
 
end


 
class PerfWriter < Writer
   
def initialize(format, io, opts = {})
     
@marshaler = Marshaler::PerfJson.new(io, {:handlers => {},
                                             
:oj_opts => {:indent => -1}}.merge(opts))
   
end
 
end
end


original_writer
= Transit::OrigWriter.new(:json, StringIO.new)
perf_writer
= Transit::PerfWriter.new(:json, StringIO.new)


n
= 100


Benchmark.benchmark do |bm|
  puts
"original"
 
3.times do
    bm
.report do
      n
.times do
        original_writer
.write(example)
     
end
   
end
 
end


  puts
  puts
"perf"
 
3.times do
    bm
.report do
      n
.times do
        perf_writer
.write(example)
     
end
   
end
 
end
end


#original
   
#4.490000   0.000000   4.490000 (  4.505609)
   
#4.510000   0.010000   4.520000 (  4.522835)
   
#4.500000   0.010000   4.510000 (  4.515551)


#perf
   
#2.950000   0.010000   2.960000 (  2.955664)
   
#2.920000   0.000000   2.920000 (  2.934824)
   
#2.940000   0.010000   2.950000 (  2.951365)


Please, review and accept the attached patch.

find_handler.patch

Nash Bridges

unread,
Sep 6, 2018, 3:26:05 AM9/6/18
to transit-format
I realized that I should've been posted this to issues. Sorry for the noise.
https://github.com/cognitect/transit-ruby/issues/23

середа, 5 вересня 2018 р. 22:14:36 UTC+3 користувач Nash Bridges написав:
Reply all
Reply to author
Forward
0 new messages