Optimize access to the default FuncPtrStub#129918
Conversation
Cache the default-type FuncPtrStub precode directly on the MethodDesc (in MethodDescCodeData) so the common GetFuncPtrStub path avoids taking the FuncPtrStubs hash-table critical section. A new field DefaultFuncPtrStub plus Set/GetDefaultFuncPtrStub accessors store and retrieve the cached precode using an interlocked publish. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
@EgorBot -arm64 using System;
using System.Reflection;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;
public interface IFoo
{
void Bar();
}
public class Foo : IFoo
{
public void Bar() { }
}
public class DelegateBindBenchmarks
{
private static readonly MethodInfo s_method = typeof(IFoo).GetMethod(nameof(IFoo.Bar))!;
private static readonly Type s_delegateType = typeof(Action);
private readonly Foo _target = new Foo();
[Benchmark(Baseline = true)]
public Delegate BindToMethodInfo_InterfaceInstanceMethod()
{
return Delegate.CreateDelegate(s_delegateType, _target, s_method);
}
}Note This comment was generated with the assistance of GitHub Copilot. |
|
Tagging subscribers to this area: @agocke |
There was a problem hiding this comment.
Pull request overview
This PR adds a per-MethodDesc cache for the default FuncPtrStub (stored in MethodDescCodeData) so the common FuncPtrStubs::GetFuncPtrStub(pMD) path can avoid taking the FuncPtrStubs hash-table lock.
Changes:
- Adds a
DefaultFuncPtrStubfield toMethodDescCodeDataplus interlocked publish/accessors onMethodDesc. - Updates
FuncPtrStubs::GetFuncPtrStubto use the cached stub for the defaultPrecodeTypeinstead of locking and querying the hash table. - Uses an interlocked “first-wins” publish so concurrent stub creation races converge on a single cached stub.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| src/coreclr/vm/method.hpp | Adds MethodDescCodeData::DefaultFuncPtrStub and declares MethodDesc accessors. |
| src/coreclr/vm/method.cpp | Implements interlocked set/get helpers for the cached default funcptr stub. |
| src/coreclr/vm/fptrstubs.cpp | Uses the per-MethodDesc cached stub for default PrecodeType and avoids the hash-table lock on that path. |
| if (type != GetDefaultType(pMD)) | ||
| { | ||
| CrstHolder ch(&m_hashTableCrst); | ||
| pPrecode = m_hashTable.Lookup(PrecodeKey(pMD, type)); | ||
| } | ||
| else | ||
| { | ||
| pPrecode = pMD->GetDefaultFuncPtrStub(); | ||
| } |
| if (type == GetDefaultType(pMD)) | ||
| { | ||
| // Set the default funcptr stub in the MethodDesc if it is not already set. If another thread beat us to it, then | ||
| // we will use the one that was set by the other thread. | ||
| if (pMD->SetDefaultFuncPtrStub(pNewPrecode)) | ||
| { | ||
| pPrecode = pNewPrecode; | ||
| amt.SuppressRelease(); | ||
| } | ||
| else | ||
| { | ||
| pPrecode = pMD->GetDefaultFuncPtrStub(); | ||
| _ASSERTE(pPrecode != NULL); | ||
| setTargetAfterAddingToHashTable = false; | ||
| } | ||
| } | ||
| else | ||
| { | ||
| CrstHolder ch(&m_hashTableCrst); |
| struct MethodDescCodeData final | ||
| { | ||
| #ifdef FEATURE_CODE_VERSIONING | ||
| PTR_MethodDescVersioningState VersioningState; | ||
| NativeCodeVersion::OptimizationTier OptimizationTier; | ||
| Precode* DefaultFuncPtrStub; | ||
| #endif // FEATURE_CODE_VERSIONING |
| #ifdef FEATURE_CODE_VERSIONING | ||
| PTR_MethodDescVersioningState VersioningState; | ||
| NativeCodeVersion::OptimizationTier OptimizationTier; | ||
| Precode* DefaultFuncPtrStub; |
There was a problem hiding this comment.
Can we reuse TemporaryEntryPoint as DefaultFuncPtrStub instead of tracking is separately?
This was not possible originally when we had compact temporary entrypoints that were not back patchable, but it should be possible now.
Caches the default-type
FuncPtrStubprecode directly on theMethodDesc(inMethodDescCodeData) so the commonFuncPtrStubs::GetFuncPtrStubpath avoids taking theFuncPtrStubshash-table critical section.A new
DefaultFuncPtrStubfield plusSetDefaultFuncPtrStub/GetDefaultFuncPtrStubaccessors store and retrieve the cached precode using an interlocked publish. For the default precode type, lookup and insertion now go through the cached field instead of the hash table +m_hashTableCrst.This was extracted from #129019 to keep the optimization independent of the preemptive-mode changes in that PR.
(It's not clear to me this is worth it, but it's certainly not a good part of that original PR).
Note
This PR was generated with the assistance of GitHub Copilot.