Repository navigation
Add compiled views performance optimization - #613
Open
piotrekpasciak wants to merge 1 commit into
Open
piotrekpasciak wants to merge 1 commit into
piotrekpasciak wants to merge 1 commit into
Conversation
piotrekpasciak
force-pushed
the
perf/simple-compiled-views
branch
from
October 22, 2026 15:14
90ad482 to
e50dc5d
Compare
piotrekpasciak
force-pushed
the
perf/simple-compiled-views
branch
from
October 23, 2026 08:40
e50dc5d to
b1eb4ac
Compare
Signed-off-by: Piotr Pasciak <piotrek.pasciak@gmail.com>
piotrekpasciak
force-pushed
the
perf/simple-compiled-views
branch
from
October 23, 2026 08:54
b1eb4ac to
4db8933
Compare
This branch has not been deployed
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Checklist:
Changes:
Rendering#object_to_hash(rendering.rb:160)call(object, options), which builds the Hash directly.Field#extract,Field#skip?(field.rb:16-24)if_callable/unless_callableare looked up once at compile time (ViewCompiler.field_conditions), and the procs are called directly.AutoExtractor#extract, which picks an extractor per callViewCompiler.read.PublicSendExtractorobject.name, orobject.public_send(REFS[n])for unusual method names.BlockExtractorREFS[n].call(object, **options)orREFS[n].call(object), using the sameSymbol#to_proccheck, done once.HashExtractorobject_to_hash.DateTimeFormatter#format,EmptyTypesdefault logicdatetime_format,defaultordefault_ifaren't compiled, and the global versions turn off compiling for the whole render.AssociationExtractor#extract(staticblueprint:, nooptions:)ViewCompiler.render_association. It skips default handling,merge_association_optionsand the Proc-blueprint check.hashify→prepare_datarender_associationcalls the child's compiled view directly. The view is checked at compile time (blueprint.view?(view)). This is why the README warns that overriddenhashify/prepare_dataaren't called for associations.Following message chain for each object, then each field makes current approach slow:
Benchmarks from repository:
Benchmarked with
benchmark-ipson Ruby 3.3.10 (arm64) against three configs:main, this branch withcompile_viewsoff, and this branch withcompile_views = true. Each config ran in its own process. Each config ran twice, interleaved, and the table shows the best run. Error was at most ±1%. All configs produce identical output for every scenario (checked by comparing an MD5 of the marshaled result).if/name/exclude_if_nil)datetime_format/defaultfields)renderto JSON, nested ×100Takeaways
compile_viewsoff (the default), performance is the same asmain, so nothing changes for anyone who doesn't opt in.datetime_format,default, custom extractors) gain the least.Caveats
Structs. ActiveRecord attribute reads cost more, so the gains in real apps will probably be smaller.hashify/prepare_dataand from not copying the options Hash when the view is the same (see the README's "Compiled Views" section).Benchmarks from a real back-end application:
Code used for benchmarking inside of the endpoint: