#ifdef Py_STATS
-#define SPECIALIZATION_FAILURE_KINDS 30
-
-typedef struct _specialization_stats {
- uint64_t success;
- uint64_t failure;
- uint64_t hit;
- uint64_t deferred;
- uint64_t miss;
- uint64_t deopt;
- uint64_t failure_kinds[SPECIALIZATION_FAILURE_KINDS];
-} SpecializationStats;
-
-typedef struct _opcode_stats {
- SpecializationStats specialization;
- uint64_t execution_count;
- uint64_t pair_count[256];
-} OpcodeStats;
-
-typedef struct _call_stats {
- uint64_t inlined_py_calls;
- uint64_t pyeval_calls;
- uint64_t frames_pushed;
- uint64_t frame_objects_created;
-} CallStats;
-
-typedef struct _object_stats {
- uint64_t allocations;
- uint64_t allocations512;
- uint64_t allocations4k;
- uint64_t allocations_big;
- uint64_t frees;
- uint64_t to_freelist;
- uint64_t from_freelist;
- uint64_t new_values;
- uint64_t dict_materialized_on_request;
- uint64_t dict_materialized_new_key;
- uint64_t dict_materialized_too_big;
- uint64_t dict_materialized_str_subclass;
-} ObjectStats;
-
-typedef struct _stats {
- OpcodeStats opcode_stats[256];
- CallStats call_stats;
- ObjectStats object_stats;
-} PyStats;
-
-extern PyStats _py_stats;
#define STAT_INC(opname, name) _py_stats.opcode_stats[opname].specialization.name++
#define STAT_DEC(opname, name) _py_stats.opcode_stats[opname].specialization.name--
#define OBJECT_STAT_INC_COND(name, cond) \
do { if (cond) _py_stats.object_stats.name++; } while (0)
-extern void _Py_PrintSpecializationStats(int to_file);
-
// Used by the _opcode extension which is built as a shared library
PyAPI_FUNC(PyObject*) _Py_GetSpecializationStats(void);
static inline void
_Py_DECREF_SPECIALIZED(PyObject *op, const destructor destruct)
{
+ _Py_DECREF_STAT_INC();
#ifdef Py_REF_DEBUG
_Py_RefTotal--;
#endif
static inline void
_Py_DECREF_NO_DEALLOC(PyObject *op)
{
+ _Py_DECREF_STAT_INC();
#ifdef Py_REF_DEBUG
_Py_RefTotal--;
#endif
whose size is determined when the object is allocated.
*/
+#include "pystats.h"
+
/* Py_DEBUG implies Py_REF_DEBUG. */
#if defined(Py_DEBUG) && !defined(Py_REF_DEBUG)
# define Py_REF_DEBUG
static inline void Py_INCREF(PyObject *op)
{
+ _Py_INCREF_STAT_INC();
#if defined(Py_REF_DEBUG) && defined(Py_LIMITED_API) && Py_LIMITED_API+0 >= 0x030A0000
// Stable ABI for Python 3.10 built in debug mode.
_Py_IncRef(op);
# define Py_INCREF(op) Py_INCREF(_PyObject_CAST(op))
#endif
-
#if defined(Py_REF_DEBUG) && defined(Py_LIMITED_API) && Py_LIMITED_API+0 >= 0x030A0000
// Stable ABI for limited C API version 3.10 of Python debug build
static inline void Py_DECREF(PyObject *op) {
#elif defined(Py_REF_DEBUG)
static inline void Py_DECREF(const char *filename, int lineno, PyObject *op)
{
+ _Py_DECREF_STAT_INC();
_Py_RefTotal--;
if (--op->ob_refcnt != 0) {
if (op->ob_refcnt < 0) {
#else
static inline void Py_DECREF(PyObject *op)
{
+ _Py_DECREF_STAT_INC();
// Non-limited C API and limited C API for Python 3.9 and older access
// directly PyObject.ob_refcnt.
if (--op->ob_refcnt == 0) {
--- /dev/null
+
+
+#ifndef Py_PYSTATS_H
+#define Py_PYSTATS_H
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef Py_STATS
+
+#define SPECIALIZATION_FAILURE_KINDS 32
+
+typedef struct _specialization_stats {
+ uint64_t success;
+ uint64_t failure;
+ uint64_t hit;
+ uint64_t deferred;
+ uint64_t miss;
+ uint64_t deopt;
+ uint64_t failure_kinds[SPECIALIZATION_FAILURE_KINDS];
+} SpecializationStats;
+
+typedef struct _opcode_stats {
+ SpecializationStats specialization;
+ uint64_t execution_count;
+ uint64_t pair_count[256];
+} OpcodeStats;
+
+typedef struct _call_stats {
+ uint64_t inlined_py_calls;
+ uint64_t pyeval_calls;
+ uint64_t frames_pushed;
+ uint64_t frame_objects_created;
+} CallStats;
+
+typedef struct _object_stats {
+ uint64_t increfs;
+ uint64_t decrefs;
+ uint64_t allocations;
+ uint64_t allocations512;
+ uint64_t allocations4k;
+ uint64_t allocations_big;
+ uint64_t frees;
+ uint64_t to_freelist;
+ uint64_t from_freelist;
+ uint64_t new_values;
+ uint64_t dict_materialized_on_request;
+ uint64_t dict_materialized_new_key;
+ uint64_t dict_materialized_too_big;
+ uint64_t dict_materialized_str_subclass;
+} ObjectStats;
+
+typedef struct _stats {
+ OpcodeStats opcode_stats[256];
+ CallStats call_stats;
+ ObjectStats object_stats;
+} PyStats;
+
+PyAPI_DATA(PyStats) _py_stats;
+
+extern void _Py_PrintSpecializationStats(int to_file);
+
+
+#define _Py_INCREF_STAT_INC() _py_stats.object_stats.increfs++
+#define _Py_DECREF_STAT_INC() _py_stats.object_stats.decrefs++
+
+#else
+
+#define _Py_INCREF_STAT_INC() ((void)0)
+#define _Py_DECREF_STAT_INC() ((void)0)
+
+#endif // !Py_STATS
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* !Py_PYSTATs_H */
$(srcdir)/Include/pymem.h \
$(srcdir)/Include/pyport.h \
$(srcdir)/Include/pystate.h \
+ $(srcdir)/Include/pystats.h \
$(srcdir)/Include/pystrcmp.h \
$(srcdir)/Include/pystrtod.h \
$(srcdir)/Include/pythonrun.h \
<ClInclude Include="..\Include\pymem.h" />
<ClInclude Include="..\Include\pyport.h" />
<ClInclude Include="..\Include\pystate.h" />
+ <ClInclude Include="..\Include\pystats.h" />
<ClInclude Include="..\Include\pystrcmp.h" />
<ClInclude Include="..\Include\pystrtod.h" />
<ClInclude Include="..\Include\pythonrun.h" />
<ClInclude Include="..\Include\pystate.h">
<Filter>Include</Filter>
</ClInclude>
+ <ClInclude Include="..\Include\pystats.h">
+ <Filter>Include</Filter>
+ </ClInclude>
<ClInclude Include="..\Include\pystrcmp.h">
<Filter>Include</Filter>
</ClInclude>
#undef Py_DECREF
#define Py_DECREF(arg) \
do { \
+ _Py_DECREF_STAT_INC(); \
PyObject *op = _PyObject_CAST(arg); \
if (--op->ob_refcnt == 0) { \
destructor dealloc = Py_TYPE(op)->tp_dealloc; \
#undef _Py_DECREF_SPECIALIZED
#define _Py_DECREF_SPECIALIZED(arg, dealloc) \
do { \
+ _Py_DECREF_STAT_INC(); \
PyObject *op = _PyObject_CAST(arg); \
if (--op->ob_refcnt == 0) { \
destructor d = (destructor)(dealloc); \
fprintf(out, "Object allocations over 4 kbytes: %" PRIu64 "\n", stats->allocations_big);
fprintf(out, "Object frees: %" PRIu64 "\n", stats->frees);
fprintf(out, "Object new values: %" PRIu64 "\n", stats->new_values);
+ fprintf(out, "Object increfs: %" PRIu64 "\n", stats->increfs);
+ fprintf(out, "Object decrefs: %" PRIu64 "\n", stats->decrefs);
fprintf(out, "Object materialize dict (on request): %" PRIu64 "\n", stats->dict_materialized_on_request);
fprintf(out, "Object materialize dict (new key): %" PRIu64 "\n", stats->dict_materialized_new_key);
fprintf(out, "Object materialize dict (too big): %" PRIu64 "\n", stats->dict_materialized_too_big);
from datetime import date
import itertools
import argparse
+import sys
if os.name == "nt":
DEFAULT_DIR = "c:\\temp\\py_stats\\"
for filename in os.listdir(DEFAULT_DIR):
with open(os.path.join(DEFAULT_DIR, filename)) as fd:
for line in fd:
- key, value = line.split(":")
+ try:
+ key, value = line.split(":")
+ except ValueError:
+ print (f"Unparsable line: '{line.strip()}' in {filename}", file=sys.stderr)
+ continue
key = key.strip()
value = int(value)
stats[key] += value
def emit_object_stats(stats):
with Section("Object stats", summary="allocations, frees and dict materializatons"):
- total = stats.get("Object new values")
+ total_materializations = stats.get("Object new values")
+ total_allocations = stats.get("Object allocations")
rows = []
for key, value in stats.items():
if key.startswith("Object"):
if "materialize" in key:
- materialize = f"{100*value/total:0.1f}%"
+ ratio = f"{100*value/total_materializations:0.1f}%"
+ elif "allocations" in key:
+ ratio = f"{100*value/total_allocations:0.1f}%"
else:
- materialize = ""
+ ratio = ""
label = key[6:].strip()
label = label[0].upper() + label[1:]
- rows.append((label, value, materialize))
+ rows.append((label, value, ratio))
emit_table(("", "Count:", "Ratio:"), rows)
def get_total(opcode_stats):