Created
May 18, 2026 18:22
-
-
Save ibrahima/a507c97838b1fbe41c5d45ed8ce07cdf to your computer and use it in GitHub Desktop.
Benchmark script for TailwindMerge::Config merging memory leak
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # frozen_string_literal: true | |
| # To run this script from the tailwind_merge repository, use: | |
| # `ruby -Ilib bench_memory_leak.rb` | |
| require "objspace" | |
| require "tailwind_merge" | |
| ITERATIONS = Integer(ENV.fetch("ITERATIONS", "1000")) | |
| PAYLOAD_SIZE = Integer(ENV.fetch("PAYLOAD_SIZE", "1024")) | |
| def rss_kb | |
| status = File.read("/proc/self/status") | |
| status[/^VmRSS:\s+(\d+)\s+kB$/, 1].to_i | |
| rescue Errno::ENOENT | |
| `ps -o rss= -p #{Process.pid}`.to_i | |
| end | |
| def format_kb_from_bytes(bytes) | |
| format("%.1f KB", bytes / 1024.0) | |
| end | |
| def format_mb_from_kb(kb) | |
| format("%.1f MB", kb / 1024.0) | |
| end | |
| def snapshot(label) | |
| GC.start(full_mark: true, immediate_sweep: true) | |
| { | |
| label: label, | |
| rss_kb: rss_kb, | |
| heap_live_slots: GC.stat[:heap_live_slots], | |
| total_allocated_objects: GC.stat[:total_allocated_objects], | |
| merger_instances: ObjectSpace.each_object(TailwindMerge::Merger).count, | |
| class_utils_instances: ObjectSpace.each_object(TailwindMerge::ClassGroupUtils).count, | |
| default_spacing_entries: TailwindMerge::Config::DEFAULTS[:theme]["spacing"].length, | |
| memsize_all: ObjectSpace.memsize_of_all, | |
| } | |
| end | |
| def print_snapshot(before, after) | |
| puts "#{before[:label]} -> #{after[:label]}" | |
| puts "RSS: #{before[:rss_kb]} KB (#{format_mb_from_kb(before[:rss_kb])}) -> " \ | |
| "#{after[:rss_kb]} KB (#{format_mb_from_kb(after[:rss_kb])}) " \ | |
| "delta=#{after[:rss_kb] - before[:rss_kb]} KB (#{format_mb_from_kb(after[:rss_kb] - before[:rss_kb])})" | |
| puts "heap_live_slots: #{before[:heap_live_slots]} -> #{after[:heap_live_slots]} " \ | |
| "delta=#{after[:heap_live_slots] - before[:heap_live_slots]}" | |
| puts "memsize_of_all: #{format_kb_from_bytes(before[:memsize_all])} -> " \ | |
| "#{format_kb_from_bytes(after[:memsize_all])} " \ | |
| "delta=#{format_kb_from_bytes(after[:memsize_all] - before[:memsize_all])}" | |
| puts "allocated objects: delta=#{after[:total_allocated_objects] - before[:total_allocated_objects]}" | |
| puts "Merger instances: #{before[:merger_instances]} -> #{after[:merger_instances]}" | |
| puts "ClassGroupUtils: #{before[:class_utils_instances]} -> #{after[:class_utils_instances]}" | |
| puts "DEFAULT spacing length: #{before[:default_spacing_entries]} -> #{after[:default_spacing_entries]}" | |
| end | |
| def build_mergers | |
| ITERATIONS.times do |i| | |
| custom_scale = "custom-space-#{i}-#{"x" * PAYLOAD_SIZE}" | |
| merger = TailwindMerge::Merger.new( | |
| config: { | |
| theme: { | |
| "spacing" => [custom_scale], | |
| }, | |
| }, | |
| ) | |
| # Exercise the custom theme path, then drop the local reference. | |
| merger.merge("p-1 p-#{custom_scale}") | |
| end | |
| end | |
| before = snapshot("before") | |
| build_mergers | |
| after = snapshot("after") | |
| print_snapshot(before, after) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment