Skip to content

Commit a9b8abf

Browse files
perf/refactor: convert ILFieldDefs/ILEventDefs/ILPropertyDefs to concrete classes with lazy name-lookup caches
- Replace abstract interface ILFieldDefs/ILEventDefs/ILPropertyDefs with concrete classes mirroring the existing ILMethodDefs pattern - Each class holds a Lazy<T[]> for entries and a lazy Dictionary<string,T> for O(1) name lookups via TryFindByName - seekReadFields/seekReadEvents/seekReadProperties now pass lazy computations to the constructors, so entries are computed at most once (previously every .Entries access on events/properties re-ran the entire range scan) - TargetTypeDefinition.GetField/GetPropertyImpl/GetEvent now O(1) via lazy fieldDefsMap/propDefsMap/eventDefsMap dictionaries over the wrapped reflection objects - TypeSymbol.GetField/GetPropertyImpl/GetEvent for TargetGeneric now use TryFindByName instead of Array.tryFind - TargetTypeDefinition.GetEnumUnderlyingType uses TryFindByName instead of Array.tryFind for the 'value__' field lookup - All creation sites updated (empty constants, binary reader, ProvidedTypeBuilder) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent b5323d0 commit a9b8abf

1 file changed

Lines changed: 63 additions & 32 deletions

File tree

src/ProvidedTypes.fs

Lines changed: 63 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -2920,8 +2920,15 @@ module internal AssemblyReader =
29202920
member x.IsRTSpecialName = (x.Attributes &&& EventAttributes.RTSpecialName) <> enum<_>(0)
29212921
override x.ToString() = "event " + x.Name
29222922

2923-
type ILEventDefs =
2924-
abstract Entries: ILEventDef[]
2923+
type ILEventDefs(larr: Lazy<ILEventDef[]>) =
2924+
let lmap = lazy (
2925+
let d = Dictionary<string, ILEventDef>()
2926+
for e in larr.Force() do d.[e.Name] <- e
2927+
d)
2928+
member __.Entries = larr.Force()
2929+
member __.TryFindByName nm =
2930+
let scc, v = lmap.Value.TryGetValue(nm)
2931+
if scc then Some v else None
29252932

29262933
[<NoComparison; NoEquality>]
29272934
type ILPropertyDef =
@@ -2947,8 +2954,15 @@ module internal AssemblyReader =
29472954
member x.IsRTSpecialName = x.Attributes &&& PropertyAttributes.RTSpecialName <> enum 0
29482955
override x.ToString() = "property " + x.Name
29492956

2950-
type ILPropertyDefs =
2951-
abstract Entries: ILPropertyDef[]
2957+
type ILPropertyDefs(larr: Lazy<ILPropertyDef[]>) =
2958+
let lmap = lazy (
2959+
let d = Dictionary<string, ILPropertyDef>()
2960+
for p in larr.Force() do d.[p.Name] <- p
2961+
d)
2962+
member __.Entries = larr.Force()
2963+
member __.TryFindByName nm =
2964+
let scc, v = lmap.Value.TryGetValue(nm)
2965+
if scc then Some v else None
29522966

29532967
[<NoComparison; NoEquality>]
29542968
type ILFieldDef =
@@ -2980,8 +2994,15 @@ module internal AssemblyReader =
29802994
override x.ToString() = "field " + x.Name
29812995

29822996

2983-
type ILFieldDefs =
2984-
abstract Entries: ILFieldDef[]
2997+
type ILFieldDefs(larr: Lazy<ILFieldDef[]>) =
2998+
let lmap = lazy (
2999+
let d = Dictionary<string, ILFieldDef>()
3000+
for f in larr.Force() do d.[f.Name] <- f
3001+
d)
3002+
member __.Entries = larr.Force()
3003+
member __.TryFindByName nm =
3004+
let scc, v = lmap.Value.TryGetValue(nm)
3005+
if scc then Some v else None
29853006

29863007
type ILMethodImplDef =
29873008
{ Overrides: ILOverridesSpec
@@ -4692,14 +4713,14 @@ module internal AssemblyReader =
46924713
let td = ltd.Force()
46934714
(td.Name, ltd)
46944715

4695-
let emptyILEvents = { new ILEventDefs with member __.Entries = [| |] }
4696-
let emptyILProperties = { new ILPropertyDefs with member __.Entries = [| |] }
4716+
let emptyILEvents = ILEventDefs(lazy [| |])
4717+
let emptyILProperties = ILPropertyDefs(lazy [| |])
46974718
let emptyILTypeDefs = ILTypeDefs (lazy [| |])
46984719
let emptyILCustomAttrs = { new ILCustomAttrs with member __.Entries = [| |] }
46994720
let mkILCustomAttrs x = { new ILCustomAttrs with member __.Entries = x }
47004721
let emptyILMethodImpls = { new ILMethodImplDefs with member __.Entries = [| |] }
47014722
let emptyILMethods = ILMethodDefs (lazy [| |])
4702-
let emptyILFields = { new ILFieldDefs with member __.Entries = [| |] }
4723+
let emptyILFields = ILFieldDefs(lazy [| |])
47034724

47044725
let mkILTy boxed tspec =
47054726
match boxed with
@@ -5771,10 +5792,7 @@ module internal AssemblyReader =
57715792
Token = idx }
57725793

