Skip to content

Suppress StateItem instance creation - #609

Merged
yui-knk merged 1 commit into
ruby:masterfrom
yui-knk:optimize_triple
Mar 12, 2025
Merged

Suppress StateItem instance creation#609
yui-knk merged 1 commit into
ruby:masterfrom
yui-knk:optimize_triple

Conversation

@yui-knk

@yui-knk yui-knk commented Mar 12, 2025

Copy link
Copy Markdown
Collaborator

Pass StateItem to Triple.new so that the number of StateItem instance creation decreases.

Before:

$ stackprof tmp/stackprof-cpu-myapp.dump --method "Lrama::Counterexamples#shortest_path"
Lrama::Counterexamples#shortest_path (yui-knk/lrama/lib/lrama/counterexamples.rb:273)
  samples:  2752 self (21.5%)  /   4854 total (37.9%)
  callers:
    4854  (  100.0%)  Lrama::Counterexamples#reduce_reduce_examples
    3345  (   68.9%)  Hash#each_key
  callees (2102 total):
    3362  (  159.9%)  Set#each
     518  (   24.6%)  Lrama::Counterexamples::Triple#state_item
     256  (   12.2%)  Class#new
     251  (   11.9%)  Set#include?
     241  (   11.5%)  #<Class:0x0000000104f1b698>.new
     219  (   10.4%)  #<Class:0x0000000104f1c598>.new
     196  (    9.3%)  Lrama::Counterexamples#reachable_state_items
     128  (    6.1%)  Struct#hash
     107  (    5.1%)  Lrama::Counterexamples#follow_l
      65  (    3.1%)  Array#shift
      40  (    1.9%)  Struct#eql?
      27  (    1.3%)  Lrama::States::Item#next_sym
      18  (    0.9%)  Array#hash
      17  (    0.8%)  Array#eql?
       2  (    0.1%)  Lrama::State#==
  code:
                                  |   273  |     def shortest_path(conflict_state, conflict_reduce_item, conflict_term)
                                  |   274  |       queue = [] #: Array[[Triple, Array[Path::path]]]
                                  |   275  |       visited = {} #: Hash[Triple, true]
                                  |   276  |       start_state = @states.states.first #: Lrama::State
                                  |   277  |       conflict_term_bit = Bitmap::from_array([conflict_term.number])
                                  |   278  |       raise "BUG: Start state should be just one kernel." if start_state.kernels.count != 1
  196    (1.5%)                   |   279  |       reachable = reachable_state_items(StateItem.new(conflict_state, conflict_reduce_item))
                                  |   280  |       start = Triple.new(start_state, start_state.kernels.first, Bitmap::from_array([@states.eof_symbol.number]))
                                  |   281  |
                                  |   282  |       queue << [start, [StartPath.new(start.state_item)]]
                                  |   283  |
  104    (0.8%) /    39   (0.3%)  |   284  |       while (triple, paths = queue.shift)
  437    (3.4%) /   296   (2.3%)  |   285  |         next if visited[triple]
  190    (1.5%) /   175   (1.4%)  |   286  |         visited[triple] = true
                                  |   287  |
                                  |   288  |         # Found
    2    (0.0%)                   |   289  |         if (triple.state == conflict_state) && (triple.item == conflict_reduce_item) && (triple.l & conflict_term_bit != 0)
                                  |   290  |           return paths
                                  |   291  |         end
                                  |   292  |
                                  |   293  |         # transition
  150    (1.2%) /    57   (0.4%)  |   294  |         next_state_item = @transitions[[triple.state_item, triple.item.next_sym]]
   26    (0.2%) /     1   (0.0%)  |   295  |         if next_state_item && reachable.include?(next_state_item)
                                  |   296  |           # @type var t: Triple
   33    (0.3%) /     2   (0.0%)  |   297  |           t = Triple.new(next_state_item.state, next_state_item.item, triple.l)
  287    (2.2%) /   218   (1.7%)  |   298  |           queue << [t, paths + [TransitionPath.new(triple.state_item, t.state_item)]]
                                  |   299  |         end
                                  |   300  |
                                  |   301  |         # production step
 3431   (26.8%) /    26   (0.2%)  |   302  |         @productions[triple.state_item]&.each do |item|
  469    (3.7%) /    24   (0.2%)  |   303  |           next unless reachable.include?(StateItem.new(triple.state, item))
                                  |   304  |
  118    (0.9%) /    11   (0.1%)  |   305  |           l = follow_l(triple.item, triple.l)
                                  |   306  |           # @type var t: Triple
  222    (1.7%) /    12   (0.1%)  |   307  |           t = Triple.new(triple.state, item, l)
 2534   (19.8%) /  1891  (14.8%)  |   308  |           queue << [t, paths + [ProductionPath.new(triple.state_item, t.state_item)]]
                                  |   309  |         end

