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

JIT: Add some convenience classes for node count JIT metrics, and automatic dumping from compShutdown #92112

Merged
merged 4 commits into from
Sep 22, 2023
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
4 changes: 4 additions & 0 deletions src/coreclr/jit/compiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1744,6 +1744,10 @@ void Compiler::compShutdown()
#endif // DEBUG
jitprintf(" NYI: %u\n", fatal_NYI);
#endif // MEASURE_FATAL

#if CALL_ARG_STATS || COUNT_BASIC_BLOCKS || COUNT_LOOPS || EMITTER_STATS || MEASURE_NODE_SIZE || MEASURE_MEM_ALLOC
DumpOnShutdown::DumpAll();
#endif
}

/*****************************************************************************
Expand Down
86 changes: 86 additions & 0 deletions src/coreclr/jit/compiler.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,92 @@ inline bool Compiler::jitIsBetweenInclusive(unsigned value, unsigned start, unsi
return start <= value && value <= end;
}

#define HISTOGRAM_MAX_SIZE_COUNT 64

#if CALL_ARG_STATS || COUNT_BASIC_BLOCKS || COUNT_LOOPS || EMITTER_STATS || MEASURE_NODE_SIZE || MEASURE_MEM_ALLOC

class Dumpable
{
public:
virtual void dump(FILE* output) = 0;
};

// Helper class to record and display a histogram of different values.
// Usage like:
// static unsigned s_buckets[] = { 1, 2, 5, 10, 0 }; // Must have terminating 0
// static Histogram s_histogram(s_buckets);
// ...
// s_histogram.record(someValue);
//
// The histogram can later be dumped with the dump function, or automatically
// be dumped on shutdown of the JIT library using the DumpOnShutdown helper
// class (see below). It will display how many recorded values fell into each
// of the buckets (<= 1, <= 2, <= 5, <= 10, > 10).
class Histogram : public Dumpable
{
public:
Histogram(const unsigned* const sizeTable);

void dump(FILE* output);
void record(unsigned size);

private:
unsigned m_sizeCount;
const unsigned* const m_sizeTable;
LONG m_counts[HISTOGRAM_MAX_SIZE_COUNT];
};

// Helper class to record and display counts of node types. Use like:
// static NodeCounts s_nodeCounts;
// ...
// s_nodeCounts.record(someNode->gtOper);
//
// The node counts can later be dumped with the dump function, or automatically
// be dumped on shutdown of the JIT library using the DumpOnShutdown helper
// class (see below). It will display output such as:
// LCL_VAR : 62221
// CNS_INT : 42139
// COMMA : 623
// CAST : 460
// ADD : 397
// RSH : 72
// NEG : 5
// UDIV : 1
//
class NodeCounts : public Dumpable
{
public:
NodeCounts() : m_counts()
{
}

void dump(FILE* output);
void record(genTreeOps oper);

private:
LONG m_counts[GT_COUNT];
};

// Helper class to register a Histogram or NodeCounts instance to automatically
// be output to jitstdout when the JIT library is shutdown. Example usage:
//
// static NodeCounts s_nodeCounts;
// static DumpOnShutdown d("Bounds check index node types", &s_nodeCounts);
// ...
// s_nodeCounts.record(...);
//
// Useful for quick ad-hoc investigations without having to manually go add
// code into Compiler::compShutdown and expose the Histogram/NodeCount to that
// function.
class DumpOnShutdown
{
public:
DumpOnShutdown(const char* name, Dumpable* histogram);
static void DumpAll();
};

#endif // CALL_ARG_STATS || COUNT_BASIC_BLOCKS || COUNT_LOOPS || EMITTER_STATS || MEASURE_NODE_SIZE

/******************************************************************************************
* Return the EH descriptor for the given region index.
*/
Expand Down
22 changes: 0 additions & 22 deletions src/coreclr/jit/jit.h
Original file line number Diff line number Diff line change
Expand Up @@ -710,28 +710,6 @@ inline size_t unsigned_abs(__int64 x)

