Skip to content

[Repo Assist] perf: O(1) assembly-name dictionary lookup in convTypeRef#493

Merged
dsyme merged 2 commits intomasterfrom
repo-assist/perf-assembly-lookup-20260401-80e6ae71360ba63f
Apr 1, 2026
Merged

[Repo Assist] perf: O(1) assembly-name dictionary lookup in convTypeRef#493
dsyme merged 2 commits intomasterfrom
repo-assist/perf-assembly-lookup-20260401-80e6ae71360ba63f

Conversation

@github-actions
Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot commented Apr 1, 2026

🤖 This PR was created by Repo Assist, an automated AI assistant.

Summary

Replaces the O(n) Seq.tryFind scan in convTypeRef's bestGuess step with O(1) dictionary lookups against the existing assembly-name tables.

Root Cause / Motivation

convTypeRef translates a design-time Type to its target-context equivalent (or vice-versa). For a type t that hasn't been seen before, the previous code did:

let bestGuess =
    asms |> Seq.tryFind(fun a -> a.FullName = t.Assembly.FullName)
    |> Option.bind(fun a -> tryGetTypeFromAssembly toTgt t.Assembly.FullName fullName a)

This is an O(n) scan through all target/source assemblies for every unique type. With 100–200 reference assemblies and hundreds or thousands of distinct types in a large generated schema, this can add meaningful latency during TP warm-up.

ProvidedTypesContext already maintains two name-indexed ConcurrentDictionarys:

  • targetAssembliesTable_ keyed by assembly simple name (→ Choice<Assembly, exn>)
  • sourceAssembliesTable_ keyed by assembly simple name (→ Assembly)

Fix

Use t.Assembly.GetName().Name as a dictionary key for O(1) look-up:

let asmSimpleName = t.Assembly.GetName().Name
let bestGuess =
    if toTgt then
        let table = getTargetAssembliesTable()
        match table.TryGetValue(asmSimpleName) with
        | true, Choice1Of2 asm -> tryGetTypeFromAssembly toTgt t.Assembly.FullName fullName asm
        | _ -> None
    else
        let table = getSourceAssembliesTable()
        match table.TryGetValue(asmSimpleName) with
        | true, asm -> tryGetTypeFromAssembly toTgt t.Assembly.FullName fullName asm
        | _ -> None

The linear scan fallback (loop) is fully preserved for the rare case where the assembly isn't in the table (e.g. type forwarding across assembly boundaries). The original getTargetAssemblies()/getSourceAssemblies() call is also preserved to populate asms for the fallback.

A new getSourceAssembliesTable() helper is added (mirrors the existing getTargetAssembliesTable()).

Trade-offs

  • Simple name lookup may theoretically match the wrong assembly if two assemblies with the same simple name but different versions are present simultaneously. In practice this does not occur in type provider reference sets. The fallback loop would still catch it.
  • Per-type translation is already memoised in typeTableFwd/typeTableBwd, so each unique type is resolved at most once; the benefit is most visible during initial TP warm-up with large schemas.

Test Status

✅ All 126 tests pass (dotnet test tests/FSharp.TypeProviders.SDK.Tests.fsproj -c Release)

Generated by 🌈 Repo Assist at Run. Learn more.

Generated by 🌈 Repo Assist at {run-started}. Learn more.

To install this agentic workflow, run

gh aw add githubnext/agentics/workflows/repo-assist.md@1f672aef974f4246124860fc532f82fe8a93a57e

Replace the O(n) Seq.tryFind scan in the bestGuess step of convTypeRef
with O(1) dictionary lookups against the existing assembly-name tables
(targetAssembliesTable_ for target, sourceAssembliesTable_ for source).

The linear scan in the fallback loop is preserved for the rare case where
an assembly simple-name is not in the table (e.g. type-forwarding across
assembly boundaries). The per-type translation cache (typeTableFwd /
typeTableBwd) already means each unique type is resolved at most once, so
this change is most beneficial during the initial warm-up of a large type
provider that references many distinct types.

All 126 tests pass.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@dsyme dsyme marked this pull request as ready for review April 1, 2026 17:09
@dsyme dsyme merged commit e60615c into master Apr 1, 2026
2 checks passed
github-actions Bot added a commit that referenced this pull request Apr 7, 2026
- Performance: O(1) convTypeRef assembly-name lookup (#493)
- Performance: O(N) ILMethodDefs index construction; lazy caches in TargetTypeDefinition (#497)
- Refactor: save-based caching for GetField/GetEvent/GetNestedType; Dictionary in ILNestedExportedTypesAndForwarders (#498)
- CI: NuGet and FAKE build caching (#495)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
dsyme pushed a commit that referenced this pull request Apr 7, 2026
🤖 *This PR was created by [Repo
Assist](https://github.com/fsprojects/FSharp.TypeProviders.SDK/actions/runs/24058266578).*

## Summary

Bumps the version to **8.5.0** by adding an entry to `RELEASE_NOTES.md`
covering all changes merged since 8.4.0:

- **Performance**: O(1) assembly-name dictionary lookup in `convTypeRef`
(#493)
- **Performance**: Avoid O(n²) allocations in `ILMethodDefs` name-index
construction; reuse lazy caches in `TargetTypeDefinition` for
`GetField`/`GetPropertyImpl`/`GetEvent` (#497)
- **Refactor**: `save`-based caching for
`GetField`/`GetEvent`/`GetNestedType` on `ProvidedTypeDefinition`;
`Dictionary` in `ILNestedExportedTypesAndForwarders` (#498)
- **CI**: NuGet and FAKE build caching (#495)

No code changes — RELEASE_NOTES.md only.

## Test Status

✅ 126/126 tests pass (`dotnet test
tests/FSharp.TypeProviders.SDK.Tests.fsproj -c Release`).




> Generated by 🌈 Repo Assist, see [workflow
run](https://github.com/fsprojects/FSharp.TypeProviders.SDK/actions/runs/24058266578).
[Learn
more](https://github.com/githubnext/agentics/blob/main/docs/repo-assist.md).
>
> To install this [agentic
workflow](https://github.com/githubnext/agentics/blob/7ee2b60744abf71b985bead4599640f165edcd93/workflows/repo-assist.md),
run
> ```
> gh aw add
githubnext/agentics@7ee2b60
> ```

<!-- gh-aw-agentic-workflow: Repo Assist, engine: copilot, model: auto,
id: 24058266578, workflow_id: repo-assist, run:
https://github.com/fsprojects/FSharp.TypeProviders.SDK/actions/runs/24058266578
-->

<!-- gh-aw-workflow-id: repo-assist -->

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant