Re: Improve "busy" numeric code's performance [was: Re: Big Integers]
Peter Bex <[email protected]>
| Newsgroups | gmane.lisp.scheme.chicken.devel,gmane.lisp.scheme.chicken |
|---|---|
| Message-ID | <Zk4HNfmkw8HgDieu@doggett> |
On Wed, May 22, 2024 at 02:42:38PM +0200, Peter Bex wrote: > Attached are two patches, one which has this bigger improvement, and > another which is a minor improvement which translates to shaving about > a second of runtime off your program (at least on my machine). The minor patch was incorrect. I copied some code from C_s_a_i_remainder into C_s_a_i_modulo inline, but that code had an early return for the case where both numbers are flonums. This code needs to be adjusted to handle the case when the arguments aren't of the same sign, just like we do after returning from integer_divrem(). I'm sending both patches again for your convenience. The second patch has a fix for the aforementioned issue. Cheers, Peter
0001-Don-t-free-the-memory-for-the-scratchspace-on-minor-.patch
(text/plain, 1.4 KB)
From d2999bb77a6ba8665be9ca997a85481b670705ea Mon Sep 17 00:00:00 2001 From: Peter Bex <[email protected]> Date: Wed, 22 May 2024 13:53:28 +0200 Subject: [PATCH 1/2] Don't free the memory for the scratchspace on minor GC This is too costly if we're in a loop that's operating on the scratchspace, because it will just reallocate the scratchspace, and use a small scratchspace which will cause excessive reallocation. --- runtime.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/runtime.c b/runtime.c index 9becd2aa..5116ed37 100644 --- a/runtime.c +++ b/runtime.c @@ -3675,13 +3675,16 @@ C_regparm void C_fcall C_reclaim(void *trampoline, C_word c) } /* GC will have copied any live objects out of scratch space: clear it */ - if (C_scratchspace_start != NULL) { - C_free(C_scratchspace_start); - C_scratchspace_start = NULL; - C_scratchspace_top = NULL; - C_scratchspace_limit = NULL; + if (C_scratchspace_start != C_scratchspace_top) { + /* And drop the scratchspace in case of a major or reallocating collection */ + if (gc_mode != GC_MINOR) { + C_free(C_scratchspace_start); + C_scratchspace_start = NULL; + C_scratchspace_limit = NULL; + scratchspace_size = 0; + } + C_scratchspace_top = C_scratchspace_start; C_scratch_usage = 0; - scratchspace_size = 0; } if(gc_mode == GC_MAJOR) { -- 2.42.0
0002-Call-integer_divrem-straight-from-the-modulo-operati.patch
(text/plain, 3.1 KB)
From 24a7600f3766c0313ef2cbc611499c10e70b5d40 Mon Sep 17 00:00:00 2001 From: Peter Bex <[email protected]> Date: Wed, 22 May 2024 13:58:11 +0200 Subject: [PATCH 2/2] Call integer_divrem straight from the modulo operations These would call C_s_a_i_remainder initially, which performs many of the same checks that we've just already done before actually calling integer_divrem. It also allocates more memory on the stack which is not necessary. In the case of the generic operator, doing so requires duplicating the code which converts floating-point integers to exact integers and back again. In the case of the integer-specific operator, there's no point in calling the *generic* remainder function in the first place! --- runtime.c | 37 +++++++++++++++++++++++++++++++++---- 1 file changed, 33 insertions(+), 4 deletions(-) diff --git a/runtime.c b/runtime.c index 5116ed37..29dbcb00 100644 --- a/runtime.c +++ b/runtime.c @@ -9243,7 +9243,8 @@ C_s_a_u_i_integer_remainder(C_word **ptr, C_word n, C_word x, C_word y) C_regparm C_word C_fcall C_s_a_i_modulo(C_word **ptr, C_word n, C_word x, C_word y) { - C_word ab[C_SIZEOF_FIX_BIGNUM], *a = ab, r; + C_word ab[C_SIZEOF_FIX_BIGNUM], *a = ab, r, + nx = C_SCHEME_FALSE, ny = C_SCHEME_FALSE; if (!C_truep(C_i_integerp(x))) barf(C_BAD_ARGUMENT_TYPE_NO_INTEGER_ERROR, "modulo", x); @@ -9251,13 +9252,41 @@ C_s_a_i_modulo(C_word **ptr, C_word n, C_word x, C_word y) barf(C_BAD_ARGUMENT_TYPE_NO_INTEGER_ERROR, "modulo", y); if (C_truep(C_i_zerop(y))) C_div_by_zero_error("modulo"); - r = C_s_a_i_remainder(&a, 2, x, y); - if (C_i_positivep(y) != C_i_positivep(r) && !C_truep(C_i_zerop(r))) { + if (C_truep(C_i_flonump(x))) { + if C_truep(C_i_flonump(y)) { + double dx = C_flonum_magnitude(x), dy = C_flonum_magnitude(y), tmp; + + C_modf(dx / dy, &tmp); + tmp = dx - tmp * dy; + if ((dx > 0.0) != (dy > 0.0) && tmp != 0.0) { + return C_flonum(ptr, tmp + dy); + } else { + return C_flonum(ptr, tmp); + } + } + x = nx = C_s_a_u_i_flo_to_int(&a, 1, x); + } + if (C_truep(C_i_flonump(y))) { + y = ny = C_s_a_u_i_flo_to_int(&a, 1, y); + } + + integer_divrem(&a, x, y, NULL, &r); + if (C_i_positivep(y) != C_i_positivep(r) && r != C_fix(0)) { C_word m = C_s_a_i_plus(ptr, 2, r, y); m = move_buffer_object(ptr, ab, m); clear_buffer_object(ab, r); r = m; } + + if (C_truep(nx) || C_truep(ny)) { + C_word newr = C_a_i_exact_to_inexact(ptr, 1, r); + clear_buffer_object(ab, r); + r = newr; + + clear_buffer_object(ab, nx); + clear_buffer_object(ab, ny); + } + return move_buffer_object(ptr, ab, r); } @@ -9267,7 +9296,7 @@ C_s_a_u_i_integer_modulo(C_word **ptr, C_word n, C_word x, C_word y) C_word ab[C_SIZEOF_FIX_BIGNUM], *a = ab, r; if (y == C_fix(0)) C_div_by_zero_error("modulo"); - r = C_s_a_i_remainder(&a, 2, x, y); + integer_divrem(&a, x, y, NULL, &r); if (C_i_positivep(y) != C_i_positivep(r) && r != C_fix(0)) { C_word m = C_s_a_u_i_integer_plus(ptr, 2, r, y); m = move_buffer_object(ptr, ab, m); -- 2.42.0