From e809ebcfb3354a14bdf95cad752452af9364b04c Mon Sep 17 00:00:00 2001 From: "Peter S. Housel" Date: Fri, 7 Jun 2024 21:18:55 -0700 Subject: [PATCH 1/8] dispatch-profiler: Remove an obsolete hack * sources/lib/dispatch-profiler/dispatch-profiler.dylan (print-dispatch-statistics): Remove a whitespace-stripping workaround for a problem with the pentium-dw command parser. --- sources/lib/dispatch-profiler/dispatch-profiler.dylan | 5 ----- 1 file changed, 5 deletions(-) diff --git a/sources/lib/dispatch-profiler/dispatch-profiler.dylan b/sources/lib/dispatch-profiler/dispatch-profiler.dylan index 0446ae63dd..d9e06ad934 100644 --- a/sources/lib/dispatch-profiler/dispatch-profiler.dylan +++ b/sources/lib/dispatch-profiler/dispatch-profiler.dylan @@ -378,11 +378,6 @@ define method print-dispatch-statistics #key library :: false-or(), profile-base :: false-or(), full? = #t, by-library? = #f, hits-only? = #t, app-results-only? = #f, uncalled-methods? = #f, app-details? = #t) let stream = #f; - // HACK: pentium-dw command parser is brain damaged - when (profile-base) - let end-index = find-key(profile-base, curry(\==, ' ')) | size(profile-base); - profile-base := copy-sequence(profile-base, end: end-index); - end when; local method current-stream (app-stream, library) => (stream) if (app-stream & app-stream ~== *standard-output*) app-stream From 0e0eb65a1f14a3c3dfb8ce09b3c86ffb64bc3d78 Mon Sep 17 00:00:00 2001 From: "Peter S. Housel" Date: Fri, 7 Jun 2024 22:21:59 -0700 Subject: [PATCH 2/8] dfmc-modeling: Make profiling cache header count slots raw * sources/dfmc/modeling/functions.dylan (&class ): Declare the count slots for profiling cache-header engine nodes as to reflect the way they are used in generated code. * sources/dfmc/llvm-back-end/llvm-entry-points.dylan (entry-point-descriptor profiling-cache-header): Remove a "todo" comment about making the change above. --- sources/dfmc/llvm-back-end/llvm-entry-points.dylan | 1 - sources/dfmc/modeling/functions.dylan | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/sources/dfmc/llvm-back-end/llvm-entry-points.dylan b/sources/dfmc/llvm-back-end/llvm-entry-points.dylan index 6df6530257..23e4044407 100644 --- a/sources/dfmc/llvm-back-end/llvm-entry-points.dylan +++ b/sources/dfmc/llvm-back-end/llvm-entry-points.dylan @@ -2247,7 +2247,6 @@ define outer cache-header entry-point-descriptor profiling-cache-header let engine-cast = op--object-pointer-cast(be, engine, pcschen-class); // Increment the profiling counter atomically - // FIXME declare engine node count fields as let rmw-slotptr-type = llvm-pointer-to(be, llvm-reference-type(be, dylan-value(#""))); let count-1-ptr diff --git a/sources/dfmc/modeling/functions.dylan b/sources/dfmc/modeling/functions.dylan index 2f7fd4a811..2c0637ccfb 100644 --- a/sources/dfmc/modeling/functions.dylan +++ b/sources/dfmc/modeling/functions.dylan @@ -1634,8 +1634,8 @@ define concrete-engine-node-initialization () - &slot profiling-call-site-cache-header-engine-node-count-1; - &slot profiling-call-site-cache-header-engine-node-count-2; + raw &slot profiling-call-site-cache-header-engine-node-count-1 :: ; + raw &slot profiling-call-site-cache-header-engine-node-count-2 :: ; &slot profiling-call-site-cache-header-engine-node-id; &slot profiling-call-site-cache-header-engine-node-library; slot profiling-call-site-cache-header-engine-node-call; From 1b6d9f66b02e46dc69d1b59c92fa1b6c5657ea40 Mon Sep 17 00:00:00 2001 From: "Peter S. Housel" Date: Fri, 7 Jun 2024 22:44:37 -0700 Subject: [PATCH 3/8] dylan: Properly initialize profiling cache header engine nodes * sources/dylan/discrimination.dylan (compute-terminal-engine-node): Change the initialization order of profiling cache header engine nodes so that the generic function is available to the primitive-initialize-engine-node implementation (via the cache-header-engine-node-parent slot). --- sources/dylan/discrimination.dylan | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sources/dylan/discrimination.dylan b/sources/dylan/discrimination.dylan index 600680b290..2343158b74 100644 --- a/sources/dylan/discrimination.dylan +++ b/sources/dylan/discrimination.dylan @@ -699,10 +699,10 @@ define function compute-terminal-engine-node (ds :: ) = bootstrap-typed-allocate-engine-node(, engine-node$k-profiling-cache-header, 0); - primitive-initialize-discriminator(new); + cache-header-engine-node-parent(new) := parent; + primitive-initialize-engine-node(new); %profile-count-low(new) := as(, 0); %profile-count-high(new) := as(, 0); - cache-header-engine-node-parent(new) := parent; install-cache-header-engine-node-next(new, ans, %ds-gf(ds)); new else From 27e9843e6d0623422a045a93e9ee7578b2533632 Mon Sep 17 00:00:00 2001 From: "Peter S. Housel" Date: Fri, 7 Jun 2024 22:48:28 -0700 Subject: [PATCH 4/8] dylan: Update hit counts in discriminator nodes * sources/dylan/new-dispatch.dylan (make-linear-class-keyed-discriminator): Initialize lckd-hits in newly instantiated nodes. (linear-class-key-lookup): Increment lckd-hits when the class is found. (make-linear-singleton-discriminator): Initialize lsd-hits in newly instantiated nodes. (immediate-linear-singleton-discriminator-element): Increment lsd-hits when the matching instance is found. (value-object-linear-singleton-discriminator-element): Increment lsd-hits when the matching instance is found. --- sources/dylan/new-dispatch.dylan | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sources/dylan/new-dispatch.dylan b/sources/dylan/new-dispatch.dylan index 0d19eb845b..d88a0e12cf 100644 --- a/sources/dylan/new-dispatch.dylan +++ b/sources/dylan/new-dispatch.dylan @@ -722,6 +722,7 @@ define function make-linear-class-keyed-discriminator let d :: = bootstrap-allocate-repeated-discriminator(code, argnum, extra-bits, table-size, $ckd-empty); lckd-index(d) := 0; + lckd-hits(d) := 0; primitive-initialize-discriminator(d); d end function; @@ -791,6 +792,7 @@ define inline function linear-class-key-lookup else let otherkey = %ckd-ref(d, i); if (pointer-id?(otherkey, key)) + lckd-hits(d) := add-without-overflow(lckd-hits(d), 1); %ckd-ref(d, add-without-overflow(i, 1)) // elseif (pointer-id?(otherkey, $ckd-empty)) // default @@ -1236,6 +1238,7 @@ define function make-linear-singleton-discriminator singleton-discriminator-table(d) := v; singleton-discriminator-default(d) := $absent-engine-node; lsd-index(d) := 0; + lsd-hits(d) := 0; local method loop(i :: , l :: ) unless (l == #()) if (~(i < len)) // @@@@ (i >= len) @@ -1291,6 +1294,7 @@ define inline function immediate-linear-singleton-discriminator-element else let k = vector-element(table, i); if (pointer-id?(k, key)) + lsd-hits(d) := add-without-overflow(lsd-hits(d), 1); lsd-index(d) := i; vector-element(table, i + 1) else @@ -1341,6 +1345,7 @@ define inline function value-object-linear-singleton-discriminator-element else let k = vector-element(table, i); if (k ~== $absent-engine-node & k = key) + lsd-hits(d) := add-without-overflow(lsd-hits(d), 1); lsd-index(d) := i; vector-element(table, i + 1) else From e822d64fcc911b57e447c1ecf1f6daac5c64a8e1 Mon Sep 17 00:00:00 2001 From: "Peter S. Housel" Date: Thu, 13 Jun 2024 21:21:17 -0700 Subject: [PATCH 5/8] dispatch-profiler: Add a macro encapsulating dispatch profiling * sources/lib/dispatch-profiler/dispatch-profiler.dylan (with-dispatch-profiling-report): New macro for performing dynamic dispatch profiling on a block of code and printing out a summary report after execution completes. * sources/lib/dispatch-profiler/dispatch-profiler-library.dylan (module dispatch-profiler): Export the with-dispatch-profiling-report macro. --- .../dispatch-profiler-library.dylan | 1 + .../dispatch-profiler/dispatch-profiler.dylan | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/sources/lib/dispatch-profiler/dispatch-profiler-library.dylan b/sources/lib/dispatch-profiler/dispatch-profiler-library.dylan index 50faeb70ac..1aeb26a7ba 100644 --- a/sources/lib/dispatch-profiler/dispatch-profiler-library.dylan +++ b/sources/lib/dispatch-profiler/dispatch-profiler-library.dylan @@ -33,6 +33,7 @@ define module dispatch-profiler clear-dispatch-statistics!, collect-dispatch-statistics, print-dispatch-statistics, + with-dispatch-profiling-report, enable-generic-caches-only, enable-call-site-caches-only ; diff --git a/sources/lib/dispatch-profiler/dispatch-profiler.dylan b/sources/lib/dispatch-profiler/dispatch-profiler.dylan index d9e06ad934..ed27613249 100644 --- a/sources/lib/dispatch-profiler/dispatch-profiler.dylan +++ b/sources/lib/dispatch-profiler/dispatch-profiler.dylan @@ -646,6 +646,24 @@ define method print-dispatch-statistics end block; end method; +define macro with-dispatch-profiling-report + { with-dispatch-profiling-report(#rest ?options:expression) + ?:body + end } + => { block () + clear-dispatch-profiling(%current-library()); + profile-all-terminal-engine-nodes?() := #t; + *dispatch-profiling-enabled?* := #t; + ?body + cleanup + *dispatch-profiling-enabled?* := #f; + + let stats = make-dispatch-statistics(#f); + collect-dispatch-statistics(%current-library(), stats); + print-dispatch-statistics(stats, ?options); + end } +end macro; + define method enable-call-site-caches-only (library) call-site-caches-enabled?() := #t; profile-all-terminal-engine-nodes?() := #t; From a45578a504c17fe71d223affa689cbc02661da0f Mon Sep 17 00:00:00 2001 From: "Peter S. Housel" Date: Tue, 18 Jun 2024 20:35:20 -0700 Subject: [PATCH 6/8] documentation: Document the dispatch-profiler library * documentation/source/library-reference/dispatch-profiler/index.rst: New documentation source. * documentation/source/library-reference/index.rst: Add the dispatch-profiler to the Library Reference. --- .../dispatch-profiler/index.rst | 261 ++++++++++++++++++ .../source/library-reference/index.rst | 1 + 2 files changed, 262 insertions(+) create mode 100644 documentation/source/library-reference/dispatch-profiler/index.rst diff --git a/documentation/source/library-reference/dispatch-profiler/index.rst b/documentation/source/library-reference/dispatch-profiler/index.rst new file mode 100644 index 0000000000..f47562dfb3 --- /dev/null +++ b/documentation/source/library-reference/dispatch-profiler/index.rst @@ -0,0 +1,261 @@ +***************************** +The DISPATCH-PROFILER library +***************************** + +.. current-library:: dispatch-profiler + +The dispatch profiler library exposes the multimethod dispatch +profiling capability built in to the Open Dylan run-time library, +making it possible to identify costly function calls in code. While +*dispatch coloring* can identify calls that could and could not be +optimized at compile time, dispatch profiling shows which calls have +an actual effect on dynamic performance. + +While this library can be quite useful, it was originally built as +part of prototyping the dispatch mechanisms in Open Dylan, rather than +as a polished tool. We hope that in the future this functionality can +be incorporated into the debugger, making direct use of this library +unnecessary. + +Preparing for Profiling +*********************** + +Before dispatch profiling can be enabled during program execution, you +will need to request that the compiler generate generic function call +data structures (with slightly higher overhead) that are capable of +collecting profile counts. This can be done by setting the +:envvar:`OPEN_DYLAN_PROFILE_ALL_CALLS` environment variable to a +non-empty string when invoking the compiler: + +.. code-block:: console + + $ OPEN_DYLAN_PROFILE_ALL_CALLS=1 dylan-compiler -build write-100mb + + +Report Output Format +******************** + +After a profiling run, upon collection of dispatch statistics a +profiling report can be written out. The report, sorted by ``COST`` +field, lists generic functions and call sites for which any method +invocations are present in the dispatch cache. The following fields +appear in the report: + +``GENERICS`` + The count of generic functions in an application or library + appearing in the report. + +``CLASSES`` + The total number of classes appearing in the application. + +``DEP-GFS`` + Not currently supported (based on an analysis currently commented out + in the compiler). + +``CT-S-CALLS`` + Count of static calls (i.e., to a specific known method or slot accessor) + as determined by the compiler for a library or application. + +``CT-D-CALLS`` + Count of dynamic calls (i.e., using full run-time dispatch through the + :drm:`` object) as determined by the compiler for + a library or application. + +``S/D`` + The ratio of (compile-time) static to dynamic calls. + +``RT-S-CALLS`` + The number of call sites in the library or application where a + method was called but the ``COST`` was 0. + +``E-RT-S/D`` + The ratio of the total number of static calls (``RT-S-CALLS`` plus + ``CT-S-CALLS``) to the total number of call sites identified by the + compiler (``CT-S-CALLS`` plus ``CT-D-CALLS``). + +``POLY`` + The degree of polymorphism. For a given call site, represents the + number of distinct methods called. For a generic function, library, + or application, represents the total call site polymorphism divided + by the number of call sites. + +``TOT SIZE`` + The memory storage size (in units of pointer-sized object slots) + used by method dispatch cache nodes. + +``AVG SIZE`` + The average memory storage size (in units of pointer-sized object + slots) of each associated method dispatch cache node. + +``HITS`` + The number of times that the dispatched method was found in the + method dispatch cache. + +``COST`` + The total number of discriminator nodes traversed in order to locate + the dispatched method in the cache. Useful as an approximation for the + cost of method dispatch for a given call site or generic function. + +``COST/HIT`` + The average number of discriminator nodes that needed to be + traversed in order to dispatch to the given method. + +``C-HITS`` + The total number of discriminator nodes traversed with successful + discrimination tests. + +``C-TRIES`` + The total number of discriminator nodes traversed (identical to + ``COST``). + +``HIT-RATE`` + The ratio of ``C-HITS`` to ``C-TRIES``. + +The DISPATCH-PROFILER module +**************************** + +.. current-module:: dispatch-profiler + +.. macro:: with-dispatch-profiling-report + :statement: + + Performs dispatch profiling over a body of code and prints out a + profiling report. + + :macrocall: + .. parsed-literal:: + with-dispatch-profiling-report (#key `keys`) + `body` + end + + :param keys: Zero or more of the keywords provided by :gf:`print-dispatch-statistics`. + :param body: A body of Dylan code. + + :description: + + Executes the *body* with dispatch profiling enabled. Clears + dispatch profiling counters before beginning execution, disables + profiling and collects statistics at the end, and then writes out + a report using :gf:`print-dispatch-statistics`. + + :example: + + .. code-block:: dylan + + define function main() + with-dispatch-profiling-report (by-library?: #t, profile-base: "write-100mb-") + let string = make(, size: 100, fill: 'x'); + with-open-file (stream = "/tmp/100mb.dylan.txt", direction: #"output") + for (i from 1 to 1024 * 1024) + write(stream, string); + end; + end; + end; + end function; + +.. generic-function:: decache-all-generics + + Restores dispatch caches of generic functions in the given + library and dependent libraries to their initial states. + + :signature: decache-all-generics (library) => () + + :parameter library: An instance of :class:``. + +.. generic-function:: clear-dispatch-profiling + + Resets call site profile counts and discriminator hit counts. + + :signature: clear-dispatch-profiling (library) => () + + :description: + + Resets the call site profile counts and discriminator hit + counts of all dispatch cache nodes for generic functions in the + given library and dependent libraries. + + :parameter library: An instance of :class:``. + +.. generic-function:: make-dispatch-statistics + + Instantiates a new object for collecting dispatch statistics in + preparation for report output. + + :signature: make-dispatch-statistics (shared-generic-caches?) => (#rest results) + + :parameter shared-generic-caches?: An instance of :class:``. + :value results: An instance of :class:``. + +.. generic-function:: clear-dispatch-statistics! + + Resets a :class:`` to its initial state. + + :signature: clear-dispatch-statistics! (profile) => () + + :parameter profile: An instance of :class:``. + +.. generic-function:: collect-dispatch-statistics + + Traverses generic function call sites in the given library and + collects dispatch statistics into the given profile results + object. + + :signature: collect-dispatch-statistics (library profile) => () + + :parameter library: An instance of :class:``. + :parameter profile: An instance of :class:``. + +.. generic-function:: print-dispatch-statistics + + Prints out a summary of dispatch profiling results. + + :signature: print-dispatch-statistics (app-results #key library profile-base full? by-library? hits-only? app-results-only? uncalled-methods? app-details?) => () + + :parameter app-results: An instance of :class:``. + :parameter #key library: An instance of ``false-or()``. + :parameter #key profile-base: An instance of ``false-or()``. + :parameter #key full?: An instance of :class:``. Defaults to ``#t``. + :parameter #key by-library?: An instance of :class:``. + :parameter #key hits-only?: An instance of :class:``. Defaults to ``#t``. + :parameter #key app-results-only?: An instance of :class:``. + :parameter #key uncalled-methods?: An instance of :class:``. + :parameter #key app-details?: An instance of :class:``. Defaults to ``#t``. + + :description: + + If a particular ``library`` is requested (by name) and if + ``by-library?`` is not false, then then ``profile-base`` must be + provided, and the output is placed in files with names based on + the profile base name, the library name, and the extension + ``.prf``. Otherwise, the results summary is written to + :var:`*standard-output*`. + + If ``hits-only?`` is not false (the default) then call sites that + never successfully used the method dispatch cache will be omitted + from the report. If ``uncalled-methods?`` is true, then the + report will list methods in the dispatch cache that were never + invoked. + +.. generic-function:: enable-generic-caches-only + + :signature: enable-generic-caches-only (library) => () + + Disable call-site method dispatch caches. + + :parameter library: An instance of :class:``. + + Configures the dispatch mechanisms in the Open Dylan run-time to + only use per-:drm:`` caches for method dispatch + rather than call-site specific caches. This was intended to be + used for comparison purposes during the development of the + dispatch mechanisms. + +.. generic-function:: enable-call-site-caches-only + + Configures the dispatch mechanisms in the Open Dylan run-time to + use call-site specific method dispatch caches. + + :signature: enable-call-site-caches-only (library) => () + + :parameter library: An instance of :class:``. + diff --git a/documentation/source/library-reference/index.rst b/documentation/source/library-reference/index.rst index a2bcf618a5..72c5c2e89b 100644 --- a/documentation/source/library-reference/index.rst +++ b/documentation/source/library-reference/index.rst @@ -25,6 +25,7 @@ Contents: collections/index coloring-stream/index common-dylan/index + dispatch-profiler/index dood/index dylan/index io/index From f03a7e288446c20633d29ec163cc0f15c4c0a104 Mon Sep 17 00:00:00 2001 From: "Peter S. Housel" Date: Sat, 22 Jun 2024 19:05:32 -0700 Subject: [PATCH 7/8] dispatch-profiler: Fix a compiler warning * sources/lib/dispatch-profiler/walk-dispatch.dylan (with-preserved-dispatch-walking-types): Comment out this currently unused macro definition. --- sources/lib/dispatch-profiler/walk-dispatch.dylan | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sources/lib/dispatch-profiler/walk-dispatch.dylan b/sources/lib/dispatch-profiler/walk-dispatch.dylan index 1042c02142..b1f726ad3c 100644 --- a/sources/lib/dispatch-profiler/walk-dispatch.dylan +++ b/sources/lib/dispatch-profiler/walk-dispatch.dylan @@ -247,6 +247,7 @@ define macro with-dispatch-walking-type end macro; +/* define macro with-preserved-dispatch-walking-types { with-preserved-dispatch-walking-types (?dws:expression) @@ -263,7 +264,7 @@ define macro with-preserved-dispatch-walking-types end block } end macro; - +*/ define method dispatch-walker From 114b29cb2e5cf89e5b9cb6d1bc91843962d85073 Mon Sep 17 00:00:00 2001 From: "Peter S. Housel" Date: Sun, 23 Jun 2024 21:47:35 -0700 Subject: [PATCH 8/8] documentation: Note dispatch-profiler changes in release notes --- documentation/source/release-notes/2024.2.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/documentation/source/release-notes/2024.2.rst b/documentation/source/release-notes/2024.2.rst index 22daa6abb2..c9562928ee 100644 --- a/documentation/source/release-notes/2024.2.rst +++ b/documentation/source/release-notes/2024.2.rst @@ -77,6 +77,9 @@ System Other ----- +* The :lib:`dispatch-profiler` library is now usable and is + documented. + * The obsolete (32-bit x86-only) ``stack-walker`` library was removed. Contributors