Skip to content

Commit 0e96924

Browse files
authored
Type relations cache: optimize key generation (#19120)
1 parent 5a67a28 commit 0e96924

File tree

2 files changed

+120
-74
lines changed

2 files changed

+120
-74
lines changed
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
### Fixed
2+
3+
* Type relations cache: optimize key generation ([Issue #19116](https://github.com/dotnet/fsharp/issues/18767)) ([PR #19120](https://github.com/dotnet/fsharp/pull/19120))

src/Compiler/Utilities/TypeHashing.fs

Lines changed: 117 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -389,147 +389,190 @@ module StructuralUtilities =
389389

390390
[<Struct; NoComparison; RequireQualifiedAccess>]
391391
type TypeToken =
392-
| Stamp of stamp: Stamp
393-
| UCase of name: string
394-
| Nullness of nullness: NullnessInfo
392+
| Stamp of int
393+
| UCase of int
394+
| Nullness of int
395395
| NullnessUnsolved
396-
| TupInfo of b: bool
396+
| TupInfo of int
397397
| Forall of int
398398
| MeasureOne
399-
| MeasureRational of int * int
399+
| MeasureDenominator of int
400+
| MeasureNumerator of int
400401
| Solved of int
401402
| Unsolved of int
402403
| Rigid of int
403404

404405
type TypeStructure =
405406
| Stable of TypeToken[]
407+
// Unstable means that the type structure of a given TType may change because of constraint solving or Trace.Undo.
406408
| Unstable of TypeToken[]
407409
| PossiblyInfinite
408410

409-
type private EmitContext =
410-
{
411-
typarMap: System.Collections.Generic.Dictionary<Stamp, int>
412-
emitNullness: bool
413-
mutable stable: bool
414-
}
411+
type private GenerationContext() =
412+
member val TyparMap = System.Collections.Generic.Dictionary<Stamp, int>(4)
413+
member val Tokens = ResizeArray<TypeToken>(256)
414+
member val EmitNullness = false with get, set
415+
member val Stable = true with get, set
415416

416-
let private emitNullness env (n: Nullness) =
417-
seq {
418-
if env.emitNullness then
419-
env.stable <- false //
417+
member this.Reset() =
418+
this.TyparMap.Clear()
419+
this.Tokens.Clear()
420+
this.EmitNullness <- false
421+
this.Stable <- true
420422

423+
let private context =
424+
new System.Threading.ThreadLocal<GenerationContext>(fun () -> GenerationContext())
425+
426+
let private getContext () =
427+
let ctx = context.Value
428+
ctx.Reset()
429+
ctx
430+
431+
let inline private encodeNullness (n: NullnessInfo) =
432+
match n with
433+
| NullnessInfo.AmbivalentToNull -> 0
434+
| NullnessInfo.WithNull -> 1
435+
| NullnessInfo.WithoutNull -> 2
436+
437+
let private emitNullness (ctx: GenerationContext) (n: Nullness) =
438+
if ctx.EmitNullness then
439+
ctx.Stable <- false
440+
441+
let out = ctx.Tokens
442+
443+
if out.Count < 256 then
421444
match n.TryEvaluate() with
422-
| ValueSome k -> TypeToken.Nullness k
423-
| ValueNone -> TypeToken.NullnessUnsolved
424-
}
445+
| ValueSome k -> out.Add(TypeToken.Nullness(encodeNullness k))
446+
| ValueNone -> out.Add(TypeToken.NullnessUnsolved)
447+
448+
let inline private emitStamp (ctx: GenerationContext) (stamp: Stamp) =
449+
let out = ctx.Tokens
450+
451+
if out.Count < 256 then
452+
// Emit low 32 bits first
453+
let lo = int (stamp &&& 0xFFFFFFFFL)
454+
out.Add(TypeToken.Stamp lo)
455+
// If high 32 bits are non-zero, emit them as another token
456+
let hi64 = stamp >>> 32
457+
458+
if hi64 <> 0L && out.Count < 256 then
459+
out.Add(TypeToken.Stamp(int hi64))
425460

426-
let rec private emitMeasure (m: Measure) =
427-
seq {
461+
let rec private emitMeasure (ctx: GenerationContext) (m: Measure) =
462+
let out = ctx.Tokens
463+
464+
if out.Count >= 256 then
465+
()
466+
else
428467
match m with
429-
| Measure.Var mv -> TypeToken.Stamp mv.Stamp
430-
| Measure.Const(tcref, _) -> TypeToken.Stamp tcref.Stamp
468+
| Measure.Var mv -> emitStamp ctx mv.Stamp
469+
| Measure.Const(tcref, _) -> emitStamp ctx tcref.Stamp
431470
| Measure.Prod(m1, m2, _) ->
432-
yield! emitMeasure m1
433-
yield! emitMeasure m2
434-
| Measure.Inv m1 -> yield! emitMeasure m1
435-
| Measure.One _ -> TypeToken.MeasureOne
471+
emitMeasure ctx m1
472+
emitMeasure ctx m2
473+
| Measure.Inv m1 -> emitMeasure ctx m1
474+
| Measure.One _ -> out.Add(TypeToken.MeasureOne)
436475
| Measure.RationalPower(m1, r) ->
437-
yield! emitMeasure m1
438-
TypeToken.MeasureRational(GetNumerator r, GetDenominator r)
439-
}
476+
emitMeasure ctx m1
477+
478+
if out.Count < 256 then
479+
out.Add(TypeToken.MeasureNumerator(GetNumerator r))
480+
out.Add(TypeToken.MeasureDenominator(GetDenominator r))
481+
482+
let rec private emitTType (ctx: GenerationContext) (ty: TType) =
483+
let out = ctx.Tokens
440484

441-
and private emitTType (env: EmitContext) (ty: TType) =
442-
seq {
485+
if out.Count >= 256 then
486+
()
487+
else
443488
match ty with
444489
| TType_ucase(u, tinst) ->
445-
TypeToken.Stamp u.TyconRef.Stamp
446-
TypeToken.UCase u.CaseName
490+
emitStamp ctx u.TyconRef.Stamp
491+
492+
if out.Count < 256 then
493+
out.Add(TypeToken.UCase(hashText u.CaseName))
447494

448495
for arg in tinst do
449-
yield! emitTType env arg
496+
emitTType ctx arg
450497

451498
| TType_app(tcref, tinst, n) ->
452-
TypeToken.Stamp tcref.Stamp
453-
yield! emitNullness env n
499+
emitStamp ctx tcref.Stamp
500+
emitNullness ctx n
454501

455502
for arg in tinst do
456-
yield! emitTType env arg
503+
emitTType ctx arg
457504

458505
| TType_anon(info, tys) ->
459-
TypeToken.Stamp info.Stamp
506+
emitStamp ctx info.Stamp
460507

461508
for arg in tys do
462-
yield! emitTType env arg
509+
emitTType ctx arg
463510

464511
| TType_tuple(tupInfo, tys) ->
465-
TypeToken.TupInfo(evalTupInfoIsStruct tupInfo)
512+
out.Add(TypeToken.TupInfo(if evalTupInfoIsStruct tupInfo then 1 else 0))
466513

467514
for arg in tys do
468-
yield! emitTType env arg
515+
emitTType ctx arg
469516

470517
| TType_forall(tps, tau) ->
471518
for tp in tps do
472-
env.typarMap.[tp.Stamp] <- env.typarMap.Count
519+
ctx.TyparMap.[tp.Stamp] <- ctx.TyparMap.Count
473520

474-
TypeToken.Forall tps.Length
521+
out.Add(TypeToken.Forall tps.Length)
475522

476-
yield! emitTType env tau
523+
emitTType ctx tau
477524

478525
| TType_fun(d, r, n) ->
479-
yield! emitTType env d
480-
yield! emitTType env r
481-
yield! emitNullness env n
526+
emitTType ctx d
527+
emitTType ctx r
528+
emitNullness ctx n
482529

483530
| TType_var(r, n) ->
484-
yield! emitNullness env n
531+
emitNullness ctx n
485532

486533
let typarId =
487-
match env.typarMap.TryGetValue r.Stamp with
534+
match ctx.TyparMap.TryGetValue r.Stamp with
488535
| true, idx -> idx
489536
| _ ->
490-
let idx = env.typarMap.Count
491-
env.typarMap.[r.Stamp] <- idx
537+
let idx = ctx.TyparMap.Count
538+
ctx.TyparMap.[r.Stamp] <- idx
492539
idx
493540

494541
// Solved may become unsolved, in case of Trace.Undo.
495-
env.stable <- false
542+
if not r.IsFromError then
543+
ctx.Stable <- false
496544

497545
match r.Solution with
498-
| Some ty -> yield! emitTType env ty
546+
| Some ty -> emitTType ctx ty
499547
| None ->
500-
if r.Rigidity = TyparRigidity.Rigid then
501-
TypeToken.Rigid typarId
502-
else
503-
TypeToken.Unsolved typarId
504-
505-
| TType_measure m -> yield! emitMeasure m
506-
}
548+
if out.Count < 256 then
549+
if r.Rigidity = TyparRigidity.Rigid then
550+
out.Add(TypeToken.Rigid typarId)
551+
else
552+
out.Add(TypeToken.Unsolved typarId)
507553

508-
let private getTypeStructureOfStrippedType (ty: TType) =
554+
| TType_measure m -> emitMeasure ctx m
509555

510-
let env =
511-
{
512-
typarMap = System.Collections.Generic.Dictionary<Stamp, int>()
513-
emitNullness = false
514-
stable = true
515-
}
556+
let private getTypeStructureOfStrippedTypeUncached (ty: TType) =
557+
let ctx = getContext ()
558+
emitTType ctx ty
516559

517-
let tokens = emitTType env ty |> Seq.truncate 256 |> Seq.toArray
560+
let out = ctx.Tokens
518561

519562
// If the sequence got too long, just drop it, we could be dealing with an infinite type.
520-
if tokens.Length = 256 then PossiblyInfinite
521-
elif not env.stable then Unstable tokens
522-
else Stable tokens
563+
if out.Count >= 256 then PossiblyInfinite
564+
elif not ctx.Stable then Unstable(out.ToArray())
565+
else Stable(out.ToArray())
523566

524567
// Speed up repeated calls by memoizing results for types that yield a stable structure.
525-
let private memoize =
568+
let private getTypeStructureOfStrippedType =
526569
WeakMap.cacheConditionally
527570
(function
528571
| Stable _ -> true
529572
| _ -> false)
530-
getTypeStructureOfStrippedType
573+
getTypeStructureOfStrippedTypeUncached
531574

532575
let tryGetTypeStructureOfStrippedType ty =
533-
match memoize ty with
576+
match getTypeStructureOfStrippedType ty with
534577
| PossiblyInfinite -> ValueNone
535578
| ts -> ValueSome ts

0 commit comments

Comments
 (0)