# Benchmarks Are Free Now

DevFeed: [Benchmarks Are Free Now](<https://devfeed.tech/articles/benchmarks-are-free-now-20533.md>)

Original publisher: [Read original article](<https://code.dblock.org/2026/08/15/benchmarks-are-free-now.html>)

Author: Daniel Doubrovkine (dblock@dblock.org)

Published: 2026-08-15T00:00:00Z

Content type: article

Language: en

Sources: [Daniel Doubrovkine](<https://devfeed.tech/sources/daniel-doubrovkine.md>)

Topics: [Ruby](<https://devfeed.tech/topics/ruby.md>), [Benchmark](<https://devfeed.tech/topics/benchmark.md>), [Polymorphism](<https://devfeed.tech/topics/polymorphism.md>), [GitHub Copilot CLI](<https://devfeed.tech/topics/github-copilot-cli.md>)

Tags: [ai](<https://devfeed.tech/tags/ai.md>), [benchmarks](<https://devfeed.tech/tags/benchmarks.md>), [caching](<https://devfeed.tech/tags/caching.md>), [cli](<https://devfeed.tech/tags/cli.md>), [copilot](<https://devfeed.tech/tags/copilot.md>), [open-source](<https://devfeed.tech/tags/open-source.md>), [performance](<https://devfeed.tech/tags/performance.md>), [ruby](<https://devfeed.tech/tags/ruby.md>)

## AI overview

A Ruby enum inheritance fix was correct but made subclass lookups about five times slower because merged enum hashes were rebuilt on every call. Benchmarks generated with Copilot CLI exposed the regression, and memoization restored subclass performance to the base-class level. Additional benchmarks quantified the gem's small overhead for hash-backed lookups and much larger overhead for exhaustive matching.

## Source excerpt

My previous post walked through four bugs in ruby-enum, a gem I maintain, all stemming from the fact that class-level instance variables aren't inherited by subclasses. The third fix, #59, made keys, key?, value?, key, value, to_h, parse and each walk up superclass and merge in a parent's enums, so a subclass would see everything its ancestors defined. It was correct, fully tested, and shipped. It also made every one of those methods roughly 5x slower on any subclass. def _enum_hash if superclass < Ruby::Enum superclass.send(:_enum_hash).merge(_own_enum_hash) else _own_enum_hash end end This recomputes the merged hash, walking the entire ancestor chain, on every single call. There's no caching. A one-level subclass calling .value pays for building a brand new hash, on top of the superclass doing the same, every time. The test suite didn't notice because tests check correctness, not speed, and correctness was fine. I only found this because I asked Copilot CLI to add a benchmark script comparing lookups at different inheritance depths, mostly out of curiosity about how the "Benchmarks" section of the README would read next to the new feature. It took one prompt and about a minute to get a working script: class Colors include Ruby::Enum define :RED, 'red' define :GREEN, 'green' end class SubColors < Colors define :BLUE, 'blue' end class SubSubColors < SubColors define :YELLOW, 'yellow' end benchmark('base class (no inheritance)', n) { Colors.value(:RED) } benchmark('subclass (1 level)', n) { SubColors.value(:RED) } benchmark('sub-subclass (2 levels)', n) { SubSubColors.value(:RED) } The first run told the story immediately: a 1-level subclass's .value calls took roughly 5x as long as the base class, and a 2-level subclass was worse still. Nobody had written this benchmark before merging #59 because writing a throwaway benchmark script used to feel like more ceremony than it was worth for a one-off performance check. Now it's a single sentence to an agent, and the scri