57735794
and seekReadFields (numtypars, hasLayout) fidx1 fidx2 =
5774-
{ new ILFieldDefs with
5775-
member __.Entries =
5776-
[| for i = fidx1 to fidx2 - 1 do
5777-
yield seekReadField (numtypars, hasLayout) i |] }
5795+
ILFieldDefs(lazy [| for i = fidx1 to fidx2 - 1 do yield seekReadField (numtypars, hasLayout) i |])
57785796

57795797
and seekReadMethods numtypars midx1 midx2 =
57805798
ILMethodDefs
@@ -6100,8 +6118,8 @@ module internal AssemblyReader =
61006118
Token = idx}
61016119

61026120
and seekReadEvents numtypars tidx =
6103-
{ new ILEventDefs with
6104-
member __.Entries =
6121+
let entries =
6122+
lazy (
61056123
match seekReadOptionalIndexedRow (getNumRows ILTableNames.EventMap, (fun i -> i, seekReadEventMapRow i), (fun (_, row) -> fst row), compare tidx, false, (fun (i, row) -> (i, snd row))) with
61066124
| None -> [| |]
61076125
| Some (rowNum, beginEventIdx) ->
@@ -6111,9 +6129,9 @@ module internal AssemblyReader =
61116129
else
61126130
let (_, endEventIdx) = seekReadEventMapRow (rowNum + 1)
61136131
endEventIdx
6114-
61156132
[| for i in beginEventIdx .. endEventIdx - 1 do
6116-
yield seekReadEvent numtypars i |] }
6133+
yield seekReadEvent numtypars i |])
6134+
ILEventDefs(entries)
61176135

61186136
and seekReadProperty numtypars idx =
61196137
let (flags, nameIdx, typIdx) = seekReadPropertyRow idx
@@ -6139,8 +6157,8 @@ module internal AssemblyReader =
61396157
Token = idx }
61406158

61416159
and seekReadProperties numtypars tidx =
6142-
{ new ILPropertyDefs with
6143-
member __.Entries =
6160+
let entries =
6161+
lazy (
61446162
match seekReadOptionalIndexedRow (getNumRows ILTableNames.PropertyMap, (fun i -> i, seekReadPropertyMapRow i), (fun (_, row) -> fst row), compare tidx, false, (fun (i, row) -> (i, snd row))) with
61456163
| None -> [| |]
61466164
| Some (rowNum, beginPropIdx) ->
@@ -6151,7 +6169,8 @@ module internal AssemblyReader =
61516169
let (_, endPropIdx) = seekReadPropertyMapRow (rowNum + 1)
61526170
endPropIdx
61536171
[| for i in beginPropIdx .. endPropIdx - 1 do
6154-
yield seekReadProperty numtypars i |] }
6172+
yield seekReadProperty numtypars i |])
6173+
ILPropertyDefs(entries)
61556174

61566175

61576176
and seekReadCustomAttrs idx =
@@ -7514,7 +7533,7 @@ namespace ProviderImplementation.ProvidedTypes
75147533
override this.GetField(name, bindingFlags) =
75157534
match kind with
75167535
| TypeSymbolKind.TargetGeneric gtd ->
7517-
gtd.Metadata.Fields.Entries |> Array.tryFind (fun md -> md.Name = name)
7536+
gtd.Metadata.Fields.TryFindByName(name)
75187537
|> Option.map (gtd.MakeFieldInfo this)
75197538
|> Option.toObj
75207539
| TypeSymbolKind.OtherGeneric gtd ->
@@ -7528,8 +7547,7 @@ namespace ProviderImplementation.ProvidedTypes
75287547
override this.GetPropertyImpl(name, bindingFlags, _binder, _returnType, _types, _modifiers) =
75297548
match kind with
75307549
| TypeSymbolKind.TargetGeneric gtd ->
7531-
gtd.Metadata.Properties.Entries
7532-
|> Array.tryFind (fun md -> md.Name = name)
7550+
gtd.Metadata.Properties.TryFindByName(name)
75337551
|> Option.map (gtd.MakePropertyInfo this)
75347552
|> Option.toObj
75357553
| TypeSymbolKind.OtherGeneric gtd ->
@@ -7543,8 +7561,7 @@ namespace ProviderImplementation.ProvidedTypes
75437561
override this.GetEvent(name, bindingFlags) =
75447562
match kind with
75457563
| TypeSymbolKind.TargetGeneric gtd ->
7546-
gtd.Metadata.Events.Entries
7547-
|> Array.tryFind (fun md -> md.Name = name)
7564+
gtd.Metadata.Events.TryFindByName(name)
75487565
|> Option.map (gtd.MakeEventInfo this)
75497566
|> Option.toObj
75507567
| TypeSymbolKind.OtherGeneric gtd ->
@@ -7994,6 +8011,20 @@ namespace ProviderImplementation.ProvidedTypes
79948011
let propDefs = lazy (inp.Properties.Entries |> Array.map (txILPropertyDef this))
79958012
let nestedDefs = lazy (inp.NestedTypes.Entries |> Array.map (asm.TxILTypeDef (Some (this :> Type))))
79968013

