diff options
| author | Paul Eggert | 2024-07-11 15:28:58 +0200 |
|---|---|---|
| committer | Paul Eggert | 2024-07-11 16:01:41 +0200 |
| commit | e30706fd12bce459b56123ad36a4e8c1c9d374e1 (patch) | |
| tree | a7f80134870feeb2904b1b7295c00cfb5f6501cd /src | |
| parent | e8b3c4cb58ce6e78b9bcfb9146f0ac6ece0d3055 (diff) | |
| download | emacs-e30706fd12bce459b56123ad36a4e8c1c9d374e1.tar.gz emacs-e30706fd12bce459b56123ad36a4e8c1c9d374e1.zip | |
Avoid mpz for some common timestamp cases
Performance problem reported by Gerd Möllmann and Mattias Engdegård in:
https://lists.gnu.org/r/emacs-devel/2024-06/msg00530.html
https://lists.gnu.org/r/emacs-devel/2024-06/msg00539.html
* src/timefns.c (CFORM_SECS_ONLY): The exact tv_nsec value is now
ignored if nonnegative (i.e., the only thing that matters is that
it’s nonnegative).
(decode_time_components): Use intmax_t instead of mpz arithmetic
if the tick count fits. Add another ‘default: eassume (false);’
so that the revised code pacifies --enable-gcc-warnings with GCC
11.4.0 on x86-64.
Diffstat (limited to 'src')
| -rw-r--r-- | src/timefns.c | 84 |
1 files changed, 73 insertions, 11 deletions
diff --git a/src/timefns.c b/src/timefns.c index ad39d1307cd..8c30016360d 100644 --- a/src/timefns.c +++ b/src/timefns.c | |||
| @@ -570,7 +570,8 @@ enum cform | |||
| 570 | { | 570 | { |
| 571 | CFORM_TICKS_HZ, /* struct ticks_hz */ | 571 | CFORM_TICKS_HZ, /* struct ticks_hz */ |
| 572 | CFORM_TIMESPEC, /* struct timespec */ | 572 | CFORM_TIMESPEC, /* struct timespec */ |
| 573 | CFORM_SECS_ONLY, /* struct timespec but tv_nsec == 0 if timespec valid */ | 573 | CFORM_SECS_ONLY, /* struct timespec but tv_nsec irrelevant |
| 574 | if timespec valid */ | ||
| 574 | CFORM_DOUBLE /* double */ | 575 | CFORM_DOUBLE /* double */ |
| 575 | }; | 576 | }; |
| 576 | 577 | ||
| @@ -894,11 +895,22 @@ decode_time_components (enum timeform form, | |||
| 894 | } | 895 | } |
| 895 | break; | 896 | break; |
| 896 | 897 | ||
| 897 | default: | 898 | case TIMEFORM_HI_LO: |
| 898 | if (! (INTEGERP (high) && INTEGERP (low) | 899 | hz = make_fixnum (1); |
| 899 | && FIXNUMP (usec) && FIXNUMP (psec))) | 900 | goto check_high_low; |
| 900 | return (struct err_time) { .err = EINVAL }; | 901 | |
| 902 | case TIMEFORM_HI_LO_US: | ||
| 903 | hz = make_fixnum (1000000); | ||
| 904 | goto check_high_low_usec; | ||
| 901 | 905 | ||
| 906 | case TIMEFORM_HI_LO_US_PS: | ||
| 907 | hz = trillion; | ||
| 908 | if (!FIXNUMP (psec)) | ||
| 909 | return (struct err_time) { .err = EINVAL }; | ||
| 910 | check_high_low_usec: | ||
| 911 | if (!FIXNUMP (usec)) | ||
| 912 | return (struct err_time) { .err = EINVAL }; | ||
| 913 | check_high_low: | ||
| 902 | { | 914 | { |
| 903 | EMACS_INT us = XFIXNUM (usec); | 915 | EMACS_INT us = XFIXNUM (usec); |
| 904 | EMACS_INT ps = XFIXNUM (psec); | 916 | EMACS_INT ps = XFIXNUM (psec); |
| @@ -906,25 +918,73 @@ decode_time_components (enum timeform form, | |||
| 906 | /* Normalize out-of-range lower-order components by carrying | 918 | /* Normalize out-of-range lower-order components by carrying |
| 907 | each overflow into the next higher-order component. */ | 919 | each overflow into the next higher-order component. */ |
| 908 | us += ps / 1000000 - (ps % 1000000 < 0); | 920 | us += ps / 1000000 - (ps % 1000000 < 0); |
| 921 | EMACS_INT s_from_us_ps = us / 1000000 - (us % 1000000 < 0); | ||
| 922 | ps = ps % 1000000 + 1000000 * (ps % 1000000 < 0); | ||
| 923 | us = us % 1000000 + 1000000 * (us % 1000000 < 0); | ||
| 924 | |||
| 925 | if (FASTER_TIMEFNS && FIXNUMP (high) && FIXNUMP (low)) | ||
| 926 | { | ||
| 927 | /* Use intmax_t arithmetic if the tick count fits. */ | ||
| 928 | intmax_t iticks; | ||
| 929 | bool v = false; | ||
| 930 | v |= ckd_mul (&iticks, XFIXNUM (high), 1 << LO_TIME_BITS); | ||
| 931 | v |= ckd_add (&iticks, iticks, XFIXNUM (low) + s_from_us_ps); | ||
| 932 | if (!v) | ||
| 933 | { | ||
| 934 | if (cform == CFORM_TIMESPEC || cform == CFORM_SECS_ONLY) | ||
| 935 | return (struct err_time) { | ||
| 936 | .time = { | ||
| 937 | .ts = s_ns_to_timespec (iticks, us * 1000 + ps / 1000) | ||
| 938 | } | ||
| 939 | }; | ||
| 940 | |||
| 941 | switch (form) | ||
| 942 | { | ||
| 943 | case TIMEFORM_HI_LO: | ||
| 944 | break; | ||
| 945 | |||
| 946 | case TIMEFORM_HI_LO_US: | ||
| 947 | v |= ckd_mul (&iticks, iticks, 1000000); | ||
| 948 | v |= ckd_add (&iticks, iticks, us); | ||
| 949 | break; | ||
| 950 | |||
| 951 | case TIMEFORM_HI_LO_US_PS: | ||
| 952 | { | ||
| 953 | int_fast64_t million = 1000000; | ||
| 954 | v |= ckd_mul (&iticks, iticks, TRILLION); | ||
| 955 | v |= ckd_add (&iticks, iticks, us * million + ps); | ||
| 956 | } | ||
| 957 | break; | ||
| 958 | |||
| 959 | default: | ||
| 960 | eassume (false); | ||
| 961 | } | ||
| 962 | |||
| 963 | if (!v) | ||
| 964 | return (struct err_time) { | ||
| 965 | .time = decode_ticks_hz (make_int (iticks), hz, cform) | ||
| 966 | }; | ||
| 967 | } | ||
| 968 | } | ||
| 969 | |||
| 970 | if (! (INTEGERP (high) && INTEGERP (low))) | ||
| 971 | return (struct err_time) { .err = EINVAL }; | ||
| 972 | |||
| 909 | mpz_t *s = &mpz[1]; | 973 | mpz_t *s = &mpz[1]; |
| 910 | mpz_set_intmax (*s, us / 1000000 - (us % 1000000 < 0)); | 974 | mpz_set_intmax (*s, s_from_us_ps); |
| 911 | mpz_add (*s, *s, *bignum_integer (&mpz[0], low)); | 975 | mpz_add (*s, *s, *bignum_integer (&mpz[0], low)); |
| 912 | mpz_addmul_ui (*s, *bignum_integer (&mpz[0], high), 1 << LO_TIME_BITS); | 976 | mpz_addmul_ui (*s, *bignum_integer (&mpz[0], high), 1 << LO_TIME_BITS); |
| 913 | ps = ps % 1000000 + 1000000 * (ps % 1000000 < 0); | ||
| 914 | us = us % 1000000 + 1000000 * (us % 1000000 < 0); | ||
| 915 | 977 | ||
| 916 | switch (form) | 978 | switch (form) |
| 917 | { | 979 | { |
| 918 | case TIMEFORM_HI_LO: | 980 | case TIMEFORM_HI_LO: |
| 919 | /* Floats and nil were handled above, so it was an integer. */ | 981 | /* Floats and nil were handled above, so it was an integer. */ |
| 920 | mpz_swap (mpz[0], *s); | 982 | mpz_swap (mpz[0], *s); |
| 921 | hz = make_fixnum (1); | ||
| 922 | break; | 983 | break; |
| 923 | 984 | ||
| 924 | case TIMEFORM_HI_LO_US: | 985 | case TIMEFORM_HI_LO_US: |
| 925 | mpz_set_ui (mpz[0], us); | 986 | mpz_set_ui (mpz[0], us); |
| 926 | mpz_addmul_ui (mpz[0], *s, 1000000); | 987 | mpz_addmul_ui (mpz[0], *s, 1000000); |
| 927 | hz = make_fixnum (1000000); | ||
| 928 | break; | 988 | break; |
| 929 | 989 | ||
| 930 | case TIMEFORM_HI_LO_US_PS: | 990 | case TIMEFORM_HI_LO_US_PS: |
| @@ -938,7 +998,6 @@ decode_time_components (enum timeform form, | |||
| 938 | mpz_set_intmax (mpz[0], i * 1000000 + ps); | 998 | mpz_set_intmax (mpz[0], i * 1000000 + ps); |
| 939 | mpz_addmul (mpz[0], *s, ztrillion); | 999 | mpz_addmul (mpz[0], *s, ztrillion); |
| 940 | #endif | 1000 | #endif |
| 941 | hz = trillion; | ||
| 942 | } | 1001 | } |
| 943 | break; | 1002 | break; |
| 944 | 1003 | ||
| @@ -948,6 +1007,9 @@ decode_time_components (enum timeform form, | |||
| 948 | ticks = make_integer_mpz (); | 1007 | ticks = make_integer_mpz (); |
| 949 | } | 1008 | } |
| 950 | break; | 1009 | break; |
| 1010 | |||
| 1011 | default: | ||
| 1012 | eassume (false); | ||
| 951 | } | 1013 | } |
| 952 | 1014 | ||
| 953 | return (struct err_time) { .time = decode_ticks_hz (ticks, hz, cform) }; | 1015 | return (struct err_time) { .time = decode_ticks_hz (ticks, hz, cform) }; |