/*****************************************************************************/

#define HISTOGRAM_MAX_SIZE_COUNT 64

#if CALL_ARG_STATS || COUNT_BASIC_BLOCKS || COUNT_LOOPS || EMITTER_STATS || MEASURE_NODE_SIZE || MEASURE_MEM_ALLOC

class Histogram
{
public:
Histogram(const unsigned* const sizeTable);

void dump(FILE* output);
void record(unsigned size);

private:
unsigned m_sizeCount;
const unsigned* const m_sizeTable;
unsigned m_counts[HISTOGRAM_MAX_SIZE_COUNT];
};

#endif // CALL_ARG_STATS || COUNT_BASIC_BLOCKS || COUNT_LOOPS || EMITTER_STATS || MEASURE_NODE_SIZE

/*****************************************************************************/

#include "error.h"

/*****************************************************************************/
Expand Down
93 changes: 90 additions & 3 deletions src/coreclr/jit/utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
#endif

#include "opcode.h"
#include "jitstd/algorithm.h"

/*****************************************************************************/

Expand Down Expand Up @@ -979,9 +980,9 @@ void Histogram::dump(FILE* output)
fprintf(output, "%7u", m_sizeTable[i]);
}

c += m_counts[i];
c += static_cast<unsigned>(m_counts[i]);

fprintf(output, " ===> %7u count (%3u%% of total)\n", m_counts[i], (int)(100.0 * c / t));
fprintf(output, " ===> %7u count (%3u%% of total)\n", static_cast<unsigned>(m_counts[i]), (int)(100.0 * c / t));
}
}

Expand All @@ -996,7 +997,93 @@ void Histogram::record(unsigned size)
}
}

m_counts[i]++;
InterlockedAdd(&m_counts[i], 1);
}

void NodeCounts::dump(FILE* output)
{
struct Entry
{
genTreeOps oper;
unsigned count;
};

Entry sorted[GT_COUNT];
for (int i = 0; i < GT_COUNT; i++)
{
sorted[i].oper = static_cast<genTreeOps>(i);
sorted[i].count = static_cast<unsigned>(m_counts[i]);
}

jitstd::sort(sorted, sorted + ArrLen(sorted), [](const Entry& lhs, const Entry& rhs) {
if (lhs.count > rhs.count)
{
return true;
}

if (lhs.count < rhs.count)
{
return false;
}

return static_cast<unsigned>(lhs.oper) < static_cast<unsigned>(rhs.oper);
});

for (const Entry& entry : sorted)
{
if (entry.count == 0)
{
break;
}

fprintf(output, "%-20s : %7u\n", GenTree::OpName(entry.oper), entry.count);
}
}

void NodeCounts::record(genTreeOps oper)
{
assert(oper < GT_COUNT);
InterlockedAdd(&m_counts[oper], 1);
}

struct DumpOnShutdownEntry
{
const char* Name;
Dumpable* Dumpable;
};

static DumpOnShutdownEntry s_dumpOnShutdown[16];

DumpOnShutdown::DumpOnShutdown(const char* name, Dumpable* dumpable)
{
for (DumpOnShutdownEntry& entry : s_dumpOnShutdown)
{
if ((entry.Name == nullptr) && (entry.Dumpable == nullptr))
{
entry.Name = name;
entry.Dumpable = dumpable;
return;
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe add an assert that the DumpOnShutdownEntry table isn't all full?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will do.


assert(!"No space left in table");
}

void DumpOnShutdown::DumpAll()
{
for (const DumpOnShutdownEntry& entry : s_dumpOnShutdown)
{
if (entry.Name != nullptr)
{
jitprintf("%s\n", entry.Name);
}

if (entry.Dumpable != nullptr)
{
entry.Dumpable->dump(jitstdout());
jitprintf("\n");
}
}
}

#endif // CALL_ARG_STATS || COUNT_BASIC_BLOCKS || COUNT_LOOPS || EMITTER_STATS || MEASURE_NODE_SIZE
Expand Down