Module: Fontist::Indexes::IndexMixin
- Included in:
- DefaultFamilyFontIndex, FilenameIndex, PreferredFamilyFontIndex
- Defined in:
- lib/fontist/indexes/index_mixin.rb
Overview
IndexMixin provides common functionality for font index classes.
Performance Optimization (Tech Debt)
This module uses a temporary Hash-based lookup cache during index building to avoid O(n²) performance when adding many entries. This is a workaround for Lutaml::Model::Collection’s Array-based storage.
The Problem
Lutaml::Model::Collection stores entries as an Array, which provides O(n) lookup when searching for existing keys. When building an index with thousands of entries, this creates O(n²) behavior:
-
8867 font styles × average 2670 comparisons = ~23.6 million comparisons
-
Index building: ~26 seconds with Array lookup
The Workaround
During ‘build` and `build_with_formulas`, we maintain a temporary `@index_build_cache` Hash that provides O(1) lookups. After building, the cache is cleared.
-
Index building with Hash lookup: ~0.08 seconds
-
Speedup: 325× faster
The Proper Fix
This tech debt should be resolved by enhancing Lutaml::Model::Collection to support efficient key-based lookups. See the reproduction script at: ‘dev/lutaml_model_collection_lookup_benchmark.rb`
Related issue: github.com/lutaml/lutaml-model/issues/XXX
Defined Under Namespace
Modules: ClassMethods
Class Method Summary collapse
Instance Method Summary collapse
- #add_formula(formula) ⇒ Object
-
#add_index_formula(style, formula_path) ⇒ Object
Add a font style to the index with O(1) or O(n) lookup.
-
#build ⇒ Object
Build index by loading all formulas from disk.
-
#build_with_formulas(formulas) ⇒ Object
Build index from pre-loaded formulas.
- #index_key_for_style(_style) ⇒ Object
- #load_formulas(key) ⇒ Object
- #load_index_formulas(key) ⇒ Object
- #to_file(file_path = self.class.path) ⇒ Object
Class Method Details
.included(base) ⇒ Object
38 39 40 |
# File 'lib/fontist/indexes/index_mixin.rb', line 38 def self.included(base) base.extend(ClassMethods) end |
Instance Method Details
#add_formula(formula) ⇒ Object
107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 |
# File 'lib/fontist/indexes/index_mixin.rb', line 107 def add_formula(formula) # Accept FormulaV4, FormulaV5, or any object that responds to all_fonts unless formula.respond_to?(:all_fonts) && formula.respond_to?(:path) raise ArgumentError, "Expected formula-like object, got #{formula.class}" end formula.all_fonts.each do |font| font.styles.each do |style| add_index_formula(style, formula.path) end end entries end |
#add_index_formula(style, formula_path) ⇒ Object
Add a font style to the index with O(1) or O(n) lookup.
Uses ‘@index_build_cache` Hash for O(1) lookup during building, falling back to O(n) Array lookup for incremental updates.
132 133 134 135 136 137 138 139 |
# File 'lib/fontist/indexes/index_mixin.rb', line 132 def add_index_formula(style, formula_path) key = prepare_index_key(style) paths = prepare_formula_paths(formula_path) return if merge_existing_entry?(key, paths) create_and_add_entry(key, paths) end |
#build ⇒ Object
Build index by loading all formulas from disk. Uses Hash-based cache for O(1) lookups during building.
78 79 80 81 82 83 84 85 86 87 88 |
# File 'lib/fontist/indexes/index_mixin.rb', line 78 def build with_index_build_cache do Formula.all.each do |formula| add_formula(formula) end end to_file self end |
#build_with_formulas(formulas) ⇒ Object
Build index from pre-loaded formulas. Uses Hash-based cache for O(1) lookups during building.
This is the preferred method when formulas are already loaded, as it avoids re-loading from disk.
95 96 97 98 99 100 101 102 103 104 105 |
# File 'lib/fontist/indexes/index_mixin.rb', line 95 def build_with_formulas(formulas) with_index_build_cache do formulas.each do |formula| add_formula(formula) end end to_file self end |
#index_key_for_style(_style) ⇒ Object
123 124 125 126 |
# File 'lib/fontist/indexes/index_mixin.rb', line 123 def index_key_for_style(_style) raise NotImplementedError, "index_key_for_style(style) must be implemented" end |
#load_formulas(key) ⇒ Object
141 142 143 |
# File 'lib/fontist/indexes/index_mixin.rb', line 141 def load_formulas(key) index_formulas(key).flat_map(&:to_full) end |
#load_index_formulas(key) ⇒ Object
145 146 147 |
# File 'lib/fontist/indexes/index_mixin.rb', line 145 def load_index_formulas(key) index_formulas(key) end |
#to_file(file_path = self.class.path) ⇒ Object
149 150 151 152 153 154 155 156 |
# File 'lib/fontist/indexes/index_mixin.rb', line 149 def to_file(file_path = self.class.path) # Use default path if file_path is nil file_path = self.class.path if file_path.nil? # puts "Writing index to #{file_path}" FileUtils.mkdir_p(File.dirname(file_path)) File.write(file_path, to_yaml) end |