Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use LockFreeReaderHashtable for some of the NodeCaches #79805

Merged
merged 2 commits into from
Dec 19, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -164,9 +164,9 @@ public TValue GetOrAdd(TKey key, Func<TKey, TValue> creator)

private void CreateNodeCaches()
{
_typeSymbols = new NodeCache<TypeDesc, IEETypeNode>(CreateNecessaryTypeNode);
_typeSymbols = new NecessaryTypeSymbolHashtable(this);

_constructedTypeSymbols = new NodeCache<TypeDesc, IEETypeNode>(CreateConstructedTypeNode);
_constructedTypeSymbols = new ConstructedTypeSymbolHashtable(this);

_clonedTypeSymbols = new NodeCache<TypeDesc, IEETypeNode>((TypeDesc type) =>
{
Expand Down Expand Up @@ -253,7 +253,7 @@ private void CreateNodeCaches()
return new PInvokeMethodFixupNode(methodData);
});

_methodEntrypoints = new NodeCache<MethodDesc, IMethodNode>(CreateMethodEntrypointNode);
_methodEntrypoints = new MethodEntrypointHashtable(this);

_unboxingStubs = new NodeCache<MethodDesc, IMethodNode>(CreateUnboxingStubNode);

Expand Down Expand Up @@ -292,29 +292,16 @@ private void CreateNodeCaches()
return new ObjectGetTypeFlowDependenciesNode(type);
});

_shadowConcreteMethods = new NodeCache<MethodKey, IMethodNode>(methodKey =>
{
MethodDesc canonMethod = methodKey.Method.GetCanonMethodTarget(CanonicalFormKind.Specific);

if (methodKey.IsUnboxingStub)
{
return new ShadowConcreteUnboxingThunkNode(methodKey.Method, MethodEntrypoint(canonMethod, true));
}
else
{
return new ShadowConcreteMethodNode(methodKey.Method, MethodEntrypoint(canonMethod));
}
});
_shadowConcreteMethods = new ShadowConcreteMethodHashtable(this);

