Project

General

Profile

Defect #44348 » bench_inline_attachments_scrubber.rb

Go MAEDA, 2026-08-17 16:01

 
1
# Benchmark for the cost of resolving inline attachments while formatting a text.
2
#
3
#   bin/rails r bench_inline_attachments_scrubber.rb
4
#
5
# Formats a text of about 100 nodes whose container has 100 attachments.
6
# It goes through textilizable, so it measures the same thing on 6.x, where
7
# inline attachments are resolved by ApplicationHelper#parse_inline_attachments,
8
# and on 7.x, where they are resolved by InlineAttachmentsScrubber.
9
#
10
# Nothing is written: the attachments are unsaved in-memory records and the
11
# text formatting setting is overridden in this process only.
12

    
13
CHUNKS = 4   # about 100 nodes
14
FILES  = 100
15
REPS   = 20
16

    
17
def Setting.text_formatting = 'common_mark'
18

    
19
CHUNK = <<~MD
20
  ## Section %<i>d
21

    
22
  Some paragraph with *emphasis*, `code` and a [link](https://example.com).
23

    
24
  * first item
25
  * second item
26

    
27
  ![](img0.png)
28
MD
29

    
30
TEXT = (1..CHUNKS).map {|i| format(CHUNK, :i => i)}.join("\n")
31

    
32
# Elements and text nodes alike: the formatter walks both.
33
def node_count(html)
34
  count = 0
35
  Loofah.fragment(html).scrub!(Loofah::Scrubber.new {|_node| count += 1})
36
  count
37
end
38

    
39
attachments =
40
  Array.new(FILES) do |i|
41
    Attachment.new(:filename => "img#{i}.png", :created_on => Time.now - i).tap {|a| a.id = i + 1}
42
  end
43

    
44
controller = ApplicationController.new
45
controller.request = ActionDispatch::TestRequest.create
46
view = controller.view_context
47

    
48
render = -> {view.textilizable(TEXT, :attachments => attachments, :only_path => true)}
49

    
50
html = render.call # warm up
51

    
52
GC.start
53
objects = GC.stat(:total_allocated_objects)
54
started = Process.clock_gettime(Process::CLOCK_MONOTONIC)
55
REPS.times {render.call}
56
elapsed = Process.clock_gettime(Process::CLOCK_MONOTONIC) - started
57
objects = GC.stat(:total_allocated_objects) - objects
58

    
59
abort 'the inline attachment was not resolved' unless html.include?('/attachments/download/1/img0.png')
60

    
61
puts "Redmine #{Redmine::VERSION}, Ruby #{RUBY_VERSION}, #{Setting.text_formatting}"
62
puts "#{node_count(html)} nodes, #{FILES} attachments, #{REPS} renderings"
63
puts format('%.1f ms and %d objects allocated per rendering', elapsed / REPS * 1000, objects / REPS)
(1-1/2)