diff options
| author | Mattias EngdegÄrd | 2024-07-20 13:12:19 +0200 |
|---|---|---|
| committer | Mattias EngdegÄrd | 2024-07-27 12:04:09 +0200 |
| commit | 2fd38e5c496a2351a25e95df37a7900f6f80f22f (patch) | |
| tree | 8b4bb30916da084175a880e285ed8656e0641b70 /src/data.c | |
| parent | 156a3ba4f9ef9f7a401cfd3ca118152169f0ddcf (diff) | |
| download | emacs-2fd38e5c496a2351a25e95df37a7900f6f80f22f.tar.gz emacs-2fd38e5c496a2351a25e95df37a7900f6f80f22f.zip | |
Simplify and speed up numeric comparisons
This makes comparison functions (=, /=, <, <=, >, >=, min, max) quite
a bit faster (10-20 %). Bytecode ops on fixnums are not affected,
nor is `value<`.
* src/data.c (arithcompare): Simplify the code to reduce the number of
branches. Remove the comparison code argument; instead, return the
relation encoded as bits, which can be tested cheaply. All callers
adapted.
* src/lisp.h (enum Arith_Comparison): Remove.
(Cmp_Bit_*, cmp_bits_t): New.
Diffstat (limited to 'src/data.c')
| -rw-r--r-- | src/data.c | 160 |
1 files changed, 76 insertions, 84 deletions
diff --git a/src/data.c b/src/data.c index 752856abf09..b4d08a3ff06 100644 --- a/src/data.c +++ b/src/data.c | |||
| @@ -2682,26 +2682,13 @@ check_number_coerce_marker (Lisp_Object x) | |||
| 2682 | return x; | 2682 | return x; |
| 2683 | } | 2683 | } |
| 2684 | 2684 | ||
| 2685 | Lisp_Object | 2685 | cmp_bits_t |
| 2686 | arithcompare (Lisp_Object num1, Lisp_Object num2, | 2686 | arithcompare (Lisp_Object num1, Lisp_Object num2) |
| 2687 | enum Arith_Comparison comparison) | ||
| 2688 | { | 2687 | { |
| 2689 | EMACS_INT i1 = 0, i2 = 0; | ||
| 2690 | bool lt, eq = true, gt; | ||
| 2691 | bool test; | ||
| 2692 | |||
| 2693 | num1 = check_number_coerce_marker (num1); | 2688 | num1 = check_number_coerce_marker (num1); |
| 2694 | num2 = check_number_coerce_marker (num2); | 2689 | num2 = check_number_coerce_marker (num2); |
| 2695 | 2690 | ||
| 2696 | /* If the comparison is mostly done by comparing two doubles, | 2691 | bool lt, eq, gt; |
| 2697 | set LT, EQ, and GT to the <, ==, > results of that comparison, | ||
| 2698 | respectively, taking care to avoid problems if either is a NaN, | ||
| 2699 | and trying to avoid problems on platforms where variables (in | ||
| 2700 | violation of the C standard) can contain excess precision. | ||
| 2701 | Regardless, set I1 and I2 to integers that break ties if the | ||
| 2702 | two-double comparison is either not done or reports | ||
| 2703 | equality. */ | ||
| 2704 | |||
| 2705 | if (FLOATP (num1)) | 2692 | if (FLOATP (num1)) |
| 2706 | { | 2693 | { |
| 2707 | double f1 = XFLOAT_DATA (num1); | 2694 | double f1 = XFLOAT_DATA (num1); |
| @@ -2723,16 +2710,30 @@ arithcompare (Lisp_Object num1, Lisp_Object num2, | |||
| 2723 | (exactly) so I1 - I2 = NUM1 - NUM2 (exactly), so comparing I1 | 2710 | (exactly) so I1 - I2 = NUM1 - NUM2 (exactly), so comparing I1 |
| 2724 | to I2 will break the tie correctly. */ | 2711 | to I2 will break the tie correctly. */ |
| 2725 | double f2 = XFIXNUM (num2); | 2712 | double f2 = XFIXNUM (num2); |
| 2726 | lt = f1 < f2; | 2713 | if (f1 == f2) |
| 2727 | eq = f1 == f2; | 2714 | { |
| 2728 | gt = f1 > f2; | 2715 | EMACS_INT i1 = f2; |
| 2729 | i1 = f2; | 2716 | EMACS_INT i2 = XFIXNUM (num2); |
| 2730 | i2 = XFIXNUM (num2); | 2717 | eq = i1 == i2; |
| 2718 | lt = i1 < i2; | ||
| 2719 | gt = i1 > i2; | ||
| 2720 | } | ||
| 2721 | else | ||
| 2722 | { | ||
| 2723 | eq = false; | ||
| 2724 | lt = f1 < f2; | ||
| 2725 | gt = f1 > f2; | ||
| 2726 | } | ||
| 2731 | } | 2727 | } |
| 2732 | else if (isnan (f1)) | 2728 | else if (isnan (f1)) |
| 2733 | lt = eq = gt = false; | 2729 | lt = eq = gt = false; |
| 2734 | else | 2730 | else |
| 2735 | i2 = mpz_cmp_d (*xbignum_val (num2), f1); | 2731 | { |
| 2732 | int cmp = mpz_cmp_d (*xbignum_val (num2), f1); | ||
| 2733 | eq = cmp == 0; | ||
| 2734 | lt = cmp > 0; | ||
| 2735 | gt = cmp < 0; | ||
| 2736 | } | ||
| 2736 | } | 2737 | } |
| 2737 | else if (FIXNUMP (num1)) | 2738 | else if (FIXNUMP (num1)) |
| 2738 | { | 2739 | { |
| @@ -2741,19 +2742,36 @@ arithcompare (Lisp_Object num1, Lisp_Object num2, | |||
| 2741 | /* Compare an integer NUM1 to a float NUM2. This is the | 2742 | /* Compare an integer NUM1 to a float NUM2. This is the |
| 2742 | converse of comparing float to integer (see above). */ | 2743 | converse of comparing float to integer (see above). */ |
| 2743 | double f1 = XFIXNUM (num1), f2 = XFLOAT_DATA (num2); | 2744 | double f1 = XFIXNUM (num1), f2 = XFLOAT_DATA (num2); |
| 2744 | lt = f1 < f2; | 2745 | if (f1 == f2) |
| 2745 | eq = f1 == f2; | 2746 | { |
| 2746 | gt = f1 > f2; | 2747 | EMACS_INT i1 = XFIXNUM (num1); |
| 2747 | i1 = XFIXNUM (num1); | 2748 | EMACS_INT i2 = f1; |
| 2748 | i2 = f1; | 2749 | eq = i1 == i2; |
| 2750 | lt = i1 < i2; | ||
| 2751 | gt = i1 > i2; | ||
| 2752 | } | ||
| 2753 | else | ||
| 2754 | { | ||
| 2755 | eq = false; | ||
| 2756 | lt = f1 < f2; | ||
| 2757 | gt = f1 > f2; | ||
| 2758 | } | ||
| 2749 | } | 2759 | } |
| 2750 | else if (FIXNUMP (num2)) | 2760 | else if (FIXNUMP (num2)) |
| 2751 | { | 2761 | { |
| 2752 | i1 = XFIXNUM (num1); | 2762 | EMACS_INT i1 = XFIXNUM (num1); |
| 2753 | i2 = XFIXNUM (num2); | 2763 | EMACS_INT i2 = XFIXNUM (num2); |
| 2764 | eq = i1 == i2; | ||
| 2765 | lt = i1 < i2; | ||
| 2766 | gt = i1 > i2; | ||
| 2754 | } | 2767 | } |
| 2755 | else | 2768 | else |
| 2756 | i2 = mpz_sgn (*xbignum_val (num2)); | 2769 | { |
| 2770 | int sgn = mpz_sgn (*xbignum_val (num2)); | ||
| 2771 | eq = sgn == 0; | ||
| 2772 | lt = sgn > 0; | ||
| 2773 | gt = sgn < 0; | ||
| 2774 | } | ||
| 2757 | } | 2775 | } |
| 2758 | else if (FLOATP (num2)) | 2776 | else if (FLOATP (num2)) |
| 2759 | { | 2777 | { |
| @@ -2761,61 +2779,36 @@ arithcompare (Lisp_Object num1, Lisp_Object num2, | |||
| 2761 | if (isnan (f2)) | 2779 | if (isnan (f2)) |
| 2762 | lt = eq = gt = false; | 2780 | lt = eq = gt = false; |
| 2763 | else | 2781 | else |
| 2764 | i1 = mpz_cmp_d (*xbignum_val (num1), f2); | 2782 | { |
| 2783 | int cmp = mpz_cmp_d (*xbignum_val (num1), f2); | ||
| 2784 | eq = cmp == 0; | ||
| 2785 | lt = cmp < 0; | ||
| 2786 | gt = cmp > 0; | ||
| 2787 | } | ||
| 2765 | } | 2788 | } |
| 2766 | else if (FIXNUMP (num2)) | 2789 | else if (FIXNUMP (num2)) |
| 2767 | i1 = mpz_sgn (*xbignum_val (num1)); | ||
| 2768 | else | ||
| 2769 | i1 = mpz_cmp (*xbignum_val (num1), *xbignum_val (num2)); | ||
| 2770 | |||
| 2771 | if (eq) | ||
| 2772 | { | 2790 | { |
| 2773 | /* The two-double comparison either reported equality, or was not done. | 2791 | int sgn = mpz_sgn (*xbignum_val (num1)); |
| 2774 | Break the tie by comparing the integers. */ | 2792 | eq = sgn == 0; |
| 2775 | lt = i1 < i2; | 2793 | lt = sgn < 0; |
| 2776 | eq = i1 == i2; | 2794 | gt = sgn > 0; |
| 2777 | gt = i1 > i2; | ||
| 2778 | } | 2795 | } |
| 2779 | 2796 | else | |
| 2780 | switch (comparison) | ||
| 2781 | { | 2797 | { |
| 2782 | case ARITH_EQUAL: | 2798 | int cmp = mpz_cmp (*xbignum_val (num1), *xbignum_val (num2)); |
| 2783 | test = eq; | 2799 | eq = cmp == 0; |
| 2784 | break; | 2800 | lt = cmp < 0; |
| 2785 | 2801 | gt = cmp > 0; | |
| 2786 | case ARITH_NOTEQUAL: | ||
| 2787 | test = !eq; | ||
| 2788 | break; | ||
| 2789 | |||
| 2790 | case ARITH_LESS: | ||
| 2791 | test = lt; | ||
| 2792 | break; | ||
| 2793 | |||
| 2794 | case ARITH_LESS_OR_EQUAL: | ||
| 2795 | test = lt | eq; | ||
| 2796 | break; | ||
| 2797 | |||
| 2798 | case ARITH_GRTR: | ||
| 2799 | test = gt; | ||
| 2800 | break; | ||
| 2801 | |||
| 2802 | case ARITH_GRTR_OR_EQUAL: | ||
| 2803 | test = gt | eq; | ||
| 2804 | break; | ||
| 2805 | |||
| 2806 | default: | ||
| 2807 | eassume (false); | ||
| 2808 | } | 2802 | } |
| 2809 | 2803 | ||
| 2810 | return test ? Qt : Qnil; | 2804 | return lt << Cmp_Bit_LT | gt << Cmp_Bit_GT | eq << Cmp_Bit_EQ; |
| 2811 | } | 2805 | } |
| 2812 | 2806 | ||
| 2813 | static Lisp_Object | 2807 | static Lisp_Object |
| 2814 | arithcompare_driver (ptrdiff_t nargs, Lisp_Object *args, | 2808 | arithcompare_driver (ptrdiff_t nargs, Lisp_Object *args, cmp_bits_t cmpmask) |
| 2815 | enum Arith_Comparison comparison) | ||
| 2816 | { | 2809 | { |
| 2817 | for (ptrdiff_t i = 1; i < nargs; i++) | 2810 | for (ptrdiff_t i = 1; i < nargs; i++) |
| 2818 | if (NILP (arithcompare (args[i - 1], args[i], comparison))) | 2811 | if (!(arithcompare (args[i - 1], args[i]) & cmpmask)) |
| 2819 | return Qnil; | 2812 | return Qnil; |
| 2820 | return Qt; | 2813 | return Qt; |
| 2821 | } | 2814 | } |
| @@ -2825,7 +2818,7 @@ DEFUN ("=", Feqlsign, Seqlsign, 1, MANY, 0, | |||
| 2825 | usage: (= NUMBER-OR-MARKER &rest NUMBERS-OR-MARKERS) */) | 2818 | usage: (= NUMBER-OR-MARKER &rest NUMBERS-OR-MARKERS) */) |
| 2826 | (ptrdiff_t nargs, Lisp_Object *args) | 2819 | (ptrdiff_t nargs, Lisp_Object *args) |
| 2827 | { | 2820 | { |
| 2828 | return arithcompare_driver (nargs, args, ARITH_EQUAL); | 2821 | return arithcompare_driver (nargs, args, Cmp_EQ); |
| 2829 | } | 2822 | } |
| 2830 | 2823 | ||
| 2831 | DEFUN ("<", Flss, Slss, 1, MANY, 0, | 2824 | DEFUN ("<", Flss, Slss, 1, MANY, 0, |
| @@ -2836,7 +2829,7 @@ usage: (< NUMBER-OR-MARKER &rest NUMBERS-OR-MARKERS) */) | |||
| 2836 | if (nargs == 2 && FIXNUMP (args[0]) && FIXNUMP (args[1])) | 2829 | if (nargs == 2 && FIXNUMP (args[0]) && FIXNUMP (args[1])) |
| 2837 | return XFIXNUM (args[0]) < XFIXNUM (args[1]) ? Qt : Qnil; | 2830 | return XFIXNUM (args[0]) < XFIXNUM (args[1]) ? Qt : Qnil; |
| 2838 | 2831 | ||
| 2839 | return arithcompare_driver (nargs, args, ARITH_LESS); | 2832 | return arithcompare_driver (nargs, args, Cmp_LT); |
| 2840 | } | 2833 | } |
| 2841 | 2834 | ||
| 2842 | DEFUN (">", Fgtr, Sgtr, 1, MANY, 0, | 2835 | DEFUN (">", Fgtr, Sgtr, 1, MANY, 0, |
| @@ -2847,7 +2840,7 @@ usage: (> NUMBER-OR-MARKER &rest NUMBERS-OR-MARKERS) */) | |||
| 2847 | if (nargs == 2 && FIXNUMP (args[0]) && FIXNUMP (args[1])) | 2840 | if (nargs == 2 && FIXNUMP (args[0]) && FIXNUMP (args[1])) |
| 2848 | return XFIXNUM (args[0]) > XFIXNUM (args[1]) ? Qt : Qnil; | 2841 | return XFIXNUM (args[0]) > XFIXNUM (args[1]) ? Qt : Qnil; |
| 2849 | 2842 | ||
| 2850 | return arithcompare_driver (nargs, args, ARITH_GRTR); | 2843 | return arithcompare_driver (nargs, args, Cmp_GT); |
| 2851 | } | 2844 | } |
| 2852 | 2845 | ||
| 2853 | DEFUN ("<=", Fleq, Sleq, 1, MANY, 0, | 2846 | DEFUN ("<=", Fleq, Sleq, 1, MANY, 0, |
| @@ -2858,7 +2851,7 @@ usage: (<= NUMBER-OR-MARKER &rest NUMBERS-OR-MARKERS) */) | |||
| 2858 | if (nargs == 2 && FIXNUMP (args[0]) && FIXNUMP (args[1])) | 2851 | if (nargs == 2 && FIXNUMP (args[0]) && FIXNUMP (args[1])) |
| 2859 | return XFIXNUM (args[0]) <= XFIXNUM (args[1]) ? Qt : Qnil; | 2852 | return XFIXNUM (args[0]) <= XFIXNUM (args[1]) ? Qt : Qnil; |
| 2860 | 2853 | ||
| 2861 | return arithcompare_driver (nargs, args, ARITH_LESS_OR_EQUAL); | 2854 | return arithcompare_driver (nargs, args, Cmp_LT | Cmp_EQ); |
| 2862 | } | 2855 | } |
| 2863 | 2856 | ||
| 2864 | DEFUN (">=", Fgeq, Sgeq, 1, MANY, 0, | 2857 | DEFUN (">=", Fgeq, Sgeq, 1, MANY, 0, |
| @@ -2869,14 +2862,14 @@ usage: (>= NUMBER-OR-MARKER &rest NUMBERS-OR-MARKERS) */) | |||
| 2869 | if (nargs == 2 && FIXNUMP (args[0]) && FIXNUMP (args[1])) | 2862 | if (nargs == 2 && FIXNUMP (args[0]) && FIXNUMP (args[1])) |
| 2870 | return XFIXNUM (args[0]) >= XFIXNUM (args[1]) ? Qt : Qnil; | 2863 | return XFIXNUM (args[0]) >= XFIXNUM (args[1]) ? Qt : Qnil; |
| 2871 | 2864 | ||
| 2872 | return arithcompare_driver (nargs, args, ARITH_GRTR_OR_EQUAL); | 2865 | return arithcompare_driver (nargs, args, Cmp_GT | Cmp_EQ); |
| 2873 | } | 2866 | } |
| 2874 | 2867 | ||
| 2875 | DEFUN ("/=", Fneq, Sneq, 2, 2, 0, | 2868 | DEFUN ("/=", Fneq, Sneq, 2, 2, 0, |
| 2876 | doc: /* Return t if first arg is not equal to second arg. Both must be numbers or markers. */) | 2869 | doc: /* Return t if first arg is not equal to second arg. Both must be numbers or markers. */) |
| 2877 | (register Lisp_Object num1, Lisp_Object num2) | 2870 | (register Lisp_Object num1, Lisp_Object num2) |
| 2878 | { | 2871 | { |
| 2879 | return arithcompare (num1, num2, ARITH_NOTEQUAL); | 2872 | return arithcompare (num1, num2) & Cmp_EQ ? Qnil : Qt; |
| 2880 | } | 2873 | } |
| 2881 | 2874 | ||
| 2882 | /* Convert the cons-of-integers, integer, or float value C to an | 2875 | /* Convert the cons-of-integers, integer, or float value C to an |
| @@ -3418,14 +3411,13 @@ Both X and Y must be numbers or markers. */) | |||
| 3418 | } | 3411 | } |
| 3419 | 3412 | ||
| 3420 | static Lisp_Object | 3413 | static Lisp_Object |
| 3421 | minmax_driver (ptrdiff_t nargs, Lisp_Object *args, | 3414 | minmax_driver (ptrdiff_t nargs, Lisp_Object *args, cmp_bits_t cmpmask) |
| 3422 | enum Arith_Comparison comparison) | ||
| 3423 | { | 3415 | { |
| 3424 | Lisp_Object accum = check_number_coerce_marker (args[0]); | 3416 | Lisp_Object accum = check_number_coerce_marker (args[0]); |
| 3425 | for (ptrdiff_t argnum = 1; argnum < nargs; argnum++) | 3417 | for (ptrdiff_t argnum = 1; argnum < nargs; argnum++) |
| 3426 | { | 3418 | { |
| 3427 | Lisp_Object val = check_number_coerce_marker (args[argnum]); | 3419 | Lisp_Object val = check_number_coerce_marker (args[argnum]); |
| 3428 | if (!NILP (arithcompare (val, accum, comparison))) | 3420 | if (arithcompare (val, accum) & cmpmask) |
| 3429 | accum = val; | 3421 | accum = val; |
| 3430 | else if (FLOATP (val) && isnan (XFLOAT_DATA (val))) | 3422 | else if (FLOATP (val) && isnan (XFLOAT_DATA (val))) |
| 3431 | return val; | 3423 | return val; |
| @@ -3439,7 +3431,7 @@ The value is always a number; markers are converted to numbers. | |||
| 3439 | usage: (max NUMBER-OR-MARKER &rest NUMBERS-OR-MARKERS) */) | 3431 | usage: (max NUMBER-OR-MARKER &rest NUMBERS-OR-MARKERS) */) |
| 3440 | (ptrdiff_t nargs, Lisp_Object *args) | 3432 | (ptrdiff_t nargs, Lisp_Object *args) |
| 3441 | { | 3433 | { |
| 3442 | return minmax_driver (nargs, args, ARITH_GRTR); | 3434 | return minmax_driver (nargs, args, Cmp_GT); |
| 3443 | } | 3435 | } |
| 3444 | 3436 | ||
| 3445 | DEFUN ("min", Fmin, Smin, 1, MANY, 0, | 3437 | DEFUN ("min", Fmin, Smin, 1, MANY, 0, |
| @@ -3448,7 +3440,7 @@ The value is always a number; markers are converted to numbers. | |||
| 3448 | usage: (min NUMBER-OR-MARKER &rest NUMBERS-OR-MARKERS) */) | 3440 | usage: (min NUMBER-OR-MARKER &rest NUMBERS-OR-MARKERS) */) |
| 3449 | (ptrdiff_t nargs, Lisp_Object *args) | 3441 | (ptrdiff_t nargs, Lisp_Object *args) |
| 3450 | { | 3442 | { |
| 3451 | return minmax_driver (nargs, args, ARITH_LESS); | 3443 | return minmax_driver (nargs, args, Cmp_LT); |
| 3452 | } | 3444 | } |
| 3453 | 3445 | ||
| 3454 | DEFUN ("logand", Flogand, Slogand, 0, MANY, 0, | 3446 | DEFUN ("logand", Flogand, Slogand, 0, MANY, 0, |