On Tue, Sep 18, 2007 at 01:45:58PM -0700, Ezra Zygmuntowicz wrote:
> In the big picture of a  
> merb dispatch the current routers compiled_statement is not even  
> close to being a significant slice of the time spent dispatching a  
> request.

Good point.

I'm not sure what the current best way to profile merb is, so I hacked
something very simple:

--- /usr/lib/ruby/gems/1.8/gems/merb-0.4.0/lib/merb/server.rb.orig      
2007-09-19 08:43:28.000000000 +0100
+++ /usr/lib/ruby/gems/1.8/gems/merb-0.4.0/lib/merb/server.rb   2007-09-19 
08:47:03.000000000 +0100
@@ -439,6 +439,8 @@
           start(@@merb_opts[:port])
         else
           trap('TERM') { exit }
+          trap("USR1") { require 'profiler'; Profiler__::start_profile }
+          trap("USR2") { Profiler__::stop_profile; 
Profiler__::print_profile($stderr) }
           mongrel_start(@@merb_opts[:port])
         end

I tested with the following app, run in production mode (merb -e production):

Merb::Router.prepare do |r|
  r.resources :foos
  r.resources :bars
  r.resources :bazs
  r.default_routes
end

class Hello < Application
  def world
    "Hello, world!"
  end
end

I then hit it 100 times on /hello/world, sent -USR1, hit it 100 times on
/hello/world again, and sent -USR2.

Here are the results:

  %   cumulative   self              self     total
 time   seconds   seconds    calls  ms/call  ms/call  name
  5.41     0.84      0.84     1000     0.84     6.98  Logger#add
  5.15     1.64      0.80      900     0.89     4.10  MonitorMixin.synchronize
  3.41     2.17      0.53      900     0.59     0.79  
Logger::Formatter#format_datetime
  3.03     2.64      0.47      900     0.52     1.78  MonitorMixin.mon_exit
  2.83     3.08      0.44      900     0.49     0.68  MonitorMixin.mon_release
  2.25     3.43      0.35      900     0.39     0.43  Logger::Formatter#msg2str
  2.12     3.76      0.33      900     0.37     0.40  
Logger::LogDevice#check_shift_log
  2.12     4.09      0.33      900     0.37     0.74  MonitorMixin.mon_enter
  2.06     4.41      0.32       99     3.23   139.39  TCPServer#accept
  2.00     4.72      0.31      100     3.10     4.80  Merb::Router#match
  1.93     5.02      0.30      100     3.00   137.10  MerbHandler#process
  1.93     5.32      0.30     3877     0.08     0.15  Hash#[]
  1.93     5.62      0.30      900     0.33     1.77  Logger::Formatter#call
  1.87     5.91      0.29      400     0.73     1.30  Hash#inspect
  1.61     6.16      0.25     1900     0.13     0.14  Time#now
  1.42     6.38      0.22      900     0.24     0.31  
MonitorMixin.mon_check_owner
  1.42     6.60      0.22     3600     0.06     0.06  Thread#critical=
  1.35     6.81      0.21      900     0.23     2.02  Logger#format_message
  1.35     7.02      0.21      400     0.52     0.52  String#split
  1.29     7.22      0.20      100     2.00   148.80  
Mongrel::HttpServer#process_client
  1.29     7.42      0.20      700     0.29     0.33  Mash#convert_key
  1.29     7.62      0.20      900     0.22     0.24  MonitorMixin.mon_acquire
  1.22     7.81      0.19      300     0.63     1.37  Hash#each_pair
  1.22     8.00      0.19      300     0.63     0.73  Mongrel::HeaderOut#[]=
  1.16     8.18      0.18     2200     0.08     0.10  String#%
  1.16     8.36      0.18      200     0.90     2.90  
Mongrel::DirHandler#can_serve
  1.09     8.53      0.17     1800     0.09     0.09  Array#shift
  1.03     8.69      0.16      600     0.27     0.62  
Class#read_inheritable_attribute
  0.97     8.84      0.15      200     0.75     0.75  File#expand_path
  0.97     8.99      0.15     1600     0.09     0.65  Class#new
  0.97     9.14      0.15      100     1.50     2.70  
