ruby-grape / grape

An opinionated framework for creating REST-like APIs in Ruby.
http://www.ruby-grape.org
MIT License
9.86k stars 1.22k forks source link

Optimize memory alloc and retained #2441

Closed ericproulx closed 2 months ago

ericproulx commented 2 months ago

This PR should improve memory allocation and retained.

Base code

class API < Grape::API
  prefix :api
  version :v1, using: :path

  namespace :test do
    2000.times do |index|
      get "/test#{index}/" do
        'hello'
      end
    end
  end
end

MemoryProfiler.report(allow_files: 'grape') do
  API.compile!
end.pretty_print

This code

branch allocated retained
master ~85.2 MB (502079 objects) ~15.74 MB (80035 objects)
this branch ~75.34MB (408047 objects) ~13.08 MB (54028 objects)
Difference ~9.96 MB (94032 objects) ~2.66 MB (26007) objects)

Real world app

branch allocated retained
master ~64.16 MB (353932 objects) ~9.02 MB (26379 objects)
this branch ~61.44 MB (329231 objects) ~5.86 MB (15271 objects)
Difference ~2.72 MB (24701 objects) ~3.17 MB (11108) objects )

Improve caching

lib/grape/namespace.rb and lib/grape/path.rb were already caching but it could be improved by adding Grape::Router.normalize_path call directly in the cache instead.

Stringify inheritable settings

prefix,namespace and other inheritable settings may be naturally declared with a symbol instead of a string and along compilation, they will be stringified and join create more complex strings. Saving them as string saves some memory allocation.

delete_if instead of -

delete_if will remove elements in the array and returns it. Substracting two arrays will create a new one. Most cases were calling keys and substracting values from its.

Replace AttributesTranslator by ActiveSupport::OrderedOptions

AttributesTranslator is basically a hash with dynamic accessor methods and it is used in lib/grape/router/route.rb and lib/grape/router/greedy_route.rb. These 2 also have an options which is the same data included in the AttributesTranslator. It didn't make sense to keep these two. So, I found that ActiveSupport::OrderedOptions is literally a hash with dynamic accessor methods and it could replace entirely AttributesTranslator. In conjunction with delegate_missing_to, I've successfully drop AttributesTranslator and just add an alias attributes to options.

Introducing base_route.rb

lib/grape/router/greedy_route.rb and lib/grape/router/route.rb are vey close in terms of code so I've created a base_route that both would inherit. It also encapsulated the regexification. I've also added some cache for the regex captures.

Some small refactors

I've refactored some small functions.

Fix #2440

dblock commented 2 months ago

This looks good. I opened https://github.com/ruby-grape/grape/issues/2442, too, might take it up if I have some time.