diff options
| author | Stefan Monnier | 2023-03-10 15:54:10 -0500 |
|---|---|---|
| committer | Stefan Monnier | 2023-03-10 15:54:10 -0500 |
| commit | d236ab09300070696f21ebfda49678b11c2327eb (patch) | |
| tree | b49f1f03f7f9168695209668f0560868e6d76672 /src | |
| parent | 83be04c66ffaec86aee136b9a94979169d1ba68d (diff) | |
| download | emacs-d236ab09300070696f21ebfda49678b11c2327eb.tar.gz emacs-d236ab09300070696f21ebfda49678b11c2327eb.zip | |
src/profiler.c: Keep track of the discarded counts
When the table overflows and wh evict entries, keep track of those
counts in a global counter so we can see the proportion of
samples this represents.
* src/profiler.c (struct profiler_log): Add `discarded` field.
(evict_lower_half): Change arg to be `struct profiler_log`.
Transfer counts to the new `discarded` field.
(record_backtrace): Change arg to be `struct profiler_log`.
(add_sample): Adjust call accordingly.
(export_log): Add `discarded` counts to the result.
Onle add the GC and `discarded` counts if they're non-zero.
(syms_of_profiler): Define new symbol `Discarded Samples`.
Diffstat (limited to 'src')
| -rw-r--r-- | src/profiler.c | 39 |
1 files changed, 23 insertions, 16 deletions
diff --git a/src/profiler.c b/src/profiler.c index d5a5a2cf5f3..6217071ef9c 100644 --- a/src/profiler.c +++ b/src/profiler.c | |||
| @@ -51,7 +51,8 @@ static const struct hash_table_test hashtest_profiler = | |||
| 51 | 51 | ||
| 52 | struct profiler_log { | 52 | struct profiler_log { |
| 53 | Lisp_Object log; | 53 | Lisp_Object log; |
| 54 | EMACS_INT gc_count; | 54 | EMACS_INT gc_count; /* Samples taken during GC. */ |
| 55 | EMACS_INT discarded; /* Samples evicted during table overflow. */ | ||
| 55 | }; | 56 | }; |
| 56 | 57 | ||
| 57 | static struct profiler_log | 58 | static struct profiler_log |
| @@ -70,7 +71,7 @@ make_log (void) | |||
| 70 | DEFAULT_REHASH_SIZE, | 71 | DEFAULT_REHASH_SIZE, |
| 71 | DEFAULT_REHASH_THRESHOLD, | 72 | DEFAULT_REHASH_THRESHOLD, |
| 72 | Qnil, false), | 73 | Qnil, false), |
| 73 | 0 }; | 74 | 0, 0 }; |
| 74 | struct Lisp_Hash_Table *h = XHASH_TABLE (log.log); | 75 | struct Lisp_Hash_Table *h = XHASH_TABLE (log.log); |
| 75 | 76 | ||
| 76 | /* What is special about our hash-tables is that the values are pre-filled | 77 | /* What is special about our hash-tables is that the values are pre-filled |
| @@ -123,8 +124,9 @@ static EMACS_INT approximate_median (log_t *log, | |||
| 123 | } | 124 | } |
| 124 | } | 125 | } |
| 125 | 126 | ||
| 126 | static void evict_lower_half (log_t *log) | 127 | static void evict_lower_half (struct profiler_log *plog) |
| 127 | { | 128 | { |
| 129 | log_t *log = XHASH_TABLE (plog->log); | ||
| 128 | ptrdiff_t size = ASIZE (log->key_and_value) / 2; | 130 | ptrdiff_t size = ASIZE (log->key_and_value) / 2; |
| 129 | EMACS_INT median = approximate_median (log, 0, size); | 131 | EMACS_INT median = approximate_median (log, 0, size); |
| 130 | 132 | ||
| @@ -134,6 +136,8 @@ static void evict_lower_half (log_t *log) | |||
| 134 | if (XFIXNUM (HASH_VALUE (log, i)) <= median) | 136 | if (XFIXNUM (HASH_VALUE (log, i)) <= median) |
| 135 | { | 137 | { |
| 136 | Lisp_Object key = HASH_KEY (log, i); | 138 | Lisp_Object key = HASH_KEY (log, i); |
| 139 | EMACS_INT count = XFIXNUM (HASH_VALUE (log, i)); | ||
| 140 | plog->discarded = saturated_add (plog->discarded, count); | ||
| 137 | { /* FIXME: we could make this more efficient. */ | 141 | { /* FIXME: we could make this more efficient. */ |
| 138 | Lisp_Object tmp; | 142 | Lisp_Object tmp; |
| 139 | XSET_HASH_TABLE (tmp, log); /* FIXME: Use make_lisp_ptr. */ | 143 | XSET_HASH_TABLE (tmp, log); /* FIXME: Use make_lisp_ptr. */ |
| @@ -155,12 +159,12 @@ static void evict_lower_half (log_t *log) | |||
| 155 | size for memory. */ | 159 | size for memory. */ |
| 156 | 160 | ||
| 157 | static void | 161 | static void |
| 158 | record_backtrace (log_t *log, EMACS_INT count) | 162 | record_backtrace (struct profiler_log *plog, EMACS_INT count) |
| 159 | { | 163 | { |
| 164 | eassert (HASH_TABLE_P (plog->log)); | ||
| 165 | log_t *log = XHASH_TABLE (plog->log); | ||
| 160 | if (log->next_free < 0) | 166 | if (log->next_free < 0) |
| 161 | /* FIXME: transfer the evicted counts to a special entry rather | 167 | evict_lower_half (plog); |
| 162 | than dropping them on the floor. */ | ||
| 163 | evict_lower_half (log); | ||
| 164 | ptrdiff_t index = log->next_free; | 168 | ptrdiff_t index = log->next_free; |
| 165 | 169 | ||
| 166 | /* Get a "working memory" vector. */ | 170 | /* Get a "working memory" vector. */ |
| @@ -240,7 +244,7 @@ static EMACS_INT current_sampling_interval; | |||
| 240 | /* Signal handler for sampling profiler. */ | 244 | /* Signal handler for sampling profiler. */ |
| 241 | 245 | ||
| 242 | static void | 246 | static void |
| 243 | add_sample (struct profiler_log *log, EMACS_INT count) | 247 | add_sample (struct profiler_log *plog, EMACS_INT count) |
| 244 | { | 248 | { |
| 245 | if (EQ (backtrace_top_function (), QAutomatic_GC)) /* bug#60237 */ | 249 | if (EQ (backtrace_top_function (), QAutomatic_GC)) /* bug#60237 */ |
| 246 | /* Special case the time-count inside GC because the hash-table | 250 | /* Special case the time-count inside GC because the hash-table |
| @@ -249,12 +253,9 @@ add_sample (struct profiler_log *log, EMACS_INT count) | |||
| 249 | not expect the ARRAY_MARK_FLAG to be set. We could try and | 253 | not expect the ARRAY_MARK_FLAG to be set. We could try and |
| 250 | harden the hash-table code, but it doesn't seem worth the | 254 | harden the hash-table code, but it doesn't seem worth the |
| 251 | effort. */ | 255 | effort. */ |
| 252 | log->gc_count = saturated_add (log->gc_count, count); | 256 | plog->gc_count = saturated_add (plog->gc_count, count); |
| 253 | else | 257 | else |
| 254 | { | 258 | record_backtrace (plog, count); |
| 255 | eassert (HASH_TABLE_P (log->log)); | ||
| 256 | record_backtrace (XHASH_TABLE (log->log), count); | ||
| 257 | } | ||
| 258 | } | 259 | } |
| 259 | 260 | ||
| 260 | 261 | ||
| @@ -424,9 +425,14 @@ static Lisp_Object | |||
| 424 | export_log (struct profiler_log *log) | 425 | export_log (struct profiler_log *log) |
| 425 | { | 426 | { |
| 426 | Lisp_Object result = log->log; | 427 | Lisp_Object result = log->log; |
| 427 | Fputhash (CALLN (Fvector, QAutomatic_GC, Qnil), | 428 | if (log->gc_count) |
| 428 | make_fixnum (log->gc_count), | 429 | Fputhash (CALLN (Fvector, QAutomatic_GC, Qnil), |
| 429 | result); | 430 | make_fixnum (log->gc_count), |
| 431 | result); | ||
| 432 | if (log->discarded) | ||
| 433 | Fputhash (CALLN (Fvector, QDiscarded_Samples, Qnil), | ||
| 434 | make_fixnum (log->discarded), | ||
| 435 | result); | ||
| 430 | /* Here we're making the log visible to Elisp, so it's not safe any | 436 | /* Here we're making the log visible to Elisp, so it's not safe any |
| 431 | more for our use afterwards since we can't rely on its special | 437 | more for our use afterwards since we can't rely on its special |
| 432 | pre-allocated keys anymore. So we have to allocate a new one. */ | 438 | pre-allocated keys anymore. So we have to allocate a new one. */ |
| @@ -595,6 +601,7 @@ to make room for new entries. */); | |||
| 595 | profiler_log_size = 10000; | 601 | profiler_log_size = 10000; |
| 596 | 602 | ||
| 597 | DEFSYM (Qprofiler_backtrace_equal, "profiler-backtrace-equal"); | 603 | DEFSYM (Qprofiler_backtrace_equal, "profiler-backtrace-equal"); |
| 604 | DEFSYM (QDiscarded_Samples, "Discarded Samples"); | ||
| 598 | 605 | ||
| 599 | defsubr (&Sfunction_equal); | 606 | defsubr (&Sfunction_equal); |
| 600 | 607 | ||