Top Level Namespace

Defined Under Namespace

Modules: Lutaml, PerformanceHelpers, PlanWalkProbe, XmlCompilerBenchmarks, YAML Classes: Address, BenchmarkRunner, CollectionHandlerBenchmark, Module, PerformanceComparator, Person, SomeCollection, WeakRef, XmlCompilerBenchmarkRunner, XmlElementBenchmark

Constant Summary collapse

ITERATIONS =

Number of iterations for profiling

10
MODE =

Model-pipeline performance probe (TODO.perf/01). Run via rake performance:probe / :profile / :oneshot, or directly:

ruby lib/tasks/performance_probe.rb probe|profile|oneshot
ARGV[0] || "probe"
REPO_ROOT =
Pathname.new(File.expand_path("../../..", __dir__))
LIB =
REPO_ROOT.join("lib")
NATIVE_ONLY_PATTERNS =

Patterns excluded under Opal because they pull in native-only deps (Nokogiri/Ox C extensions, Thor CLI, etc.). REXML is pure Ruby and ships in the bundled gem + moxml's lib/compat/opal/rexml/* shadows, so it is NOT excluded — it is a fully working second Opal adapter.

[
  %r{(nokogiri|ox)_adapter\z},
  %r{/schema/(relaxng|xsd)\b},
  %r{/schema_builder/(nokogiri|oga)\b},
  %r{/schema/builder/(nokogiri|oga)\b},
  %r{\Alutaml/model/cli\z},
].freeze
DECL_RE =

Match autoload :Name, "...anything..." where the string argument may span multiple physical lines (whitespace after the comma).

/autoload\s+:[A-Za-z0-9_]+\s*,\s*"([^"]+)"/
ENTRY_FILES =

Order matters: namespace-defining entry files must load before any of their nested files. lutaml/model.rb declares the Lutaml::Model module (referenced by every other file); lutaml/xml.rb declares Lutaml::Xml. After the entry files, load by depth-then-name so a parent file (e.g. lutaml/hash_format.rb) is required before its children (e.g. lutaml/hash_format/adapter/document.rb).

ENTRY_FILES are added unconditionally: they are top-level entry points that nothing else autoloads (because they ARE the entry points users require directly), so they would otherwise be missing from the list.

%w[lutaml/model lutaml/xml].freeze

Instance Method Summary collapse

Instance Method Details

#allocs_of(&blk) ⇒ Object



44
45
46
47
48
49
# File 'lib/tasks/performance_probe.rb', line 44

def allocs_of(&blk)
  GC.start
  before = GC.stat(:total_allocated_objects)
  50.times(&blk)
  (GC.stat(:total_allocated_objects) - before) / 50.0
end

#build_root(item, root, count) ⇒ Object



32
33
34
# File 'lib/tasks/performance_probe.rb', line 32

def build_root(item, root, count)
  root.new(item: (0...count).map { |i| item.new(id: i, name: "Test #{i}", tags: %w[a b c]) })
end

#define_modelsObject



12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# File 'lib/tasks/performance_probe.rb', line 12

def define_models
  item = Class.new(Lutaml::Model::Serializable) do
    attribute :id, :integer
    attribute :name, :string
    attribute :tags, :string, collection: true

    key_value do
      map "id", to: :id
      map "name", to: :name
      map "tags", to: :tags
    end
  end
  root = Class.new(Lutaml::Model::Serializable) do
    attribute :item, item, collection: true

    key_value { map "item", to: :item }
  end
  [item, root]
end

#ips_of(seconds = 3) ⇒ Object



36
37
38
39
40
41
42
# File 'lib/tasks/performance_probe.rb', line 36

def ips_of(seconds = 3)
  require "benchmark/ips"
  lambda do |label, &blk|
    r = Benchmark.ips(time: seconds, quiet: true) { |x| x.report(label, &blk) }
    r.entries.max_by(&:ips).ips.round(1)
  end
end

#join_multiline_autoloads(src) ⇒ Object

Stitches multi-line autoload declarations into a single logical line so DECL_RE can match. We only merge when the line ends right after the comma that follows autoload :Name.



40
41
42
43
44
# File 'lib/compat/opal/generate_boot.rb', line 40

def join_multiline_autoloads(src)
  src.gsub(/(autoload\s+:[A-Za-z0-9_]+\s*,)\s*\n\s*/) do
    "#{Regexp.last_match(1)} "
  end
end

#person_instanceObject



74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
# File 'lib/tasks/memory_profile.rb', line 74

def person_instance
  Person.new(
    id: 123,
    first_name: "John",
    last_name: "Doe",
    email: "[email protected]",
    age: 30,
    address: Address.new(
      street: "123 Main St",
      city: "City",
      zip: "12345",
      country: "Country",
    ),
    phone_numbers: ["555-1000", "555-2000"],
    tags: ["tag1", "tag2"],
  )
end

#person_xmlObject

Test data



53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
# File 'lib/tasks/memory_profile.rb', line 53

def person_xml
  "    <person id=\"123\">\n      <first_name>John</first_name>\n      <last_name>Doe</last_name>\n      <email>[email protected]</email>\n      <age>30</age>\n      <address>\n        <street>123 Main St</street>\n        <city>City</city>\n        <zip>12345</zip>\n        <country>Country</country>\n      </address>\n      <phone_number>555-1000</phone_number>\n      <phone_number>555-2000</phone_number>\n      <tag>tag1</tag>\n      <tag>tag2</tag>\n    </person>\n  XML\nend\n"

#resolve_path(raw, declaring_file) ⇒ Object

Resolve an autoload's raw string argument to a require path relative to LIB. Both common interpolation shapes resolve relative to the declaring file's directory, which is what MRI does for them.



49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
# File 'lib/compat/opal/generate_boot.rb', line 49

def resolve_path(raw, declaring_file)
  declaring_dir = Pathname.new(declaring_file).dirname

  if raw.start_with?("\#{__dir__}/")
    subdir = raw.sub("\#{__dir__}/", "").delete_suffix(".rb")
    (declaring_dir + subdir).relative_path_from(LIB).to_s.delete_suffix(".rb")
  elsif raw.start_with?("\#{File.dirname(__FILE__)}/")
    subdir = raw.sub("\#{File.dirname(__FILE__)}/", "").delete_suffix(".rb")
    (declaring_dir + subdir).relative_path_from(LIB).to_s.delete_suffix(".rb")
  elsif raw.start_with?("lutaml/")
    raw.delete_suffix(".rb")
  else
    raw.delete_suffix(".rb")
  end
end