Skip to content

Commit

Permalink
JIT: add option to choose guarded devirt class randomly (#53399)
Browse files Browse the repository at this point in the history
Add a config setting to randomly choose one of the observed classes for
guarded devirtualization, rather than the most likely class.
  • Loading branch information
AndyAyersMS authored Jun 1, 2021
1 parent 78fd895 commit 22640e1
Show file tree
Hide file tree
Showing 6 changed files with 151 additions and 11 deletions.
6 changes: 6 additions & 0 deletions src/coreclr/jit/compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -5732,6 +5732,12 @@ class Compiler
void fgIncorporateBlockCounts();
void fgIncorporateEdgeCounts();

CORINFO_CLASS_HANDLE getRandomClass(ICorJitInfo::PgoInstrumentationSchema* schema,
UINT32 countSchemaItems,
BYTE* pInstrumentationData,
int32_t ilOffset,
CLRRandom* random);

public:
const char* fgPgoFailReason;
bool fgPgoDisabled;
Expand Down
32 changes: 27 additions & 5 deletions src/coreclr/jit/importer.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

Expand Down Expand Up @@ -21645,17 +21644,40 @@ void Compiler::considerGuardedDevirtualization(
unsigned likelihood = 0;
unsigned numberOfClasses = 0;

CORINFO_CLASS_HANDLE likelyClass =
getLikelyClass(fgPgoSchema, fgPgoSchemaCount, fgPgoData, ilOffset, &likelihood, &numberOfClasses);
CORINFO_CLASS_HANDLE likelyClass = NO_CLASS_HANDLE;

bool doRandomDevirt = false;

#ifdef DEBUG
// Optional stress mode to pick a random known class, rather than
// the most likely known class.
//
doRandomDevirt = JitConfig.JitRandomGuardedDevirtualization() != 0;

if (doRandomDevirt)
{
// Reuse the random inliner's random state.
//
CLRRandom* const random =
impInlineRoot()->m_inlineStrategy->GetRandom(JitConfig.JitRandomGuardedDevirtualization());
likelihood = 100;
numberOfClasses = 1;
likelyClass = getRandomClass(fgPgoSchema, fgPgoSchemaCount, fgPgoData, ilOffset, random);
}
else
#endif
{
likelyClass = getLikelyClass(fgPgoSchema, fgPgoSchemaCount, fgPgoData, ilOffset, &likelihood, &numberOfClasses);
}

if (likelyClass == NO_CLASS_HANDLE)
{
JITDUMP("No likely class, sorry\n");
return;
}

JITDUMP("Likely class for %p (%s) is %p (%s) [likelihood:%u classes seen:%u]\n", dspPtr(objClass), objClassName,
likelyClass, eeGetClassName(likelyClass), likelihood, numberOfClasses);
JITDUMP("%s class for %p (%s) is %p (%s) [likelihood:%u classes seen:%u]\n", doRandomDevirt ? "Random" : "Likely",
dspPtr(objClass), objClassName, likelyClass, eeGetClassName(likelyClass), likelihood, numberOfClasses);

// Todo: a more advanced heuristic using likelihood, number of
// classes, and the profile count for this block.
Expand Down
9 changes: 7 additions & 2 deletions src/coreclr/jit/inline.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1665,6 +1665,9 @@ void InlineStrategy::FinalizeXml(FILE* file)
//------------------------------------------------------------------------
// GetRandom: setup or access random state
//
// Arguments:
// seed -- seed value to use if not doing random inlines
//
// Return Value:
// New or pre-existing random state.
//
Expand All @@ -1673,11 +1676,11 @@ void InlineStrategy::FinalizeXml(FILE* file)
// specified externally (via stress or policy setting) and partially
// specified internally via method hash.

CLRRandom* InlineStrategy::GetRandom()
CLRRandom* InlineStrategy::GetRandom(int optionalSeed)
{
if (m_Random == nullptr)
{
int externalSeed = 0;
int externalSeed = optionalSeed;

#ifdef DEBUG

Expand Down Expand Up @@ -1707,6 +1710,8 @@ CLRRandom* InlineStrategy::GetRandom()

int seed = externalSeed ^ internalSeed;

JITDUMP("\n*** Using random seed ext(%u) ^ int(%u) = %u\n", externalSeed, internalSeed, seed);

m_Random = new (m_Compiler, CMK_Inlining) CLRRandom();
m_Random->Init(seed);
}
Expand Down
2 changes: 1 addition & 1 deletion src/coreclr/jit/inline.h
Original file line number Diff line number Diff line change
Expand Up @@ -912,7 +912,7 @@ class InlineStrategy
}

// Set up or access random state (for use by RandomPolicy)
CLRRandom* GetRandom();
CLRRandom* GetRandom(int optionalSeed = 0);

#endif // defined(DEBUG) || defined(INLINE_DATA)

Expand Down
1 change: 1 addition & 0 deletions src/coreclr/jit/jitconfigvalues.h
Original file line number Diff line number Diff line change
Expand Up @@ -469,6 +469,7 @@ CONFIG_INTEGER(JitGuardedDevirtualizationChainLikelihood, W("JitGuardedDevirtual
CONFIG_INTEGER(JitGuardedDevirtualizationChainStatements, W("JitGuardedDevirtualizationChainStatements"), 4)
#if defined(DEBUG)
CONFIG_STRING(JitGuardedDevirtualizationRange, W("JitGuardedDevirtualizationRange"))
CONFIG_INTEGER(JitRandomGuardedDevirtualization, W("JitRandomGuardedDevirtualization"), 0)
#endif // DEBUG

// Enable insertion of patchpoints into Tier0 methods with loops.
Expand Down
112 changes: 109 additions & 3 deletions src/coreclr/jit/likelyclass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ struct LikelyClassHistogramEntry
};

// Summarizes a ClassProfile table by forming a Histogram

//
struct LikelyClassHistogram
{
Expand All @@ -54,6 +53,13 @@ struct LikelyClassHistogram
}
};

//------------------------------------------------------------------------
// LikelyClassHistogram::LikelyClassHistgram: construct a new histogram
//
// Arguments:
// histogramEntries - pointer to the table portion of a ClassProfile* object (see corjit.h)
// entryCount - number of entries in the table to examine
//
LikelyClassHistogram::LikelyClassHistogram(INT_PTR* histogramEntries, unsigned entryCount)
{
m_unknownTypes = 0;
Expand Down Expand Up @@ -97,8 +103,29 @@ LikelyClassHistogram::LikelyClassHistogram(INT_PTR* histogramEntries, unsigned e
}
}

// This is used by the devirtualization logic below, and by crossgen2 when producing the R2R image (to reduce the size
// cost of carrying the type histogram)
//------------------------------------------------------------------------
// getLikelyClass: find class profile data for an IL offset, and return the most likely class
//
// Arguments:
// schema - profile schema
// countSchemaItems - number of items in the schema
// pInstrumentationData - associated data
// ilOffset - il offset of the callvirt
// pLikelihood - [OUT] likelihood of observing that entry [0...100]
// pNumberOfClasses - [OUT] estimated number of classes seen at runtime
//
// Returns:
// Class handle for the most likely class, or nullptr
//
// Notes:
// A "monomorphic" call site will return likelihood 100 and number of entries = 1.
//
// This is used by the devirtualization logic below, and by crossgen2 when producing
// the R2R image (to reduce the sizecost of carrying the type histogram)
//
// This code can runs without a jit instance present, so JITDUMP and related
// cannot be used.
//
extern "C" DLLEXPORT CORINFO_CLASS_HANDLE WINAPI getLikelyClass(ICorJitInfo::PgoInstrumentationSchema* schema,
UINT32 countSchemaItems,
BYTE* pInstrumentationData,
Expand Down Expand Up @@ -220,3 +247,82 @@ extern "C" DLLEXPORT CORINFO_CLASS_HANDLE WINAPI getLikelyClass(ICorJitInfo::Pgo
//
return NULL;
}

//------------------------------------------------------------------------
// getRandomClass: find class profile data for an IL offset, and return
// one of the possible classes at random
//
// Arguments:
// schema - profile schema
// countSchemaItems - number of items in the schema
// pInstrumentationData - associated data
// ilOffset - il offset of the callvirt
// random - randomness generator
//
// Returns:
// Randomly observed class, or nullptr.
//
CORINFO_CLASS_HANDLE Compiler::getRandomClass(ICorJitInfo::PgoInstrumentationSchema* schema,
UINT32 countSchemaItems,
BYTE* pInstrumentationData,
int32_t ilOffset,
CLRRandom* random)
{
if (schema == nullptr)
{
return NO_CLASS_HANDLE;
}

for (COUNT_T i = 0; i < countSchemaItems; i++)
{
if (schema[i].ILOffset != (int32_t)ilOffset)
{
continue;
}

if ((schema[i].InstrumentationKind == ICorJitInfo::PgoInstrumentationKind::GetLikelyClass) &&
(schema[i].Count == 1))
{
INT_PTR result = *(INT_PTR*)(pInstrumentationData + schema[i].Offset);
if (ICorJitInfo::IsUnknownTypeHandle(result))
{
return NO_CLASS_HANDLE;
}
else
{
return (CORINFO_CLASS_HANDLE)result;
}
}

bool isHistogramCount =
(schema[i].InstrumentationKind == ICorJitInfo::PgoInstrumentationKind::TypeHandleHistogramIntCount) ||
(schema[i].InstrumentationKind == ICorJitInfo::PgoInstrumentationKind::TypeHandleHistogramLongCount);

if (isHistogramCount && (schema[i].Count == 1) && ((i + 1) < countSchemaItems) &&
(schema[i + 1].InstrumentationKind == ICorJitInfo::PgoInstrumentationKind::TypeHandleHistogramTypeHandle))
{
// Form a histogram
//
LikelyClassHistogram h((INT_PTR*)(pInstrumentationData + schema[i + 1].Offset), schema[i + 1].Count);

if (h.countHistogramElements == 0)
{
return NO_CLASS_HANDLE;
}

// Choose an entry at random.
//
unsigned randomEntryIndex = random->Next(0, h.countHistogramElements - 1);
LikelyClassHistogramEntry randomEntry = h.HistogramEntryAt(randomEntryIndex);

if (ICorJitInfo::IsUnknownTypeHandle(randomEntry.m_mt))
{
return NO_CLASS_HANDLE;
}

return (CORINFO_CLASS_HANDLE)randomEntry.m_mt;
}
}

return NO_CLASS_HANDLE;
}

0 comments on commit 22640e1

Please sign in to comment.