After:

$ stackprof tmp/stackprof-cpu-myapp.dump --method "Lrama::Counterexamples#shortest_path"
Lrama::Counterexamples#shortest_path (yui-knk/lrama/lib/lrama/counterexamples.rb:273)
  samples:  4512 self (34.3%)  /   7467 total (56.8%)
  callers:
    7467  (  100.0%)  Lrama::Counterexamples#reduce_reduce_examples
    6605  (   88.5%)  Hash#each_key
  callees (2955 total):
    6619  (  224.0%)  Set#each
    1136  (   38.4%)  Class#new
    1095  (   37.1%)  #<Class:0x00000001047f78a8>.new
     238  (    8.1%)  Lrama::Counterexamples#reachable_state_items
     216  (    7.3%)  Set#include?
     117  (    4.0%)  Kernel#hash
      43  (    1.5%)  Lrama::Counterexamples#follow_l
      35  (    1.2%)  Lrama::States::Item#next_sym
      16  (    0.5%)  Array#shift
      13  (    0.4%)  Lrama::Counterexamples::Triple#state
       8  (    0.3%)  Array#hash
       7  (    0.2%)  Array#eql?
       5  (    0.2%)  Struct#hash
       4  (    0.1%)  Lrama::Counterexamples::Triple#state_item
       3  (    0.1%)  Struct#eql?
       3  (    0.1%)  Lrama::Counterexamples::Triple#item
       2  (    0.1%)  Lrama::State#==
  code:
                                  |   273  |     def shortest_path(conflict_state, conflict_reduce_item, conflict_term)
                                  |   274  |       queue = [] #: Array[[Triple, Array[Path::path]]]
                                  |   275  |       visited = {} #: Hash[Triple, true]
                                  |   276  |       start_state = @states.states.first #: Lrama::State
                                  |   277  |       conflict_term_bit = Bitmap::from_array([conflict_term.number])
                                  |   278  |       raise "BUG: Start state should be just one kernel." if start_state.kernels.count != 1
  238    (1.8%)                   |   279  |       reachable = reachable_state_items(StateItem.new(conflict_state, conflict_reduce_item))
                                  |   280  |       start = Triple.new(StateItem.new(start_state, start_state.kernels.first), Bitmap::from_array([@states.eof_symbol.number]))
                                  |   281  |
                                  |   282  |       queue << [start, [StartPath.new(start.state_item)]]
                                  |   283  |
   21    (0.2%) /     5   (0.0%)  |   284  |       while (triple, paths = queue.shift)
  135    (1.0%) /    18   (0.1%)  |   285  |         next if visited[triple]
   77    (0.6%) /    77   (0.6%)  |   286  |         visited[triple] = true
                                  |   287  |
                                  |   288  |         # Found
    4    (0.0%) /     1   (0.0%)  |   289  |         if (triple.state == conflict_state) && (triple.item == conflict_reduce_item) && (triple.l & conflict_term_bit != 0)
                                  |   290  |           return paths
                                  |   291  |         end
                                  |   292  |
                                  |   293  |         # transition
   83    (0.6%) /    32   (0.2%)  |   294  |         next_state_item = @transitions[[triple.state_item, triple.item.next_sym]]
   17    (0.1%)                   |   295  |         if next_state_item && reachable.include?(next_state_item)
                                  |   296  |           # @type var t: Triple
   26    (0.2%)                   |   297  |           t = Triple.new(next_state_item, triple.l)
  230    (1.8%) /   207   (1.6%)  |   298  |           queue << [t, paths + [TransitionPath.new(triple.state_item, t.state_item)]]
                                  |   299  |         end
                                  |   300  |
                                  |   301  |         # production step
 6638   (50.5%) /    11   (0.1%)  |   302  |         @productions[triple.state_item]&.each do |item|
  792    (6.0%) /     7   (0.1%)  |   303  |           next unless reachable.include?(StateItem.new(triple.state, item))
                                  |   304  |
   55    (0.4%) /    10   (0.1%)  |   305  |           l = follow_l(triple.item, triple.l)
                                  |   306  |           # @type var t: Triple
 1090    (8.3%) /    25   (0.2%)  |   307  |           t = Triple.new(StateItem.new(triple.state, item), l)
 4665   (35.5%) /  4118  (31.3%)  |   308  |           queue << [t, paths + [ProductionPath.new(triple.state_item, t.state_item)]]
    1    (0.0%) /     1   (0.0%)  |   309  |         end
                                  |   310  |       end