Mongrel::HttpRequest#initialize
  0.97     9.29      0.15      100     1.50    88.80  Merb::Dispatcher#handle
  0.97     9.44      0.15      200     0.75     1.95  Merb::Request#query_parse
  0.90     9.58      0.14     1800     0.08     0.08  Hash#default
  0.90     9.72      0.14       78     1.79    19.87  Kernel.sleep
  0.84     9.85      0.13      800     0.16     0.96  
Merb::Request#route_params  0.77     9.97      0.12      200     0.60     0.80  
Mash#default
  0.77    10.09      0.12      800     0.15     0.78  Merb::Request#route_match
  0.71    10.20      0.11      600     0.18     0.18  
Class#inheritable_attributes
  0.71    10.31      0.11      900     0.12     0.17  Logger#format_severity
  0.64    10.41      0.10     1800     0.06     0.06  NilClass#nil?
  0.64    10.51      0.10     1000     0.10     0.10  String#inspect
  0.64    10.61      0.10     1300     0.08     0.08  Fixnum#==
  0.64    10.71      0.10      900     0.11     0.11  Thread#pass
  0.64    10.81      0.10      300     0.33     4.93  Merb::Request#params
  0.64    10.91      0.10      106     0.94     1.60  Array#map
  0.64    11.01      0.10      100     1.00     1.50  Merb::Request#path
  0.64    11.11      0.10      200     0.50     0.70  
Mongrel::HttpRequest#unescape
  0.58    11.20      0.09      300     0.30     0.57  
Merb::Request#controller_name
  0.58    11.29      0.09      400     0.23     1.35  
Merb::AbstractController#before_filters
  0.58    11.38      0.09      100     0.90     4.00  Kernel.catch
  0.58    11.47      0.09      753     0.12    18.46  Array#each
  0.58    11.56      0.09     1200     0.08     0.08  Kernel.respond_to?
  0.58    11.65      0.09      900     0.10     4.29  Logger::LogDevice#write
  0.58    11.74      0.09     1300     0.07     0.07  IO#write
  0.52    11.82      0.08      100     0.80     1.20  String#to_const_string
  0.52    11.90      0.08      400     0.20     0.25  Mash#convert_value
  0.52    11.98      0.08      100     0.80     0.80  Thread#initialize
  0.52    12.06      0.08      400     0.20     0.28  
Mongrel::HttpResponse#write
  0.52    12.14      0.08      400     0.20     0.52  
Merb::Controller#_session_id_key
  0.52    12.22      0.08      100     0.80     5.60  
Merb::Request#controller_class
  0.52    12.30      0.08     1453     0.06     0.06  Kernel.nil?
  0.45    12.37      0.07      400     0.18     0.88  
Merb::AbstractController#after_filters
  0.45    12.44      0.07     1800     0.04     0.04  Module#===
  0.45    12.51      0.07      100     0.70     0.70  Fixnum#>=
  0.45    12.58      0.07      300     0.23     0.33  Array#include?
  0.45    12.65      0.07      900     0.08     7.90  Logger#info
  0.45    12.72      0.07      800     0.09     0.09  Symbol#inspect
  0.45    12.79      0.07      200     0.35     1.00  
Mongrel::HttpResponse#send_status
  0.45    12.86      0.07     1000     0.07     0.07  Time#strftime
  0.39    12.92      0.06     1000     0.06     0.06  String#<<
  0.39    12.98      0.06      100     0.60     1.80  Hash#each
  0.39    13.04      0.06      100     0.60     2.80  
Mongrel::HttpResponse#initialize
  0.39    13.10      0.06      200     0.30     0.35  Enumerable.inject
  0.39    13.16      0.06      200     0.30     1.30  
Merb::Request#body_and_query_params
  0.39    13.22      0.06      300     0.20     0.20  Hash#key?
  0.39    13.28      0.06      100     0.60     2.50  Object#full_const_get
  0.39    13.34      0.06     1100     0.05     0.05  Fixnum#-
  0.39    13.40      0.06      100     0.60     7.00  