8014+
// O(1) name-lookup dicts for GetField/GetPropertyImpl/GetEvent
8015+
let fieldDefsMap =
8016+
lazy (let d = Dictionary<string,FieldInfo>()
8017+
for f in fieldDefs.Force() do d.[f.Name] <- f
8018+
d)
8019+
let propDefsMap =
8020+
lazy (let d = Dictionary<string,PropertyInfo>()
8021+
for p in propDefs.Force() do d.[p.Name] <- p
8022+
d)
8023+
let eventDefsMap =
8024+
lazy (let d = Dictionary<string,EventInfo>()
8025+
for e in eventDefs.Force() do d.[e.Name] <- e
8026+
d)
8027+
79978028
// Cache derived properties that are computed from immutable input data.
79988029
// The F# compiler may call FullName, BaseType and GetInterfaces() many times per type
79998030
// during type-checking; caching avoids repeated string allocations and type-resolution work.
@@ -8063,13 +8094,13 @@ namespace ProviderImplementation.ProvidedTypes
80638094
md |> Option.map (txILMethodDef this) |> Option.toObj
80648095

80658096
override this.GetField(name, _bindingFlags) =
8066-
fieldDefs.Force() |> Array.tryFind (fun f -> f.Name = name) |> Option.toObj
8097+
let scc, f = fieldDefsMap.Value.TryGetValue(name) in if scc then f else null
80678098

80688099
override this.GetPropertyImpl(name, _bindingFlags, _binder, _returnType, _types, _modifiers) =
8069-
propDefs.Force() |> Array.tryFind (fun p -> p.Name = name) |> Option.toObj
8100+
let scc, p = propDefsMap.Value.TryGetValue(name) in if scc then p else null
80708101

80718102
override this.GetEvent(name, _bindingFlags) =
8072-
eventDefs.Force() |> Array.tryFind (fun ev -> ev.Name = name) |> Option.toObj
8103+
let scc, e = eventDefsMap.Value.TryGetValue(name) in if scc then e else null
80738104

80748105
override this.GetNestedType(name, _bindingFlags) =
80758106
inp.NestedTypes.TryFindByName(UNone, name) |> Option.map (asm.TxILTypeDef (Some (this :> Type))) |> Option.toObj
@@ -8107,7 +8138,7 @@ namespace ProviderImplementation.ProvidedTypes
81078138
if this.IsEnum then
81088139
// Read the underlying type from the special "value__" field that the compiler emits for every enum.
81098140
// This correctly handles non-Int32 backing types (byte, int16, int64, etc.).
8110-
let valueField = inp.Fields.Entries |> Array.tryFind (fun f -> f.Name = "value__")
8141+
let valueField = inp.Fields.TryFindByName("value__")
81118142
match valueField with
81128143
| Some f -> txILType ([| |], [| |]) f.FieldType
81138144
| None -> txILType ([| |], [| |]) ilGlobals.typ_Int32
@@ -14111,9 +14142,9 @@ namespace ProviderImplementation.ProvidedTypes
1411114142
//SecurityDecls=emptyILSecurityDecls;
1411214143
//HasSecurity=false;
1411314144
NestedTypes = ILTypeDefs( lazy [| for x in nestedTypes -> let td = x.Content in td.Namespace, td.Name, lazy td |] )
14114-
Fields = { new ILFieldDefs with member __.Entries = [| for x in fields -> x.Content |] }
14115-
Properties = { new ILPropertyDefs with member __.Entries = [| for x in props -> x.Content |] }
14116-
Events = { new ILEventDefs with member __.Entries = [| for x in events -> x.Content |] }
14145+
Fields = ILFieldDefs(lazy [| for x in fields -> x.Content |])
14146+
Properties = ILPropertyDefs(lazy [| for x in props -> x.Content |])
14147+
Events = ILEventDefs(lazy [| for x in events -> x.Content |])
1411714148
Methods = ILMethodDefs (lazy [| for x in methods -> x.Content |])
1411814149
MethodImpls = { new ILMethodImplDefs with member __.Entries = methodImpls.ToArray() }
1411914150
CustomAttrs = mkILCustomAttrs (cattrs.ToArray())

0 commit comments

Comments
 (0)