_virtMethods = new NodeCache<MethodDesc, VirtualMethodUseNode>((MethodDesc method) =>
_shadowConcreteUnboxingMethods = new NodeCache<MethodDesc, ShadowConcreteUnboxingThunkNode>(method =>
{
// We don't need to track virtual method uses for types that have a vtable with a known layout.
// It's a waste of CPU time and memory.
Debug.Assert(!VTable(method.OwningType).HasFixedSlots);

return new VirtualMethodUseNode(method);
MethodDesc canonMethod = method.GetCanonMethodTarget(CanonicalFormKind.Specific);
return new ShadowConcreteUnboxingThunkNode(method, MethodEntrypoint(canonMethod, true));
});

_virtMethods = new VirtualMethodUseHashtable(this);

_variantMethods = new NodeCache<MethodDesc, VariantInterfaceMethodUseNode>((MethodDesc method) =>
{
// We don't need to track virtual method uses for types that have a vtable with a known layout.
Expand Down Expand Up @@ -401,13 +388,7 @@ private void CreateNodeCaches()
return new StructMarshallingDataNode(type);
});

_vTableNodes = new NodeCache<TypeDesc, VTableSliceNode>((TypeDesc type ) =>
{
if (CompilationModuleGroup.ShouldProduceFullVTable(type))
return new EagerlyBuiltVTableSliceNode(type);
else
return _vtableSliceProvider.GetSlice(type);
});
_vTableNodes = new VTableSliceHashtable(this);

_methodGenericDictionaries = new NodeCache<MethodDesc, ISortableSymbolNode>(method =>
{
Expand Down Expand Up @@ -479,7 +460,7 @@ protected virtual ISymbolNode CreateGenericLookupFromTypeNode(ReadyToRunGenericH
return new ReadyToRunGenericLookupFromTypeNode(this, helperKey.HelperId, helperKey.Target, helperKey.DictionaryOwner);
}

protected virtual IEETypeNode CreateNecessaryTypeNode(TypeDesc type)
private IEETypeNode CreateNecessaryTypeNode(TypeDesc type)
{
Debug.Assert(!_compilationModuleGroup.ShouldReferenceThroughImportTable(type));
if (_compilationModuleGroup.ContainsType(type))
Expand Down Expand Up @@ -507,7 +488,7 @@ protected virtual IEETypeNode CreateNecessaryTypeNode(TypeDesc type)
}
}

protected virtual IEETypeNode CreateConstructedTypeNode(TypeDesc type)
private IEETypeNode CreateConstructedTypeNode(TypeDesc type)
{
// Canonical definition types are *not* constructed types (call NecessaryTypeSymbol to get them)
Debug.Assert(!type.IsCanonicalDefinitionType(CanonicalFormKind.Any));
Expand Down Expand Up @@ -541,7 +522,23 @@ protected virtual ISymbolDefinitionNode CreateThreadStaticsNode(MetadataType typ
return new ThreadStaticsNode(type, this);
}

private NodeCache<TypeDesc, IEETypeNode> _typeSymbols;
private abstract class TypeSymbolHashtable : LockFreeReaderHashtable<TypeDesc, IEETypeNode>
{
protected readonly NodeFactory _factory;
public TypeSymbolHashtable(NodeFactory factory) => _factory = factory;
protected override bool CompareKeyToValue(TypeDesc key, IEETypeNode value) => key == value.Type;
protected override bool CompareValueToValue(IEETypeNode value1, IEETypeNode value2) => value1.Type == value2.Type;
protected override int GetKeyHashCode(TypeDesc key) => key.GetHashCode();
protected override int GetValueHashCode(IEETypeNode value) => value.Type.GetHashCode();
}

private sealed class NecessaryTypeSymbolHashtable : TypeSymbolHashtable
{
public NecessaryTypeSymbolHashtable(NodeFactory factory) : base(factory) { }
protected override IEETypeNode CreateValueFromKey(TypeDesc key) => _factory.CreateNecessaryTypeNode(key);
}

private NecessaryTypeSymbolHashtable _typeSymbols;

public IEETypeNode NecessaryTypeSymbol(TypeDesc type)
{
Expand All @@ -557,10 +554,16 @@ public IEETypeNode NecessaryTypeSymbol(TypeDesc type)

Debug.Assert(!TypeCannotHaveEEType(type));

return _typeSymbols.GetOrAdd(type);
return _typeSymbols.GetOrCreateValue(type);
}

private NodeCache<TypeDesc, IEETypeNode> _constructedTypeSymbols;
private sealed class ConstructedTypeSymbolHashtable : TypeSymbolHashtable
{
public ConstructedTypeSymbolHashtable(NodeFactory factory) : base(factory) { }
protected override IEETypeNode CreateValueFromKey(TypeDesc key) => _factory.CreateConstructedTypeNode(key);
}

private ConstructedTypeSymbolHashtable _constructedTypeSymbols;

public IEETypeNode ConstructedTypeSymbol(TypeDesc type)
{
Expand All @@ -571,7 +574,7 @@ public IEETypeNode ConstructedTypeSymbol(TypeDesc type)

Debug.Assert(!TypeCannotHaveEEType(type));

return _constructedTypeSymbols.GetOrAdd(type);
return _constructedTypeSymbols.GetOrCreateValue(type);
}

private NodeCache<TypeDesc, IEETypeNode> _clonedTypeSymbols;
Expand Down Expand Up @@ -758,11 +761,28 @@ public PInvokeMethodFixupNode PInvokeMethodFixup(PInvokeMethodData methodData)
return _pInvokeMethodFixups.GetOrAdd(methodData);
}

private NodeCache<TypeDesc, VTableSliceNode> _vTableNodes;
private sealed class VTableSliceHashtable : LockFreeReaderHashtable<TypeDesc, VTableSliceNode>
{
private readonly NodeFactory _factory;
public VTableSliceHashtable(NodeFactory factory) => _factory = factory;
protected override bool CompareKeyToValue(TypeDesc key, VTableSliceNode value) => key == value.Type;
protected override bool CompareValueToValue(VTableSliceNode value1, VTableSliceNode value2) => value1.Type == value2.Type;
protected override VTableSliceNode CreateValueFromKey(TypeDesc key)
{
if (_factory.CompilationModuleGroup.ShouldProduceFullVTable(key))
return new EagerlyBuiltVTableSliceNode(key);
else
return _factory._vtableSliceProvider.GetSlice(key);
}
protected override int GetKeyHashCode(TypeDesc key) => key.GetHashCode();
protected override int GetValueHashCode(VTableSliceNode value) => value.Type.GetHashCode();
}

private VTableSliceHashtable _vTableNodes;

public VTableSliceNode VTable(TypeDesc type)
{
return _vTableNodes.GetOrAdd(type);
return _vTableNodes.GetOrCreateValue(type);
}

private NodeCache<MethodDesc, ISortableSymbolNode> _methodGenericDictionaries;
Expand All @@ -789,7 +809,18 @@ public IMethodNode StringAllocator(MethodDesc stringConstructor)
return _stringAllocators.GetOrAdd(stringConstructor);
}

protected NodeCache<MethodDesc, IMethodNode> _methodEntrypoints;
private sealed class MethodEntrypointHashtable : LockFreeReaderHashtable<MethodDesc, IMethodNode>
{
private readonly NodeFactory _factory;
public MethodEntrypointHashtable(NodeFactory factory) => _factory = factory;
protected override bool CompareKeyToValue(MethodDesc key, IMethodNode value) => key == value.Method;
protected override bool CompareValueToValue(IMethodNode value1, IMethodNode value2) => value1.Method == value2.Method;
protected override IMethodNode CreateValueFromKey(MethodDesc key) => _factory.CreateMethodEntrypointNode(key);
protected override int GetKeyHashCode(MethodDesc key) => key.GetHashCode();
protected override int GetValueHashCode(IMethodNode value) => value.Method.GetHashCode();
}

private MethodEntrypointHashtable _methodEntrypoints;
private NodeCache<MethodDesc, IMethodNode> _unboxingStubs;
private NodeCache<IMethodNode, MethodAssociatedDataNode> _methodAssociatedData;

Expand All @@ -800,7 +831,7 @@ public IMethodNode MethodEntrypoint(MethodDesc method, bool unboxingStub = false
return _unboxingStubs.GetOrAdd(method);
}

return _methodEntrypoints.GetOrAdd(method);
return _methodEntrypoints.GetOrCreateValue(method);
}