Merb::AbstractController#dispatch
  0.39    13.46      0.06     1800     0.03     0.03  Thread#current
  0.32    13.51      0.05      100     0.50     1.30  
Merb::CookieStore#initialize
  0.32    13.56      0.05      100     0.50     0.70  
Mongrel::HttpParser#execute
  0.32    13.61      0.05      100     0.50     2.80  
Merb::Controller#set_dispatch_variables
  0.32    13.66      0.05      200     0.25     0.80  Hash#to_mash
  0.32    13.71      0.05      200     0.25     0.40  Mash#initialize
  0.32    13.76      0.05      100     0.50    39.90  Merb::Controller#dispatch
  0.26    13.80      0.04      400     0.10     0.20  
Merb::Request#content_type  0.26    13.84      0.04      600     0.07     0.07  
String#==
  0.26    13.88      0.04      100     0.40     1.00  Time#httpdate
  0.26    13.92      0.04      100     0.40     1.30  
Merb::Request#query_params  0.26    13.96      0.04      200     0.20     0.20  
String#tr
  0.26    14.00      0.04     1100     0.04     0.04  Fixnum#<
  0.26    14.04      0.04      100     0.40     0.40  
Merb::AbstractController#default_thrown_content
  0.26    14.08      0.04     1100     0.04     0.04  Array#[]
... snip everything below 0.25%
  0.00    15.53      0.00        1     0.00 15530.00  #toplevel

It looks like logging is the worst offender, with Logger#add and its mutex
(MonitorMixin) accounting for the top 8 slots, 4.09 out of 15.53 seconds, or
26%. In fact, it looks like Logger#add and its children, at 6.98ms per call
and called 1000 times, count for 6.98s in total.

TCPAccept is next, and Merb::Router#match is third. However this only
accounts for 2.00%

Now, I've seen figures for Merb performance typically in the 400-600
requests per second range. Are these measured with logging turned off? If
not, I think there is a strong argument for introducing a mutex-free logging
system.

(Simple idea: replace Logger#add with an append to a string buffer, which is
atomic. Then once per second empty the buffer using String#slice! which
should also be atomic, and write that to the log)

Finally, I repeated the test using merb -e production -l WARN

  %   cumulative   self              self     total
 time   seconds   seconds    calls  ms/call  ms/call  name
  4.62     0.42      0.42     1000     0.42     0.51  Logger#add
  3.85     0.77      0.35      100     3.50     6.00  Merb::Router#match
  3.74     1.11      0.34      100     3.40    10.30  IO#close
  3.19     1.40      0.29      100     2.90    79.70  
Mongrel::HttpServer#process_client
  3.19     1.69      0.29      100     2.90    71.40  MerbHandler#process
  2.75     1.94      0.25      900     0.28     0.81  Logger#info
  2.42     2.16      0.22     1600     0.14     0.86  Class#new
  2.31     2.37      0.21      600     0.35     0.50  
Class#read_inheritable_attribute
  2.09     2.56      0.19      400     0.48     0.73  Hash#inspect
  2.09     2.75      0.19      400     0.47     0.47  String#split
  2.09     2.94      0.19     3742     0.05     0.11  Hash#[]
  1.98     3.12      0.18      738     0.24    10.15  Array#each
  1.98     3.30      0.18      100     1.80     2.00  Time#httpdate
  1.65     3.45      0.15      400     0.38     1.23  
Merb::AbstractController#before_filters
  1.65     3.60      0.15      300     0.50     5.93  Merb::Request#params
  1.65     3.75      0.15      100     1.50     1.90  Merb::Request#json_params
  1.54     3.89      0.14      100     1.40     2.10  
Mongrel::HttpRequest#initialize
  1.54     4.03      0.14     1000     0.14     0.17  Time#now
  1.43     4.16      0.13      200     0.65     1.40  Hash#to_mash
  1.43     4.29      0.13      100     1.30    55.30  Merb::Dispatcher#handle
  1.32     4.41      0.12      100     1.20    17.30  Merb::Controller#dispatch
  1.32     4.53      0.12      700     0.17     0.24  Mash#convert_key
  1.32     4.65      0.12      300     0.40     0.80  Mongrel::HeaderOut#[]=
  1.21     4.76      0.11      100     1.10     3.90  Merb::Controller#build
  1.10     4.86      0.10      800     0.13     1.03  
