Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reduce memory allocation on TypeName#initialize #1363

Merged

Conversation

pocke
Copy link
Member

@pocke pocke commented Jul 3, 2023

This PR improves the performance of initializing TypeName. It improves loading an environment.

This PR reduces 9% execution time.

Profiling

StackProf

code

require 'rbs'
require 'stackprof'

def subject
  loader = RBS::EnvironmentLoader.new
  RBS::Environment.from_loader(loader).resolve_type_names
end

StackProf.run(mode: :wall, out: "tmp/stackprof-environment-load-#{Time.now.to_f}") do
  40.times do
    subject
  end
end

result

$ stackprof tmp/stackprof-environment-load-1689829686.8585012 -m 'RBS::TypeName#initialize'
RBS::TypeName#initialize (/Users/kuwabara.masataka/ghq/github.com/ruby/rbs/lib/rbs/type_name.rb:9)
  samples:    61 self (1.4%)  /    185 total (4.3%)
  callers:
     102  (   55.1%)  RBS::Parser._parse_signature
      83  (   44.9%)  Class#new
  callees (124 total):
      99  (   79.8%)  Regexp#===
      19  (   15.3%)  Symbol#to_s
       6  (    4.8%)  String#[]
  code:
                                  |     9  |     def initialize(namespace:, name:)
    6    (0.1%) /     6   (0.1%)  |    10  |       @namespace = namespace
   13    (0.3%) /    13   (0.3%)  |    11  |       @name = name
   43    (1.0%) /    18   (0.4%)  |    12  |       @kind = case name.to_s[0,1]
  114    (2.6%) /    20   (0.5%)  |    13  |               when /[A-Z]/
    1    (0.0%) /     1   (0.0%)  |    14  |                 :class
    8    (0.2%) /     3   (0.1%)  |    15  |               when /[a-z]/
                                  |    16  |                 :alias

It indicates TypeName#initialize consumes 4.3% execution time.

MemoryProfiler

code

require 'rbs'
require 'memory_profiler'

def subject
  loader = RBS::EnvironmentLoader.new
  RBS::Environment.from_loader(loader).resolve_type_names
end

report = MemoryProfiler.report do
  subject
end

report.pretty_print

part of result

allocated memory by location
-----------------------------------
  16711810  /Users/kuwabara.masataka/ghq/github.com/ruby/rbs/lib/rbs/parser_aux.rb:17
   2033824  /Users/kuwabara.masataka/ghq/github.com/ruby/rbs/lib/rbs/type_name.rb:13
   1896399  /Users/kuwabara.masataka/ghq/github.com/ruby/rbs/lib/rbs/environment_loader.rb:144
    881200  /Users/kuwabara.masataka/ghq/github.com/ruby/rbs/lib/rbs/type_name.rb:12

allocated objects by location
-----------------------------------
    155956  /Users/kuwabara.masataka/ghq/github.com/ruby/rbs/lib/rbs/parser_aux.rb:17
     22030  /Users/kuwabara.masataka/ghq/github.com/ruby/rbs/lib/rbs/type_name.rb:12
     19556  /Users/kuwabara.masataka/ghq/github.com/ruby/rbs/lib/rbs/type_name.rb:13

Allocated String Report
-----------------------------------
      4162  "E"
      2083  /Users/kuwabara.masataka/ghq/github.com/ruby/rbs/lib/rbs/type_name.rb:12
      2079  /Users/kuwabara.masataka/ghq/github.com/ruby/rbs/lib/rbs/type_name.rb:13

      3775  "S"
      1887  /Users/kuwabara.masataka/ghq/github.com/ruby/rbs/lib/rbs/type_name.rb:12
      1887  /Users/kuwabara.masataka/ghq/github.com/ruby/rbs/lib/rbs/type_name.rb:13
         1  /Users/kuwabara.masataka/ghq/github.com/ruby/rbs/lib/rbs/parser_aux.rb:17

      3610  "I"
      1807  /Users/kuwabara.masataka/ghq/github.com/ruby/rbs/lib/rbs/type_name.rb:12
      1803  /Users/kuwabara.masataka/ghq/github.com/ruby/rbs/lib/rbs/type_name.rb:13

      1372  "Integer"
      1372  /Users/kuwabara.masataka/ghq/github.com/ruby/rbs/lib/rbs/type_name.rb:12

It indicates TypeName#initialize allocates many strings.

Benchmark

require 'rbs'
require 'benchmark'

def subject
  loader = RBS::EnvironmentLoader.new
  RBS::Environment.from_loader(loader).resolve_type_names
end

Benchmark.bmbm(20) do |x|
  x.report do
    40.times do
      subject
    end
  end
end

before

$ ruby -v bench.rb
ruby 3.2.2 (2023-03-30 revision e51014f9c0) [arm64-darwin21]
Rehearsal --------------------------------------------------------
                       3.109180   0.101717   3.210897 (  3.256885)
----------------------------------------------- total: 3.210897sec

                           user     system      total        real
                       3.212283   0.103555   3.315838 (  3.339915)

after

$ ruby -v bench.rb
ruby 3.2.2 (2023-03-30 revision e51014f9c0) [arm64-darwin21]
Rehearsal --------------------------------------------------------
                       3.250580   0.104434   3.355014 (  3.406756)
----------------------------------------------- total: 3.355014sec

                           user     system      total        real
                       2.909117   0.098380   3.007497 (  3.031184)

It reduces 9% execution time in this benchmark script.
I also tested on a large Rails app and got the same improvement (reducing 9% execution time).

Implementation note

I used Symbol#match? instead of Symbol#start_with? because Symbol#match? is faster than #start_with? because #start_with? creates a MatchData.

Copy link
Member

@soutaro soutaro left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good. Looks like test is failing because of 2.x?

@soutaro soutaro added this to the RBS 3.2 milestone Jul 13, 2023
@pocke
Copy link
Member Author

pocke commented Jul 20, 2023

Yes, Ruby 2.6 does not have Symbol#start_with? so the CI is failing.

I'll rebase this PR on the master branch, write the PR description, then merge this PR.

@pocke pocke marked this pull request as ready for review July 20, 2023 05:18
@pocke pocke added this pull request to the merge queue Jul 20, 2023
Merged via the queue into ruby:master with commit db860d1 Jul 20, 2023
23 checks passed
@pocke pocke deleted the Reduce_memory_allocation_on_TypeName_initialize branch July 20, 2023 05:24
@soutaro soutaro added the Released PRs already included in the released version label Aug 18, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Released PRs already included in the released version
Development

Successfully merging this pull request may close these issues.

2 participants