Project

General

Profile

Feature #43957 » 43957-bench.rb

Go MAEDA, 2026-04-20 06:52

 
1
require "benchmark"
2

    
3
STATUS_COUNTS = [10, 20, 40, 80].freeze
4
TRACKER_IDS = [1].freeze
5
ROLE_IDS = [1].freeze
6
ITERATIONS = 5
7

    
8
def build_status_ids(count)
9
  (100_000..(100_000 + count - 1)).to_a
10
end
11

    
12
def build_transitions(status_ids)
13
  status_ids.each_with_object({}) do |old_status_id, transitions|
14
    transitions[old_status_id.to_s] =
15
      status_ids.each_with_object({}) do |new_status_id, by_new_status|
16
        by_new_status[new_status_id.to_s] = {
17
          "always" => "1",
18
          "author" => "1",
19
          "assignee" => "1"
20
        }
21
      end
22
  end
23
end
24

    
25
def seed_records!(status_ids, tracker_ids, role_ids)
26
  rows = []
27

    
28
  tracker_ids.each do |tracker_id|
29
    role_ids.each do |role_id|
30
      status_ids.each do |old_status_id|
31
        status_ids.each do |new_status_id|
32
          rows << {
33
            :tracker_id => tracker_id,
34
            :role_id => role_id,
35
            :old_status_id => old_status_id,
36
            :new_status_id => new_status_id,
37
            :author => false,
38
            :assignee => false
39
          }
40
          rows << {
41
            :tracker_id => tracker_id,
42
            :role_id => role_id,
43
            :old_status_id => old_status_id,
44
            :new_status_id => new_status_id,
45
            :author => false,
46
            :assignee => true
47
          }
48
        end
49
      end
50
    end
51
  end
52

    
53
  WorkflowTransition.insert_all!(rows)
54
end
55

    
56
def delete_seed_records!(status_ids, tracker_ids, role_ids)
57
  WorkflowTransition.where(
58
    :tracker_id => tracker_ids,
59
    :role_id => role_ids,
60
    :old_status_id => status_ids,
61
    :new_status_id => status_ids
62
  ).delete_all
63
end
64

    
65
def original_replace_transitions(trackers, roles, transitions)
66
  trackers = Array.wrap(trackers)
67
  roles = Array.wrap(roles)
68

    
69
  WorkflowTransition.transaction do
70
    records = WorkflowTransition.where(:tracker_id => trackers.map(&:id), :role_id => roles.map(&:id)).to_a
71

    
72
    transitions.each do |old_status_id, transitions_by_new_status|
73
      transitions_by_new_status.each do |new_status_id, transition_by_rule|
74
        transition_by_rule.each do |rule, transition|
75
          trackers.each do |tracker|
76
            roles.each do |role|
77
              w = records.select do |r|
78
                r.old_status_id == old_status_id.to_i &&
79
                  r.new_status_id == new_status_id.to_i &&
80
                  r.tracker_id == tracker.id &&
81
                  r.role_id == role.id &&
82
                  !r.destroyed?
83
              end
84
              if rule == "always"
85
                w = w.select {|r| !r.author && !r.assignee}
86
              else
87
                w = w.select {|r| r.author || r.assignee}
88
              end
89
              if w.size > 1
90
                w[1..-1].each(&:destroy)
91
              end
92
              w = w.first
93

    
94
              if ["1", true].include?(transition)
95
                unless w
96
                  w = WorkflowTransition.new(
97
                    :old_status_id => old_status_id,
98
                    :new_status_id => new_status_id,
99
                    :tracker_id => tracker.id,
100
                    :role_id => role.id
101
                  )
102
                  records << w
103
                end
104
                w.author = true if rule == "author"
105
                w.assignee = true if rule == "assignee"
106
                w.save if w.changed?
107
              elsif w
108
                if rule == "always"
109
                  w.destroy
110
                elsif rule == "author"
111
                  if w.assignee
112
                    w.author = false
113
                    w.save if w.changed?
114
                  else
115
                    w.destroy
116
                  end
117
                elsif rule == "assignee"
118
                  if w.author
119
                    w.assignee = false
120
                    w.save if w.changed?
121
                  else
122
                    w.destroy
123
                  end
124
                end
125
              end
126
            end
127
          end
128
        end
129
      end
130
    end
131
  end
132
end
133

    
134
def benchmark_variant(status_ids)
135
  tracker = Tracker.find(TRACKER_IDS.first)
136
  role = Role.find(ROLE_IDS.first)
137
  transitions = build_transitions(status_ids)
138

    
139
  before_durations = []
140
  after_durations = []
141

    
142
  ITERATIONS.times do
143
    delete_seed_records!(status_ids, TRACKER_IDS, ROLE_IDS)
144
    seed_records!(status_ids, TRACKER_IDS, ROLE_IDS)
145
    before_durations << Benchmark.realtime { original_replace_transitions(tracker, role, transitions) }
146

    
147
    delete_seed_records!(status_ids, TRACKER_IDS, ROLE_IDS)
148
    seed_records!(status_ids, TRACKER_IDS, ROLE_IDS)
149
    after_durations << Benchmark.realtime { WorkflowTransition.replace_transitions(tracker, role, transitions) }
150
  ensure
151
    delete_seed_records!(status_ids, TRACKER_IDS, ROLE_IDS)
152
  end
153

    
154
  {
155
    :count => status_ids.size,
156
    :before_avg => before_durations.sum / before_durations.size,
157
    :after_avg => after_durations.sum / after_durations.size
158
  }
159
end
160

    
161
results = STATUS_COUNTS.map do |count|
162
  benchmark_variant(build_status_ids(count))
163
end
164

    
165
puts "| statuses | before_avg_s | after_avg_s | speedup | reduction |"
166
puts "| ---: | ---: | ---: | ---: | ---: |"
167
results.each do |result|
168
  speedup = result[:before_avg] / result[:after_avg]
169
  reduction = (1 - (result[:after_avg] / result[:before_avg])) * 100
170
  puts "| #{result[:count]} | #{format('%.4f', result[:before_avg])} | #{format('%.4f', result[:after_avg])} | #{format('%.2f', speedup)}x | #{format('%.1f', reduction)}% |"
171
end
172

    
(3-3/3)