Merb::Request#route_params  1.10     4.96      0.10      200     0.50     0.55  
Mongrel::HttpResponse#send_header
  0.99     5.05      0.09      300     0.30     1.00  Hash#each_pair
  0.99     5.14      0.09      100     0.90     1.10  Merb::Request#method
  0.99     5.23      0.09       55     1.64    24.73  Kernel.sleep
  0.88     5.31      0.08      200     0.40     0.40  File#expand_path
  0.88     5.39      0.08      400     0.20     0.20  String#%
  0.88     5.47      0.08      200     0.40     0.70  Mash#initialize
  0.88     5.55      0.08      200     0.40     0.55  Mash#include?
  0.77     5.62      0.07      100     0.70     6.10  
Merb::AbstractController#dispatch
  0.77     5.69      0.07      100     0.70     4.90  
Mongrel::HttpResponse#initialize
  0.77     5.76      0.07      200     0.35     1.20  
Mongrel::DirHandler#can_serve
  0.77     5.83      0.07     1800     0.04     0.04  Hash#default
  0.77     5.90      0.07      800     0.09     0.85  Merb::Request#route_match
  0.77     5.97      0.07      100     0.70     1.60  
Merb::Controller#set_dispatch_variables
  0.66     6.03      0.06      400     0.15     0.62  
Merb::AbstractController#after_filters
  0.66     6.09      0.06      100     0.60     0.60  StringScanner#scan
  0.66     6.15      0.06      100     0.60     0.70  Object#blank?
  0.66     6.21      0.06      200     0.30     2.25  Merb::Request#query_parse
  0.66     6.27      0.06      200     0.30     0.30  String#gsub
  0.66     6.33      0.06      400     0.15     0.68  
Merb::Controller#_session_id_key
  0.66     6.39      0.06     1438     0.04     0.04  Kernel.nil?
  0.66     6.45      0.06     1000     0.06     0.06  Hash#[]=
  0.55     6.50      0.05       99     0.51    52.63  TCPServer#accept
  0.55     6.55      0.05      200     0.25     1.40  Merb::Request#cookies
  0.55     6.60      0.05      900     0.06     0.06  Array#last
  0.55     6.65      0.05      100     0.50     1.20  
Mongrel::HttpResponse#finished
  0.55     6.70      0.05      100     0.50     5.70  
Merb::SessionMixin.setup_session
  0.55     6.75      0.05       76     0.66     1.18  Array#map
  0.55     6.80      0.05      400     0.13     0.22  Mash#convert_value
  0.55     6.85      0.05      300     0.17     0.23  StringIO#initialize
  0.55     6.90      0.05      400     0.13     0.22  
Mongrel::HttpResponse#write
  0.55     6.95      0.05      400     0.13     0.27  
Merb::Request#content_type  0.55     7.00      0.05      100     0.50     4.70  
Merb::Request#controller_class
  0.55     7.05      0.05      100     0.50     2.60  Object#full_const_get
  0.55     7.10      0.05      200     0.25     0.25  String#to_s
  0.55     7.15      0.05      100     0.50     1.00  Thread#new
  0.55     7.20      0.05      100     0.50     0.50  Merb::Controller#status
... snip everything below 0.5%
  0.00     9.09      0.00        1     0.00  9090.00  #toplevel

This is more interesting: even though nothing is actually logged, because of
the large number of calls to Logger#add it still accounts for 4.62%, with
another 2.75% for Logger#info and 1.54% for Time#now (the fact it has been
called 1000 times suggests that timestamps are being made for log entries
which are never written!)

Merb::Router#match is now the number two offender at 3.85%, but this is
probably still not enough by itself to warrant a rewrite.

Regards,

Brian.
_______________________________________________
Merb-devel mailing list
[email protected]
http://rubyforge.org/mailman/listinfo/merb-devel

Reply via email to