diff options
| author | Paul Eggert | 2019-09-14 00:32:01 -0700 |
|---|---|---|
| committer | Paul Eggert | 2019-09-14 00:37:26 -0700 |
| commit | bac66302e92bdd3a353102d2076548e7e83d92e5 (patch) | |
| tree | 6007fc144e41b0a0f0eb43bf11100eb4ce8085d4 /src/alloc.c | |
| parent | e4fb98b542c57fa4856fbeb14230ace34d910117 (diff) | |
| download | emacs-bac66302e92bdd3a353102d2076548e7e83d92e5.tar.gz emacs-bac66302e92bdd3a353102d2076548e7e83d92e5.zip | |
Improve gc-cons-percentage calculation
The old calculation relied on a hodgpodge of partly updated GC
stats to find a number to multiply gc-cons-percentage by.
The new one counts data found by the previous GC, plus half of
the data allocated since then; this is more systematic albeit
still ad hoc.
* src/alloc.c (consing_until_gc, gc_threshold, consing_threshold):
Now EMACS_INT, not intmax_t.
(HI_THRESHOLD): New macro.
(tally_consing): New function.
(make_interval, allocate_string, allocate_string_data)
(make_float, free_cons, allocate_vectorlike, Fmake_symbol): Use it.
(allow_garbage_collection, inhibit_garbage_collection)
(consing_threshold, garbage_collect):
Use HI_THRESHOLD rather than INTMAX_MAX.
(consing_threshold): New arg SINCE_GC. All callers changed.
(bump_consing_until_gc): Return new consing_until_gc, instead of
nil. All callers changed. Don’t worry about overflow since we
now saturate at HI_THRESHOLD. Guess that half of
recently-allocated objects are still alive, instead of relying on
the previous (even less-accurate) hodgepodge.
(maybe_garbage_collect): New function.
(garbage_collect): Work even if a finalizer disables or enables
memory profiling. Do not use malloc_probe if GC reclaimed nothing.
* src/lisp.h (maybe_gc): Call maybe_garbage_collect instead
of garbage_collect.
Diffstat (limited to 'src/alloc.c')
| -rw-r--r-- | src/alloc.c | 127 |
1 files changed, 74 insertions, 53 deletions
diff --git a/src/alloc.c b/src/alloc.c index ca8311cc00a..497f600551e 100644 --- a/src/alloc.c +++ b/src/alloc.c | |||
| @@ -224,7 +224,7 @@ struct emacs_globals globals; | |||
| 224 | 224 | ||
| 225 | /* maybe_gc collects garbage if this goes negative. */ | 225 | /* maybe_gc collects garbage if this goes negative. */ |
| 226 | 226 | ||
| 227 | intmax_t consing_until_gc; | 227 | EMACS_INT consing_until_gc; |
| 228 | 228 | ||
| 229 | #ifdef HAVE_PDUMPER | 229 | #ifdef HAVE_PDUMPER |
| 230 | /* Number of finalizers run: used to loop over GC until we stop | 230 | /* Number of finalizers run: used to loop over GC until we stop |
| @@ -238,9 +238,16 @@ bool gc_in_progress; | |||
| 238 | 238 | ||
| 239 | /* System byte and object counts reported by GC. */ | 239 | /* System byte and object counts reported by GC. */ |
| 240 | 240 | ||
| 241 | /* Assume byte counts fit in uintptr_t and object counts fit into | ||
| 242 | intptr_t. */ | ||
| 241 | typedef uintptr_t byte_ct; | 243 | typedef uintptr_t byte_ct; |
| 242 | typedef intptr_t object_ct; | 244 | typedef intptr_t object_ct; |
| 243 | 245 | ||
| 246 | /* Large-magnitude value for a threshold count, which fits in EMACS_INT. | ||
| 247 | Using only half the EMACS_INT range avoids overflow hassles. | ||
| 248 | There is no need to fit these counts into fixnums. */ | ||
| 249 | #define HI_THRESHOLD (EMACS_INT_MAX / 2) | ||
| 250 | |||
| 244 | /* Number of live and free conses etc. counted by the most-recent GC. */ | 251 | /* Number of live and free conses etc. counted by the most-recent GC. */ |
| 245 | 252 | ||
| 246 | static struct gcstat | 253 | static struct gcstat |
| @@ -299,7 +306,7 @@ static intptr_t garbage_collection_inhibited; | |||
| 299 | 306 | ||
| 300 | /* The GC threshold in bytes, the last time it was calculated | 307 | /* The GC threshold in bytes, the last time it was calculated |
| 301 | from gc-cons-threshold and gc-cons-percentage. */ | 308 | from gc-cons-threshold and gc-cons-percentage. */ |
| 302 | static intmax_t gc_threshold; | 309 | static EMACS_INT gc_threshold; |
| 303 | 310 | ||
| 304 | /* If nonzero, this is a warning delivered by malloc and not yet | 311 | /* If nonzero, this is a warning delivered by malloc and not yet |
| 305 | displayed. */ | 312 | displayed. */ |
| @@ -536,6 +543,15 @@ XFLOAT_INIT (Lisp_Object f, double n) | |||
| 536 | XFLOAT (f)->u.data = n; | 543 | XFLOAT (f)->u.data = n; |
| 537 | } | 544 | } |
| 538 | 545 | ||
| 546 | /* Account for allocation of NBYTES in the heap. This is a separate | ||
| 547 | function to avoid hassles with implementation-defined conversion | ||
| 548 | from unsigned to signed types. */ | ||
| 549 | static void | ||
| 550 | tally_consing (ptrdiff_t nbytes) | ||
| 551 | { | ||
| 552 | consing_until_gc -= nbytes; | ||
| 553 | } | ||
| 554 | |||
| 539 | #ifdef DOUG_LEA_MALLOC | 555 | #ifdef DOUG_LEA_MALLOC |
| 540 | static bool | 556 | static bool |
| 541 | pointers_fit_in_lispobj_p (void) | 557 | pointers_fit_in_lispobj_p (void) |
| @@ -1372,7 +1388,7 @@ make_interval (void) | |||
| 1372 | 1388 | ||
| 1373 | MALLOC_UNBLOCK_INPUT; | 1389 | MALLOC_UNBLOCK_INPUT; |
| 1374 | 1390 | ||
| 1375 | consing_until_gc -= sizeof (struct interval); | 1391 | tally_consing (sizeof (struct interval)); |
| 1376 | intervals_consed++; | 1392 | intervals_consed++; |
| 1377 | RESET_INTERVAL (val); | 1393 | RESET_INTERVAL (val); |
| 1378 | val->gcmarkbit = 0; | 1394 | val->gcmarkbit = 0; |
| @@ -1739,7 +1755,7 @@ allocate_string (void) | |||
| 1739 | MALLOC_UNBLOCK_INPUT; | 1755 | MALLOC_UNBLOCK_INPUT; |
| 1740 | 1756 | ||
| 1741 | ++strings_consed; | 1757 | ++strings_consed; |
| 1742 | consing_until_gc -= sizeof *s; | 1758 | tally_consing (sizeof *s); |
| 1743 | 1759 | ||
| 1744 | #ifdef GC_CHECK_STRING_BYTES | 1760 | #ifdef GC_CHECK_STRING_BYTES |
| 1745 | if (!noninteractive) | 1761 | if (!noninteractive) |
| @@ -1859,7 +1875,7 @@ allocate_string_data (struct Lisp_String *s, | |||
| 1859 | old_data->string = NULL; | 1875 | old_data->string = NULL; |
| 1860 | } | 1876 | } |
| 1861 | 1877 | ||
| 1862 | consing_until_gc -= needed; | 1878 | tally_consing (needed); |
| 1863 | } | 1879 | } |
| 1864 | 1880 | ||
| 1865 | 1881 | ||
| @@ -2464,7 +2480,7 @@ make_float (double float_value) | |||
| 2464 | 2480 | ||
| 2465 | XFLOAT_INIT (val, float_value); | 2481 | XFLOAT_INIT (val, float_value); |
| 2466 | eassert (!XFLOAT_MARKED_P (XFLOAT (val))); | 2482 | eassert (!XFLOAT_MARKED_P (XFLOAT (val))); |
| 2467 | consing_until_gc -= sizeof (struct Lisp_Float); | 2483 | tally_consing (sizeof (struct Lisp_Float)); |
| 2468 | floats_consed++; | 2484 | floats_consed++; |
| 2469 | return val; | 2485 | return val; |
| 2470 | } | 2486 | } |
| @@ -2535,8 +2551,8 @@ free_cons (struct Lisp_Cons *ptr) | |||
| 2535 | ptr->u.s.u.chain = cons_free_list; | 2551 | ptr->u.s.u.chain = cons_free_list; |
| 2536 | ptr->u.s.car = dead_object (); | 2552 | ptr->u.s.car = dead_object (); |
| 2537 | cons_free_list = ptr; | 2553 | cons_free_list = ptr; |
| 2538 | if (INT_ADD_WRAPV (consing_until_gc, sizeof *ptr, &consing_until_gc)) | 2554 | ptrdiff_t nbytes = sizeof *ptr; |
| 2539 | consing_until_gc = INTMAX_MAX; | 2555 | tally_consing (-nbytes); |
| 2540 | } | 2556 | } |
| 2541 | 2557 | ||
| 2542 | DEFUN ("cons", Fcons, Scons, 2, 2, 0, | 2558 | DEFUN ("cons", Fcons, Scons, 2, 2, 0, |
| @@ -3153,7 +3169,7 @@ allocate_vectorlike (ptrdiff_t len) | |||
| 3153 | if (find_suspicious_object_in_range (p, (char *) p + nbytes)) | 3169 | if (find_suspicious_object_in_range (p, (char *) p + nbytes)) |
| 3154 | emacs_abort (); | 3170 | emacs_abort (); |
| 3155 | 3171 | ||
| 3156 | consing_until_gc -= nbytes; | 3172 | tally_consing (nbytes); |
| 3157 | vector_cells_consed += len; | 3173 | vector_cells_consed += len; |
| 3158 | 3174 | ||
| 3159 | MALLOC_UNBLOCK_INPUT; | 3175 | MALLOC_UNBLOCK_INPUT; |
| @@ -3438,7 +3454,7 @@ Its value is void, and its function definition and property list are nil. */) | |||
| 3438 | MALLOC_UNBLOCK_INPUT; | 3454 | MALLOC_UNBLOCK_INPUT; |
| 3439 | 3455 | ||
| 3440 | init_symbol (val, name); | 3456 | init_symbol (val, name); |
| 3441 | consing_until_gc -= sizeof (struct Lisp_Symbol); | 3457 | tally_consing (sizeof (struct Lisp_Symbol)); |
| 3442 | symbols_consed++; | 3458 | symbols_consed++; |
| 3443 | return val; | 3459 | return val; |
| 3444 | } | 3460 | } |
| @@ -5477,7 +5493,7 @@ staticpro (Lisp_Object const *varaddress) | |||
| 5477 | static void | 5493 | static void |
| 5478 | allow_garbage_collection (intmax_t consing) | 5494 | allow_garbage_collection (intmax_t consing) |
| 5479 | { | 5495 | { |
| 5480 | consing_until_gc = consing - (INTMAX_MAX - consing_until_gc); | 5496 | consing_until_gc = consing - (HI_THRESHOLD - consing_until_gc); |
| 5481 | garbage_collection_inhibited--; | 5497 | garbage_collection_inhibited--; |
| 5482 | } | 5498 | } |
| 5483 | 5499 | ||
| @@ -5487,7 +5503,7 @@ inhibit_garbage_collection (void) | |||
| 5487 | ptrdiff_t count = SPECPDL_INDEX (); | 5503 | ptrdiff_t count = SPECPDL_INDEX (); |
| 5488 | record_unwind_protect_intmax (allow_garbage_collection, consing_until_gc); | 5504 | record_unwind_protect_intmax (allow_garbage_collection, consing_until_gc); |
| 5489 | garbage_collection_inhibited++; | 5505 | garbage_collection_inhibited++; |
| 5490 | consing_until_gc = INTMAX_MAX; | 5506 | consing_until_gc = HI_THRESHOLD; |
| 5491 | return count; | 5507 | return count; |
| 5492 | } | 5508 | } |
| 5493 | 5509 | ||
| @@ -5761,11 +5777,13 @@ mark_and_sweep_weak_table_contents (void) | |||
| 5761 | } | 5777 | } |
| 5762 | } | 5778 | } |
| 5763 | 5779 | ||
| 5764 | /* Return the number of bytes to cons between GCs, assuming | 5780 | /* Return the number of bytes to cons between GCs, given THRESHOLD and |
| 5765 | gc-cons-threshold is THRESHOLD and gc-cons-percentage is | 5781 | PERCENTAGE. When calculating a threshold based on PERCENTAGE, |
| 5766 | PERCENTAGE. */ | 5782 | assume SINCE_GC bytes have been allocated since the most recent GC. |
| 5767 | static intmax_t | 5783 | The returned value is positive and no greater than HI_THRESHOLD. */ |
| 5768 | consing_threshold (intmax_t threshold, Lisp_Object percentage) | 5784 | static EMACS_INT |
| 5785 | consing_threshold (intmax_t threshold, Lisp_Object percentage, | ||
| 5786 | intmax_t since_gc) | ||
| 5769 | { | 5787 | { |
| 5770 | if (!NILP (Vmemory_full)) | 5788 | if (!NILP (Vmemory_full)) |
| 5771 | return memory_full_cons_threshold; | 5789 | return memory_full_cons_threshold; |
| @@ -5775,42 +5793,33 @@ consing_threshold (intmax_t threshold, Lisp_Object percentage) | |||
| 5775 | if (FLOATP (percentage)) | 5793 | if (FLOATP (percentage)) |
| 5776 | { | 5794 | { |
| 5777 | double tot = (XFLOAT_DATA (percentage) | 5795 | double tot = (XFLOAT_DATA (percentage) |
| 5778 | * total_bytes_of_live_objects ()); | 5796 | * (total_bytes_of_live_objects () + since_gc)); |
| 5779 | if (threshold < tot) | 5797 | if (threshold < tot) |
| 5780 | { | 5798 | { |
| 5781 | if (tot < INTMAX_MAX) | 5799 | if (tot < HI_THRESHOLD) |
| 5782 | threshold = tot; | 5800 | return tot; |
| 5783 | else | 5801 | else |
| 5784 | threshold = INTMAX_MAX; | 5802 | return HI_THRESHOLD; |
| 5785 | } | 5803 | } |
| 5786 | } | 5804 | } |
| 5787 | return threshold; | 5805 | return min (threshold, HI_THRESHOLD); |
| 5788 | } | 5806 | } |
| 5789 | } | 5807 | } |
| 5790 | 5808 | ||
| 5791 | /* Adjust consing_until_gc, assuming gc-cons-threshold is THRESHOLD and | 5809 | /* Adjust consing_until_gc and gc_threshold, given THRESHOLD and PERCENTAGE. |
| 5792 | gc-cons-percentage is PERCENTAGE. */ | 5810 | Return the updated consing_until_gc. */ |
| 5793 | static Lisp_Object | 5811 | |
| 5812 | static EMACS_INT | ||
| 5794 | bump_consing_until_gc (intmax_t threshold, Lisp_Object percentage) | 5813 | bump_consing_until_gc (intmax_t threshold, Lisp_Object percentage) |
| 5795 | { | 5814 | { |
| 5796 | /* If consing_until_gc is negative leave it alone, since this prevents | 5815 | /* Guesstimate that half the bytes allocated since the most |
| 5797 | negative integer overflow and a GC would have been done soon anyway. */ | 5816 | recent GC are still in use. */ |
| 5798 | if (0 <= consing_until_gc) | 5817 | EMACS_INT since_gc = (gc_threshold - consing_until_gc) >> 1; |
| 5799 | { | 5818 | EMACS_INT new_gc_threshold = consing_threshold (threshold, percentage, |
| 5800 | threshold = consing_threshold (threshold, percentage); | 5819 | since_gc); |
| 5801 | intmax_t sum; | 5820 | consing_until_gc += new_gc_threshold - gc_threshold; |
| 5802 | if (INT_ADD_WRAPV (consing_until_gc, threshold - gc_threshold, &sum)) | 5821 | gc_threshold = new_gc_threshold; |
| 5803 | { | 5822 | return consing_until_gc; |
| 5804 | /* Scale the threshold down so that consing_until_gc does | ||
| 5805 | not overflow. */ | ||
| 5806 | sum = INTMAX_MAX; | ||
| 5807 | threshold = INTMAX_MAX - consing_until_gc + gc_threshold; | ||
| 5808 | } | ||
| 5809 | consing_until_gc = sum; | ||
| 5810 | gc_threshold = threshold; | ||
| 5811 | } | ||
| 5812 | |||
| 5813 | return Qnil; | ||
| 5814 | } | 5823 | } |
| 5815 | 5824 | ||
| 5816 | /* Watch changes to gc-cons-threshold. */ | 5825 | /* Watch changes to gc-cons-threshold. */ |
| @@ -5821,7 +5830,8 @@ watch_gc_cons_threshold (Lisp_Object symbol, Lisp_Object newval, | |||
| 5821 | intmax_t threshold; | 5830 | intmax_t threshold; |
| 5822 | if (! (INTEGERP (newval) && integer_to_intmax (newval, &threshold))) | 5831 | if (! (INTEGERP (newval) && integer_to_intmax (newval, &threshold))) |
| 5823 | return Qnil; | 5832 | return Qnil; |
| 5824 | return bump_consing_until_gc (threshold, Vgc_cons_percentage); | 5833 | bump_consing_until_gc (threshold, Vgc_cons_percentage); |
| 5834 | return Qnil; | ||
| 5825 | } | 5835 | } |
| 5826 | 5836 | ||
| 5827 | /* Watch changes to gc-cons-percentage. */ | 5837 | /* Watch changes to gc-cons-percentage. */ |
| @@ -5829,7 +5839,18 @@ static Lisp_Object | |||
| 5829 | watch_gc_cons_percentage (Lisp_Object symbol, Lisp_Object newval, | 5839 | watch_gc_cons_percentage (Lisp_Object symbol, Lisp_Object newval, |
| 5830 | Lisp_Object operation, Lisp_Object where) | 5840 | Lisp_Object operation, Lisp_Object where) |
| 5831 | { | 5841 | { |
| 5832 | return bump_consing_until_gc (gc_cons_threshold, newval); | 5842 | bump_consing_until_gc (gc_cons_threshold, newval); |
| 5843 | return Qnil; | ||
| 5844 | } | ||
| 5845 | |||
| 5846 | /* It may be time to collect garbage. Recalculate consing_until_gc, | ||
| 5847 | since it might depend on current usage, and do the garbage | ||
| 5848 | collection if the recalculation says so. */ | ||
| 5849 | void | ||
| 5850 | maybe_garbage_collect (void) | ||
| 5851 | { | ||
| 5852 | if (bump_consing_until_gc (gc_cons_threshold, Vgc_cons_percentage) < 0) | ||
| 5853 | garbage_collect (); | ||
| 5833 | } | 5854 | } |
| 5834 | 5855 | ||
| 5835 | /* Subroutine of Fgarbage_collect that does most of the work. */ | 5856 | /* Subroutine of Fgarbage_collect that does most of the work. */ |
| @@ -5841,7 +5862,6 @@ garbage_collect (void) | |||
| 5841 | bool message_p; | 5862 | bool message_p; |
| 5842 | ptrdiff_t count = SPECPDL_INDEX (); | 5863 | ptrdiff_t count = SPECPDL_INDEX (); |
| 5843 | struct timespec start; | 5864 | struct timespec start; |
| 5844 | byte_ct tot_before = 0; | ||
| 5845 | 5865 | ||
| 5846 | eassert (weak_hash_tables == NULL); | 5866 | eassert (weak_hash_tables == NULL); |
| 5847 | 5867 | ||
| @@ -5856,14 +5876,15 @@ garbage_collect (void) | |||
| 5856 | FOR_EACH_BUFFER (nextb) | 5876 | FOR_EACH_BUFFER (nextb) |
| 5857 | compact_buffer (nextb); | 5877 | compact_buffer (nextb); |
| 5858 | 5878 | ||
| 5859 | if (profiler_memory_running) | 5879 | byte_ct tot_before = (profiler_memory_running |
| 5860 | tot_before = total_bytes_of_live_objects (); | 5880 | ? total_bytes_of_live_objects () |
| 5881 | : (byte_ct) -1); | ||
| 5861 | 5882 | ||
| 5862 | start = current_timespec (); | 5883 | start = current_timespec (); |
| 5863 | 5884 | ||
| 5864 | /* In case user calls debug_print during GC, | 5885 | /* In case user calls debug_print during GC, |
| 5865 | don't let that cause a recursive GC. */ | 5886 | don't let that cause a recursive GC. */ |
| 5866 | consing_until_gc = INTMAX_MAX; | 5887 | consing_until_gc = HI_THRESHOLD; |
| 5867 | 5888 | ||
| 5868 | /* Save what's currently displayed in the echo area. Don't do that | 5889 | /* Save what's currently displayed in the echo area. Don't do that |
| 5869 | if we are GC'ing because we've run out of memory, since | 5890 | if we are GC'ing because we've run out of memory, since |
| @@ -5975,7 +5996,7 @@ garbage_collect (void) | |||
| 5975 | unblock_input (); | 5996 | unblock_input (); |
| 5976 | 5997 | ||
| 5977 | consing_until_gc = gc_threshold | 5998 | consing_until_gc = gc_threshold |
| 5978 | = consing_threshold (gc_cons_threshold, Vgc_cons_percentage); | 5999 | = consing_threshold (gc_cons_threshold, Vgc_cons_percentage, 0); |
| 5979 | 6000 | ||
| 5980 | if (garbage_collection_messages && NILP (Vmemory_full)) | 6001 | if (garbage_collection_messages && NILP (Vmemory_full)) |
| 5981 | { | 6002 | { |
| @@ -6008,11 +6029,11 @@ garbage_collect (void) | |||
| 6008 | gcs_done++; | 6029 | gcs_done++; |
| 6009 | 6030 | ||
| 6010 | /* Collect profiling data. */ | 6031 | /* Collect profiling data. */ |
| 6011 | if (profiler_memory_running) | 6032 | if (tot_before != (byte_ct) -1) |
| 6012 | { | 6033 | { |
| 6013 | byte_ct tot_after = total_bytes_of_live_objects (); | 6034 | byte_ct tot_after = total_bytes_of_live_objects (); |
| 6014 | byte_ct swept = tot_before <= tot_after ? 0 : tot_before - tot_after; | 6035 | if (tot_after < tot_before) |
| 6015 | malloc_probe (min (swept, SIZE_MAX)); | 6036 | malloc_probe (min (tot_before - tot_after, SIZE_MAX)); |
| 6016 | } | 6037 | } |
| 6017 | } | 6038 | } |
| 6018 | 6039 | ||