From 2bad3e1e0e7044dced55ed07244200b545db366c Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Fri, 15 Sep 2023 12:06:53 +0200 Subject: [PATCH 1/3] JIT: Add some convenience classes for ad-hoc JIT metrics - Add a NodeCounts class that can be used to easily record the count of node types seen - Add a DumpOnShutdown class that can be used to easily register either a Histogram class or NodeCounts class to be dumped as part of Compiler::compShutdown --- src/coreclr/jit/compiler.cpp | 4 ++ src/coreclr/jit/compiler.hpp | 47 ++++++++++++++++++++ src/coreclr/jit/jit.h | 22 ---------- src/coreclr/jit/utils.cpp | 85 ++++++++++++++++++++++++++++++++++++ 4 files changed, 136 insertions(+), 22 deletions(-) diff --git a/src/coreclr/jit/compiler.cpp b/src/coreclr/jit/compiler.cpp index dd29c5fb47cc2f..acefde345e6500 100644 --- a/src/coreclr/jit/compiler.cpp +++ b/src/coreclr/jit/compiler.cpp @@ -1746,6 +1746,10 @@ void Compiler::compShutdown() #endif // DEBUG fprintf(fout, " 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(fout); +#endif } /***************************************************************************** diff --git a/src/coreclr/jit/compiler.hpp b/src/coreclr/jit/compiler.hpp index 24c009c88607c0..7ef8e6e9ca05b3 100644 --- a/src/coreclr/jit/compiler.hpp +++ b/src/coreclr/jit/compiler.hpp @@ -244,6 +244,53 @@ 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; +}; + +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; + unsigned m_counts[HISTOGRAM_MAX_SIZE_COUNT]; +}; + +class NodeCounts : public Dumpable +{ +public: + NodeCounts() : m_counts() + { + } + + void dump(FILE* output); + void record(genTreeOps oper); + +private: + unsigned m_counts[GT_COUNT]; +}; + +class DumpOnShutdown +{ +public: + DumpOnShutdown(const char* name, Dumpable* histogram); + static void DumpAll(FILE* output); +}; + +#endif // CALL_ARG_STATS || COUNT_BASIC_BLOCKS || COUNT_LOOPS || EMITTER_STATS || MEASURE_NODE_SIZE + /****************************************************************************************** * Return the EH descriptor for the given region index. */ diff --git a/src/coreclr/jit/jit.h b/src/coreclr/jit/jit.h index 4c637fac0fac5d..4b12ce38234e2e 100644 --- a/src/coreclr/jit/jit.h +++ b/src/coreclr/jit/jit.h @@ -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" /*****************************************************************************/ diff --git a/src/coreclr/jit/utils.cpp b/src/coreclr/jit/utils.cpp index 245f84c7f62349..2904ad0c08de4f 100644 --- a/src/coreclr/jit/utils.cpp +++ b/src/coreclr/jit/utils.cpp @@ -22,6 +22,7 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX #endif #include "opcode.h" +#include "jitstd/algorithm.h" /*****************************************************************************/ @@ -999,6 +1000,90 @@ void Histogram::record(unsigned size) m_counts[i]++; } +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(i); + sorted[i].count = 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(lhs.oper) < static_cast(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); + m_counts[oper]++; +} + +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; + break; + } + } +} + +void DumpOnShutdown::DumpAll(FILE* fout) +{ + for (const DumpOnShutdownEntry& entry : s_dumpOnShutdown) + { + if (entry.Name != nullptr) + { + fprintf(fout, "%s\n", entry.Name); + } + + if (entry.Dumpable != nullptr) + { + entry.Dumpable->dump(fout); + fprintf(fout, "\n"); + } + } +} + #endif // CALL_ARG_STATS || COUNT_BASIC_BLOCKS || COUNT_LOOPS || EMITTER_STATS || MEASURE_NODE_SIZE /***************************************************************************** From 0a2730c0451be8c16473002be9d4f0fe753cba80 Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Fri, 22 Sep 2023 10:56:48 +0200 Subject: [PATCH 2/3] Add some example usage comments --- src/coreclr/jit/compiler.hpp | 43 ++++++++++++++++++++++++++++++++++-- src/coreclr/jit/utils.cpp | 14 +++++++----- 2 files changed, 49 insertions(+), 8 deletions(-) diff --git a/src/coreclr/jit/compiler.hpp b/src/coreclr/jit/compiler.hpp index 7ef8e6e9ca05b3..2111b1a11fed59 100644 --- a/src/coreclr/jit/compiler.hpp +++ b/src/coreclr/jit/compiler.hpp @@ -254,6 +254,17 @@ class Dumpable 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: @@ -265,9 +276,26 @@ class Histogram : public Dumpable private: unsigned m_sizeCount; const unsigned* const m_sizeTable; - unsigned m_counts[HISTOGRAM_MAX_SIZE_COUNT]; + 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: @@ -279,9 +307,20 @@ class NodeCounts : public Dumpable void record(genTreeOps oper); private: - unsigned m_counts[GT_COUNT]; + 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: diff --git a/src/coreclr/jit/utils.cpp b/src/coreclr/jit/utils.cpp index 2904ad0c08de4f..332ca1747dc3f8 100644 --- a/src/coreclr/jit/utils.cpp +++ b/src/coreclr/jit/utils.cpp @@ -980,9 +980,9 @@ void Histogram::dump(FILE* output) fprintf(output, "%7u", m_sizeTable[i]); } - c += m_counts[i]; + c += static_cast(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(m_counts[i]), (int)(100.0 * c / t)); } } @@ -997,7 +997,7 @@ void Histogram::record(unsigned size) } } - m_counts[i]++; + InterlockedAdd(&m_counts[i], 1); } void NodeCounts::dump(FILE* output) @@ -1012,7 +1012,7 @@ void NodeCounts::dump(FILE* output) for (int i = 0; i < GT_COUNT; i++) { sorted[i].oper = static_cast(i); - sorted[i].count = m_counts[i]; + sorted[i].count = static_cast(m_counts[i]); } jitstd::sort(sorted, sorted + ArrLen(sorted), [](const Entry& lhs, const Entry& rhs) { @@ -1043,7 +1043,7 @@ void NodeCounts::dump(FILE* output) void NodeCounts::record(genTreeOps oper) { assert(oper < GT_COUNT); - m_counts[oper]++; + InterlockedAdd(&m_counts[oper], 1); } struct DumpOnShutdownEntry @@ -1062,9 +1062,11 @@ DumpOnShutdown::DumpOnShutdown(const char* name, Dumpable* dumpable) { entry.Name = name; entry.Dumpable = dumpable; - break; + return; } } + + assert(!"No space left in table"); } void DumpOnShutdown::DumpAll(FILE* fout) From 7f95aa343df9a0cdad67bc6374fa0f03e639db6a Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Fri, 22 Sep 2023 12:01:48 +0200 Subject: [PATCH 3/3] Fix after merge --- src/coreclr/jit/compiler.cpp | 2 +- src/coreclr/jit/compiler.hpp | 2 +- src/coreclr/jit/utils.cpp | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/coreclr/jit/compiler.cpp b/src/coreclr/jit/compiler.cpp index 98ba269612b75f..4c9ebeef15dd2c 100644 --- a/src/coreclr/jit/compiler.cpp +++ b/src/coreclr/jit/compiler.cpp @@ -1746,7 +1746,7 @@ void Compiler::compShutdown() #endif // MEASURE_FATAL #if CALL_ARG_STATS || COUNT_BASIC_BLOCKS || COUNT_LOOPS || EMITTER_STATS || MEASURE_NODE_SIZE || MEASURE_MEM_ALLOC - DumpOnShutdown::DumpAll(fout); + DumpOnShutdown::DumpAll(); #endif } diff --git a/src/coreclr/jit/compiler.hpp b/src/coreclr/jit/compiler.hpp index 2111b1a11fed59..a786b56edc29dc 100644 --- a/src/coreclr/jit/compiler.hpp +++ b/src/coreclr/jit/compiler.hpp @@ -325,7 +325,7 @@ class DumpOnShutdown { public: DumpOnShutdown(const char* name, Dumpable* histogram); - static void DumpAll(FILE* output); + static void DumpAll(); }; #endif // CALL_ARG_STATS || COUNT_BASIC_BLOCKS || COUNT_LOOPS || EMITTER_STATS || MEASURE_NODE_SIZE diff --git a/src/coreclr/jit/utils.cpp b/src/coreclr/jit/utils.cpp index 7be2a0f302eaba..4b13ab0be053b5 100644 --- a/src/coreclr/jit/utils.cpp +++ b/src/coreclr/jit/utils.cpp @@ -1069,19 +1069,19 @@ DumpOnShutdown::DumpOnShutdown(const char* name, Dumpable* dumpable) assert(!"No space left in table"); } -void DumpOnShutdown::DumpAll(FILE* fout) +void DumpOnShutdown::DumpAll() { for (const DumpOnShutdownEntry& entry : s_dumpOnShutdown) { if (entry.Name != nullptr) { - fprintf(fout, "%s\n", entry.Name); + jitprintf("%s\n", entry.Name); } if (entry.Dumpable != nullptr) { - entry.Dumpable->dump(fout); - fprintf(fout, "\n"); + entry.Dumpable->dump(jitstdout()); + jitprintf("\n"); } } }