kumi 0.0.12 → 0.0.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +15 -0
- data/lib/kumi/core/analyzer/passes/lower_to_ir_pass.rb +33 -0
- data/lib/kumi/core/ir/execution_engine.rb +30 -1
- data/lib/kumi/runtime/executable.rb +14 -2
- data/lib/kumi/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7af4bdb11ef5da5a25b799cef8387752636a8224634c70656efc203b9d923185
|
4
|
+
data.tar.gz: 4e604513e42dcb8754fab54ead4140d5ca2d8443ae4d0461e62dc3b48bb925d4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: dcbd93268081ff8f0bcf112101603b180932196b1d6fcb59aae2f363d6bbfe4e9ea07cf3cbe811290222dcd15cdf132ea04f409bea34f81599412f729211a5fa
|
7
|
+
data.tar.gz: 3e0c2e91609e742c2852457b2dd61a114a03b988ae983e0cfb23c9bbf46bc5c6c1714636961ed46b4b9ae924c6a77a79dc0aab9013cadbb73e49d5374f8b8dbc
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,18 @@
|
|
1
|
+
## [Unreleased]
|
2
|
+
|
3
|
+
## [0.0.13] – 2025-08-14
|
4
|
+
### Added
|
5
|
+
- Runtime performance optimizations for interpreter execution
|
6
|
+
- Input load deduplication to cache loaded input values and avoid redundant operations
|
7
|
+
- Constant folding optimization to evaluate literal expressions during compilation
|
8
|
+
- Accessor memoization with proper cache isolation per input context
|
9
|
+
- Selective cache invalidation for incremental updates
|
10
|
+
|
11
|
+
### Fixed
|
12
|
+
- Cache isolation between different input contexts preventing cross-context pollution
|
13
|
+
- Cascade mutual exclusion tests now pass correctly with proper trait evaluation
|
14
|
+
- Incremental update performance with targeted cache clearing
|
15
|
+
|
1
16
|
## [0.0.12] – 2025-08-14
|
2
17
|
### Added
|
3
18
|
- Hash objects input declarations with `hash :field do ... end` syntax
|
@@ -379,6 +379,7 @@ module Kumi
|
|
379
379
|
when Syntax::InputReference
|
380
380
|
plan_id = pick_plan_id_for_input([expr.name], access_plans,
|
381
381
|
scope_plan: scope_plan, need_indices: need_indices)
|
382
|
+
|
382
383
|
plans = access_plans.fetch(expr.name.to_s, [])
|
383
384
|
selected = plans.find { |p| p.accessor_key == plan_id }
|
384
385
|
scope = selected ? selected.scope : []
|
@@ -430,6 +431,13 @@ module Kumi
|
|
430
431
|
when Syntax::CallExpression
|
431
432
|
entry = Kumi::Registry.entry(expr.fn_name)
|
432
433
|
|
434
|
+
# Constant folding optimization: evaluate expressions with all literal arguments
|
435
|
+
if can_constant_fold?(expr, entry)
|
436
|
+
folded_value = constant_fold(expr, entry)
|
437
|
+
ops << Kumi::Core::IR::Ops.Const(folded_value)
|
438
|
+
return ops.size - 1
|
439
|
+
end
|
440
|
+
|
433
441
|
if ENV["DEBUG_LOWER"] && has_nested_reducer?(expr)
|
434
442
|
puts " NESTED_REDUCER_DETECTED in #{expr.fn_name} with req_scope=#{required_scope.inspect}"
|
435
443
|
end
|
@@ -986,6 +994,31 @@ module Kumi
|
|
986
994
|
end
|
987
995
|
end
|
988
996
|
end
|
997
|
+
|
998
|
+
# Constant folding optimization helpers
|
999
|
+
def can_constant_fold?(expr, entry)
|
1000
|
+
return false unless entry&.fn # Skip if function not found
|
1001
|
+
return false if entry.reducer # Skip reducer functions for now
|
1002
|
+
return false if expr.args.empty? # Need at least one argument
|
1003
|
+
|
1004
|
+
# Check if all arguments are literals
|
1005
|
+
expr.args.all? { |arg| arg.is_a?(Syntax::Literal) }
|
1006
|
+
end
|
1007
|
+
|
1008
|
+
def constant_fold(expr, entry)
|
1009
|
+
literal_values = expr.args.map(&:value)
|
1010
|
+
|
1011
|
+
begin
|
1012
|
+
# Call the function with literal values at compile time
|
1013
|
+
entry.fn.call(*literal_values)
|
1014
|
+
rescue StandardError => e
|
1015
|
+
# If constant folding fails, fall back to runtime evaluation
|
1016
|
+
# This shouldn't happen with pure functions, but be defensive
|
1017
|
+
puts "Constant folding failed for #{expr.fn_name}: #{e.message}" if ENV["DEBUG_LOWER"]
|
1018
|
+
raise "Cannot constant fold #{expr.fn_name}: #{e.message}"
|
1019
|
+
end
|
1020
|
+
end
|
1021
|
+
|
989
1022
|
end
|
990
1023
|
end
|
991
1024
|
end
|
@@ -42,7 +42,36 @@ module Kumi
|
|
42
42
|
# - DEBUG_GROUP_ROWS=1 prints grouping decisions during Lift.
|
43
43
|
module ExecutionEngine
|
44
44
|
def self.run(ir_module, ctx, accessors:, registry:)
|
45
|
-
|
45
|
+
# Use persistent accessor cache if available, otherwise create temporary one
|
46
|
+
if ctx[:accessor_cache]
|
47
|
+
# Include input data in cache key to avoid cross-context pollution
|
48
|
+
input_key = ctx[:input]&.hash || ctx["input"]&.hash || 0
|
49
|
+
memoized_accessors = add_persistent_memoization(accessors, ctx[:accessor_cache], input_key)
|
50
|
+
else
|
51
|
+
memoized_accessors = add_temporary_memoization(accessors)
|
52
|
+
end
|
53
|
+
|
54
|
+
Interpreter.run(ir_module, ctx, accessors: memoized_accessors, registry: registry)
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
def self.add_persistent_memoization(accessors, cache, input_key)
|
60
|
+
accessors.map do |plan_id, accessor_fn|
|
61
|
+
[plan_id, lambda do |input_data|
|
62
|
+
cache_key = [plan_id, input_key]
|
63
|
+
cache[cache_key] ||= accessor_fn.call(input_data)
|
64
|
+
end]
|
65
|
+
end.to_h
|
66
|
+
end
|
67
|
+
|
68
|
+
def self.add_temporary_memoization(accessors)
|
69
|
+
cache = {}
|
70
|
+
accessors.map do |plan_id, accessor_fn|
|
71
|
+
[plan_id, lambda do |input_data|
|
72
|
+
cache[plan_id] ||= accessor_fn.call(input_data)
|
73
|
+
end]
|
74
|
+
end.to_h
|
46
75
|
end
|
47
76
|
end
|
48
77
|
end
|
@@ -62,6 +62,7 @@ module Kumi
|
|
62
62
|
@reg = registry
|
63
63
|
@input_metadata = input_metadata.freeze
|
64
64
|
@decl = @ir.decls.map { |d| [d.name, d] }.to_h
|
65
|
+
@accessor_cache = {} # Persistent accessor cache across evaluations
|
65
66
|
end
|
66
67
|
|
67
68
|
def decl?(name) = @decl.key?(name)
|
@@ -85,12 +86,20 @@ module Kumi
|
|
85
86
|
def eval_decl(name, input, mode: :ruby)
|
86
87
|
raise Kumi::Core::Errors::RuntimeError, "unknown decl #{name}" unless decl?(name)
|
87
88
|
|
88
|
-
out = Kumi::Core::IR::ExecutionEngine.run(@ir, { input: input, target: name },
|
89
|
+
out = Kumi::Core::IR::ExecutionEngine.run(@ir, { input: input, target: name, accessor_cache: @accessor_cache },
|
89
90
|
accessors: @acc, registry: @reg).fetch(name)
|
90
91
|
|
91
92
|
mode == :ruby ? unwrap(@decl[name], out) : out
|
92
93
|
end
|
93
94
|
|
95
|
+
def clear_field_accessor_cache(field_name)
|
96
|
+
# Clear cache entries for all accessor plans related to this field
|
97
|
+
# Cache keys are now [plan_id, input_key] arrays
|
98
|
+
@accessor_cache.delete_if { |cache_key, _|
|
99
|
+
cache_key.is_a?(Array) && cache_key[0].to_s.start_with?("#{field_name}:")
|
100
|
+
}
|
101
|
+
end
|
102
|
+
|
94
103
|
private
|
95
104
|
|
96
105
|
def validate_keys(keys)
|
@@ -153,9 +162,12 @@ module Kumi
|
|
153
162
|
|
154
163
|
# Update the input data
|
155
164
|
@input = deep_merge(@input, { field => value })
|
165
|
+
|
166
|
+
# Clear accessor cache for this specific field
|
167
|
+
@program.clear_field_accessor_cache(field)
|
156
168
|
end
|
157
169
|
|
158
|
-
# Clear cache after all updates
|
170
|
+
# Clear declaration evaluation cache after all updates
|
159
171
|
@cache.clear
|
160
172
|
self
|
161
173
|
end
|
data/lib/kumi/version.rb
CHANGED