Pass `StateItem` to `Triple.new` so that the number of `StateItem`
instance creation decreases.

Before:

```
$ stackprof tmp/stackprof-cpu-myapp.dump --method "Lrama::Counterexamples#shortest_path"
Lrama::Counterexamples#shortest_path (yui-knk/lrama/lib/lrama/counterexamples.rb:273)
  samples:  2752 self (21.5%)  /   4854 total (37.9%)
  callers:
    4854  (  100.0%)  Lrama::Counterexamples#reduce_reduce_examples
    3345  (   68.9%)  Hash#each_key
  callees (2102 total):
    3362  (  159.9%)  Set#each
     518  (   24.6%)  Lrama::Counterexamples::Triple#state_item
     256  (   12.2%)  Class#new
     251  (   11.9%)  Set#include?
     241  (   11.5%)  #<Class:0x0000000104f1b698>.new
     219  (   10.4%)  #<Class:0x0000000104f1c598>.new
     196  (    9.3%)  Lrama::Counterexamples#reachable_state_items
     128  (    6.1%)  Struct#hash
     107  (    5.1%)  Lrama::Counterexamples#follow_l
      65  (    3.1%)  Array#shift
      40  (    1.9%)  Struct#eql?
      27  (    1.3%)  Lrama::States::Item#next_sym
      18  (    0.9%)  Array#hash
      17  (    0.8%)  Array#eql?
       2  (    0.1%)  Lrama::State#==
  code:
                                  |   273  |     def shortest_path(conflict_state, conflict_reduce_item, conflict_term)
                                  |   274  |       queue = [] #: Array[[Triple, Array[Path::path]]]
                                  |   275  |       visited = {} #: Hash[Triple, true]
                                  |   276  |       start_state = @States.states.first #: Lrama::State
                                  |   277  |       conflict_term_bit = Bitmap::from_array([conflict_term.number])
                                  |   278  |       raise "BUG: Start state should be just one kernel." if start_state.kernels.count != 1
  196    (1.5%)                   |   279  |       reachable = reachable_state_items(StateItem.new(conflict_state, conflict_reduce_item))
                                  |   280  |       start = Triple.new(start_state, start_state.kernels.first, Bitmap::from_array([@states.eof_symbol.number]))
                                  |   281  |
                                  |   282  |       queue << [start, [StartPath.new(start.state_item)]]
                                  |   283  |
  104    (0.8%) /    39   (0.3%)  |   284  |       while (triple, paths = queue.shift)
  437    (3.4%) /   296   (2.3%)  |   285  |         next if visited[triple]
  190    (1.5%) /   175   (1.4%)  |   286  |         visited[triple] = true
                                  |   287  |
                                  |   288  |         # Found
    2    (0.0%)                   |   289  |         if (triple.state == conflict_state) && (triple.item == conflict_reduce_item) && (triple.l & conflict_term_bit != 0)
                                  |   290  |           return paths
                                  |   291  |         end
                                  |   292  |
                                  |   293  |         # transition
  150    (1.2%) /    57   (0.4%)  |   294  |         next_state_item = @transitions[[triple.state_item, triple.item.next_sym]]
   26    (0.2%) /     1   (0.0%)  |   295  |         if next_state_item && reachable.include?(next_state_item)
                                  |   296  |           # @type var t: Triple
   33    (0.3%) /     2   (0.0%)  |   297  |           t = Triple.new(next_state_item.state, next_state_item.item, triple.l)
  287    (2.2%) /   218   (1.7%)  |   298  |           queue << [t, paths + [TransitionPath.new(triple.state_item, t.state_item)]]
                                  |   299  |         end
                                  |   300  |
                                  |   301  |         # production step
 3431   (26.8%) /    26   (0.2%)  |   302  |         @Productions[triple.state_item]&.each do |item|
  469    (3.7%) /    24   (0.2%)  |   303  |           next unless reachable.include?(StateItem.new(triple.state, item))
                                  |   304  |
  118    (0.9%) /    11   (0.1%)  |   305  |           l = follow_l(triple.item, triple.l)
                                  |   306  |           # @type var t: Triple
  222    (1.7%) /    12   (0.1%)  |   307  |           t = Triple.new(triple.state, item, l)
 2534   (19.8%) /  1891  (14.8%)  |   308  |           queue << [t, paths + [ProductionPath.new(triple.state_item, t.state_item)]]
                                  |   309  |         end
```

After:

```
$ stackprof tmp/stackprof-cpu-myapp.dump --method "Lrama::Counterexamples#shortest_path"
Lrama::Counterexamples#shortest_path (yui-knk/lrama/lib/lrama/counterexamples.rb:273)
  samples:  4512 self (34.3%)  /   7467 total (56.8%)
  callers:
    7467  (  100.0%)  Lrama::Counterexamples#reduce_reduce_examples
    6605  (   88.5%)  Hash#each_key
  callees (2955 total):
    6619  (  224.0%)  Set#each
    1136  (   38.4%)  Class#new
    1095  (   37.1%)  #<Class:0x00000001047f78a8>.new
     238  (    8.1%)  Lrama::Counterexamples#reachable_state_items
     216  (    7.3%)  Set#include?
     117  (    4.0%)  Kernel#hash
      43  (    1.5%)  Lrama::Counterexamples#follow_l
      35  (    1.2%)  Lrama::States::Item#next_sym
      16  (    0.5%)  Array#shift
      13  (    0.4%)  Lrama::Counterexamples::Triple#state
       8  (    0.3%)  Array#hash
       7  (    0.2%)  Array#eql?
       5  (    0.2%)  Struct#hash
       4  (    0.1%)  Lrama::Counterexamples::Triple#state_item
       3  (    0.1%)  Struct#eql?
       3  (    0.1%)  Lrama::Counterexamples::Triple#item
       2  (    0.1%)  Lrama::State#==
  code:
                                  |   273  |     def shortest_path(conflict_state, conflict_reduce_item, conflict_term)
                                  |   274  |       queue = [] #: Array[[Triple, Array[Path::path]]]
                                  |   275  |       visited = {} #: Hash[Triple, true]
                                  |   276  |       start_state = @States.states.first #: Lrama::State
                                  |   277  |       conflict_term_bit = Bitmap::from_array([conflict_term.number])
                                  |   278  |       raise "BUG: Start state should be just one kernel." if start_state.kernels.count != 1
  238    (1.8%)                   |   279  |       reachable = reachable_state_items(StateItem.new(conflict_state, conflict_reduce_item))
                                  |   280  |       start = Triple.new(StateItem.new(start_state, start_state.kernels.first), Bitmap::from_array([@states.eof_symbol.number]))
                                  |   281  |
                                  |   282  |       queue << [start, [StartPath.new(start.state_item)]]
                                  |   283  |
   21    (0.2%) /     5   (0.0%)  |   284  |       while (triple, paths = queue.shift)
  135    (1.0%) /    18   (0.1%)  |   285  |         next if visited[triple]
   77    (0.6%) /    77   (0.6%)  |   286  |         visited[triple] = true
                                  |   287  |
                                  |   288  |         # Found
    4    (0.0%) /     1   (0.0%)  |   289  |         if (triple.state == conflict_state) && (triple.item == conflict_reduce_item) && (triple.l & conflict_term_bit != 0)
                                  |   290  |           return paths
                                  |   291  |         end
                                  |   292  |
                                  |   293  |         # transition
   83    (0.6%) /    32   (0.2%)  |   294  |         next_state_item = @transitions[[triple.state_item, triple.item.next_sym]]
   17    (0.1%)                   |   295  |         if next_state_item && reachable.include?(next_state_item)
                                  |   296  |           # @type var t: Triple
   26    (0.2%)                   |   297  |           t = Triple.new(next_state_item, triple.l)
  230    (1.8%) /   207   (1.6%)  |   298  |           queue << [t, paths + [TransitionPath.new(triple.state_item, t.state_item)]]
                                  |   299  |         end
                                  |   300  |
                                  |   301  |         # production step
 6638   (50.5%) /    11   (0.1%)  |   302  |         @Productions[triple.state_item]&.each do |item|
  792    (6.0%) /     7   (0.1%)  |   303  |           next unless reachable.include?(StateItem.new(triple.state, item))
                                  |   304  |
   55    (0.4%) /    10   (0.1%)  |   305  |           l = follow_l(triple.item, triple.l)
                                  |   306  |           # @type var t: Triple
 1090    (8.3%) /    25   (0.2%)  |   307  |           t = Triple.new(StateItem.new(triple.state, item), l)
 4665   (35.5%) /  4118  (31.3%)  |   308  |           queue << [t, paths + [ProductionPath.new(triple.state_item, t.state_item)]]
    1    (0.0%) /     1   (0.0%)  |   309  |         end
                                  |   310  |       end
```
@yui-knk
yui-knk merged commit 5c58402 into ruby:master Mar 12, 2025
@yui-knk
yui-knk deleted the optimize_triple branch March 12, 2025 15:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant