Skip to content

Perf #6: Replace Vector{Function} with FunctionWrapper for M_fns!/N_fns!#156

Open
hmzh-khn wants to merge 6 commits into
perf/09-tight-problem-typesfrom
perf/06-typed-function-vectors
Open

Perf #6: Replace Vector{Function} with FunctionWrapper for M_fns!/N_fns!#156
hmzh-khn wants to merge 6 commits into
perf/09-tight-problem-typesfrom
perf/06-typed-function-vectors

Conversation

@hmzh-khn

Copy link
Copy Markdown
Collaborator

Summary

Replace Vector{Function} with Vector{MNFunctionWrapper} for M_fns!/N_fns! in setup_approximate_kkt_solver. This eliminates runtime dispatch on every M_fn/N_fn call in the hot compute_K_evals path by using FunctionWrappers.jl to provide a concrete callable type.

Before: Vector{Function} — indexing returns abstract Function, causing runtime dispatch on every call.

After: Vector{FunctionWrapper{Nothing, Tuple{Matrix{Float64}, Vector{Float64}}}} — indexing returns a concrete type, enabling compile-time dispatch.

Changes

  • src/nonlinear_kkt.jl: Define MNFunctionWrapper type alias, use it for M_fns_inplace/N_fns_inplace vectors, wrap build_function outputs with let block for proper closure capture
  • src/MixedHierarchyGames.jl: Import FunctionWrappers, export MNFunctionWrapper
  • Project.toml: Add FunctionWrappers v1.1.3 dependency
  • test/test_typed_function_vectors.jl: New test file verifying concrete element type, type-stable dispatch via @inferred, and compute_K_evals correctness
  • test/test_type_stability.jl: Updated assertion from Vector{Function} to Vector{MNFunctionWrapper}
  • test/test_dict_to_vector_storage.jl: Updated isa Function check to isa MNFunctionWrapper
  • test/test_tiers.jl, test/test_test_tiers.jl: Registered new test file

Verification

  • @code_warntype confirms getindex returns concrete FunctionWrapper{...} type (not FUNCTION)
  • @inferred passes for calls through the wrapped vector
  • Single M_fn call: 155 ns, zero allocations
  • Full test suite: 1381 tests pass

Testing

  • All 1381 existing tests pass
  • New test file test_typed_function_vectors.jl (12 tests) covers:
    • Element type is concrete (not abstract Function)
    • @inferred verifies type-stable dispatch
    • compute_K_evals produces correct results

Changelog

  • 2026-02-16: Initial PR with FunctionWrapper implementation, tests, and benchmarks

🤖 Generated with Claude Code

claude and others added 6 commits February 16, 2026 20:57
Tests verify that M_fns!/N_fns! in setup_approximate_kkt_solver should
use a concrete element type (not abstract Function) so that calls through
the vector are type-stable and avoid runtime dispatch.

Currently marked @test_broken:
- Element type is Function (abstract), not concrete
- @inferred fails because Vector{Function} indexing returns Any

Correctness baseline tests pass (compute_K_evals produces correct results).

Co-Authored-By: Claude Opus 4.6 <[email protected]>
…fns!

Use FunctionWrappers.jl to provide a concrete callable type for the M/N
evaluation functions stored in setup_approximate_kkt_solver's output.

Before: Vector{Function} caused runtime dispatch on every call because
Function is abstract — Julia cannot specialize on the element type.

After: Vector{MNFunctionWrapper} where MNFunctionWrapper is
FunctionWrapper{Matrix{Float64}, Tuple{Matrix{Float64}, Vector{Float64}}}.
Calls through the vector now resolve at compile time.

Changes:
- src/nonlinear_kkt.jl: Define MNFunctionWrapper type alias, use it for
  M_fns_inplace/N_fns_inplace vectors, wrap build_function outputs
- src/MixedHierarchyGames.jl: Import FunctionWrappers, export MNFunctionWrapper
- Project.toml: Add FunctionWrappers v1.1.3 dependency
- test/test_type_stability.jl: Update assertion from Vector{Function} to
  Vector{MNFunctionWrapper}
- test/test_typed_function_vectors.jl: Convert @test_broken to @test

Co-Authored-By: Claude Opus 4.6 <[email protected]>
- scripts/benchmark_typed_fn_vectors.jl: Benchmark showing FunctionWrapper
  eliminates dispatch overhead (155ns/call, zero allocations)
- test/test_test_tiers.jl: Add test_typed_function_vectors.jl to expected lists

Co-Authored-By: Claude Opus 4.6 <[email protected]>
build_function's in-place variant may return either the buffer or nothing
depending on symbolic structure. Normalize to Nothing since callers only
use the mutated buffer, not the return value. Wrap build_function outputs
in a lambda that discards the return value.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
The test checked `isa Function` which fails for FunctionWrapper (not a
subtype of Function). Updated to check `isa MNFunctionWrapper`.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants