CS312 - Spring 2012 - Class 18
profilers
- profiler (aka code profilers) are a way for understanding what your program is doing
- in particular where it is spending most of its time
- where it is allocating the most memory
- etc.
- a profiling program runs your code and monitors where your program spends its time, relative to the code
- almost all languages have profilers of varying quality and complexity
- if your programming is taking too long or using too much memory, a profiler is a good place to start
ruby profiling example
- ruby has a built in profiler (though many external ones also exist)
- to run it, you add "-r profile" after the ruby call, e.g.:
ruby -r profile test.rb
- look at test.rb in the
ProfileExamples code
- if we run this program, where will it spend the majority of its time?
- we could guess at it, but let's run the profiler instead
- after running we get a lot of output:
% cumulative self self total
time seconds seconds calls ms/call ms/call name
46.54 4.44 4.44 502502 0.01 0.01 BasicObject#!=
38.05 8.07 3.63 1000 3.63 9.44 LinkedList#add
14.47 9.45 1.38 503502 0.00 0.00 BasicObject#==
0.31 9.48 0.03 2 15.00 30.00 LinkedList#each
0.31 9.51 0.03 1001 0.03 0.03 Class#new
0.21 9.53 0.02 1 20.00 40.00 Integer#times
0.10 9.54 0.01 1000 0.01 0.02 LinkedList#remove
0.00 9.54 0.00 1 0.00 0.00 Float#/
0.00 9.54 0.00 1 0.00 0.00 Kernel.require_relative
0.00 9.54 0.00 1000 0.00 0.00 Node#initialize
0.00 9.54 0.00 1 0.00 0.00 Module#attr_reader
0.00 9.54 0.00 1 0.00 0.00 Module#attr_accessor
0.00 9.54 0.00 1 0.00 9440.00 Integer#upto
0.00 9.54 0.00 1000 0.00 0.00 Float#+
0.00 9.54 0.00 1001 0.00 0.00 Fixnum#+
0.00 9.54 0.00 12 0.00 0.00 Module#method_added
0.00 9.54 0.00 1 0.00 0.00 BasicObject#initialize
0.00 9.54 0.00 1 0.00 30.00 Object#average
0.00 9.54 0.00 2 0.00 0.00 Float#to_s
0.00 9.54 0.00 4 0.00 0.00 IO#write
0.00 9.54 0.00 2 0.00 0.00 IO#puts
0.00 9.54 0.00 2 0.00 0.00 Kernel.puts
0.00 9.54 0.00 1000 0.00 0.00 Array#<<
0.00 9.54 0.00 1 0.00 0.00 Array#sort
0.00 9.54 0.00 2 0.00 0.00 Array#length
0.00 9.54 0.00 2 0.00 0.00 Fixnum#/
0.00 9.54 0.00 1 0.00 0.00 Fixnum#even?
0.00 9.54 0.00 2 0.00 0.00 Array#[]
0.00 9.54 0.00 1 0.00 0.00 Fixnum#-
0.00 9.54 0.00 1 0.00 30.00 Object#median
0.00 9.54 0.00 2 0.00 0.00 Class#inherited
0.00 9.54 0.00 4 0.00 0.00 IO#set_encoding
0.00 9.54 0.00 1 0.00 9540.00 #toplevel
- each row in the output is a particular function (operators count as functions in ruby)
- the output is sorted from most expensive to least
- % time is the overall percentage of the program execution that was spent in this function
- cumulative seconds is the total time spent doing this function AND ALL ABOVE
- self-seconds is the total time spent in this funcion
- calls is the number of calls
- self ms/call is the time per call
- looking at this where is the most time being spent?
profile data
- different profilers give you differing amounts of information
- for this one, all we have are the different functions
- some, will give you a line-by-line allotment and will allow you to drill down into the data
- BasicObject#!= and BasicObject#== are just checking for inequality/equality
- LinkedList#add seems to be using a lot of time relative to everything else
- this could be a hint to look into this more
- if we look into it, we see that whoever implemented this, is added to the tail end of the linked list, but without a tail pointer!
- every time the code has to traverse the linked list
- if we change this to add to the front (or add a tail pointer) then the performance changes drastically
how do profilers work?
- a number of different approaches
- sampling: sample the program regularly and see where the program is and tally based on this
- instrumentation: modify the source code to put in calls to profile code
look at faster.rb in
ProfileExample code
- which is faster, test1 or test2?
- use the ruby profiler to figure it out