public MethodAssociatedDataNode MethodAssociatedData(IMethodNode methodNode)
Expand Down Expand Up @@ -863,10 +894,26 @@ internal ObjectGetTypeFlowDependenciesNode ObjectGetTypeFlowDependencies(Metadat
return _objectGetTypeFlowDependencies.GetOrAdd(type);
}

private NodeCache<MethodKey, IMethodNode> _shadowConcreteMethods;
private sealed class ShadowConcreteMethodHashtable : LockFreeReaderHashtable<MethodDesc, ShadowConcreteMethodNode>
{
private readonly NodeFactory _factory;
public ShadowConcreteMethodHashtable(NodeFactory factory) => _factory = factory;
protected override bool CompareKeyToValue(MethodDesc key, ShadowConcreteMethodNode value) => key == value.Method;
protected override bool CompareValueToValue(ShadowConcreteMethodNode value1, ShadowConcreteMethodNode value2) => value1.Method == value2.Method;
protected override ShadowConcreteMethodNode CreateValueFromKey(MethodDesc key) =>
new ShadowConcreteMethodNode(key, _factory.MethodEntrypoint(key.GetCanonMethodTarget(CanonicalFormKind.Specific)));
protected override int GetKeyHashCode(MethodDesc key) => key.GetHashCode();
protected override int GetValueHashCode(ShadowConcreteMethodNode value) => value.Method.GetHashCode();
}

private ShadowConcreteMethodHashtable _shadowConcreteMethods;
private NodeCache<MethodDesc, ShadowConcreteUnboxingThunkNode> _shadowConcreteUnboxingMethods;
public IMethodNode ShadowConcreteMethod(MethodDesc method, bool isUnboxingStub = false)
{
return _shadowConcreteMethods.GetOrAdd(new MethodKey(method, isUnboxingStub));
if (isUnboxingStub)
return _shadowConcreteUnboxingMethods.GetOrAdd(method);
else
return _shadowConcreteMethods.GetOrCreateValue(method);
}

private static readonly string[][] s_helperEntrypointNames = new string[][] {
Expand Down Expand Up @@ -932,11 +979,28 @@ public MethodDesc InstanceMethodRemovedHelper
}
}

private NodeCache<MethodDesc, VirtualMethodUseNode> _virtMethods;
private sealed class VirtualMethodUseHashtable : LockFreeReaderHashtable<MethodDesc, VirtualMethodUseNode>
{
private readonly NodeFactory _factory;
public VirtualMethodUseHashtable(NodeFactory factory) => _factory = factory;
protected override bool CompareKeyToValue(MethodDesc key, VirtualMethodUseNode value) => key == value.Method;
protected override bool CompareValueToValue(VirtualMethodUseNode value1, VirtualMethodUseNode value2) => value1.Method == value2.Method;
protected override VirtualMethodUseNode CreateValueFromKey(MethodDesc key)
{
// We don't need to track virtual method uses for types that have a vtable with a known layout.
// It's a waste of CPU time and memory.
Debug.Assert(!_factory.VTable(key.OwningType).HasFixedSlots);
return new VirtualMethodUseNode(key);
}
protected override int GetKeyHashCode(MethodDesc key) => key.GetHashCode();
protected override int GetValueHashCode(VirtualMethodUseNode value) => value.Method.GetHashCode();
}

private VirtualMethodUseHashtable _virtMethods;

public DependencyNodeCore<NodeFactory> VirtualMethodUse(MethodDesc decl)
{
return _virtMethods.GetOrAdd(decl);
return _virtMethods.GetOrCreateValue(decl);
}

private NodeCache<MethodDesc, VariantInterfaceMethodUseNode> _variantMethods;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ internal sealed class VirtualMethodUseNode : DependencyNodeCore<NodeFactory>
{
private readonly MethodDesc _decl;

public MethodDesc Method => _decl;

public VirtualMethodUseNode(MethodDesc decl)
{
Debug.Assert(!decl.IsRuntimeDeterminedExactMethod);
Expand Down