aboutsummaryrefslogtreecommitdiffstats
path: root/src/data.c
diff options
context:
space:
mode:
authorMattias EngdegÄrd2024-07-20 13:12:19 +0200
committerMattias EngdegÄrd2024-07-27 12:04:09 +0200
commit2fd38e5c496a2351a25e95df37a7900f6f80f22f (patch)
tree8b4bb30916da084175a880e285ed8656e0641b70 /src/data.c
parent156a3ba4f9ef9f7a401cfd3ca118152169f0ddcf (diff)
downloademacs-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.c160
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
2685Lisp_Object 2685cmp_bits_t
2686arithcompare (Lisp_Object num1, Lisp_Object num2, 2686arithcompare (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
2813static Lisp_Object 2807static Lisp_Object
2814arithcompare_driver (ptrdiff_t nargs, Lisp_Object *args, 2808arithcompare_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,
2825usage: (= NUMBER-OR-MARKER &rest NUMBERS-OR-MARKERS) */) 2818usage: (= 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
2831DEFUN ("<", Flss, Slss, 1, MANY, 0, 2824DEFUN ("<", 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
2842DEFUN (">", Fgtr, Sgtr, 1, MANY, 0, 2835DEFUN (">", 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
2853DEFUN ("<=", Fleq, Sleq, 1, MANY, 0, 2846DEFUN ("<=", 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
2864DEFUN (">=", Fgeq, Sgeq, 1, MANY, 0, 2857DEFUN (">=", 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
2875DEFUN ("/=", Fneq, Sneq, 2, 2, 0, 2868DEFUN ("/=", 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
3420static Lisp_Object 3413static Lisp_Object
3421minmax_driver (ptrdiff_t nargs, Lisp_Object *args, 3414minmax_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.
3439usage: (max NUMBER-OR-MARKER &rest NUMBERS-OR-MARKERS) */) 3431usage: (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
3445DEFUN ("min", Fmin, Smin, 1, MANY, 0, 3437DEFUN ("min", Fmin, Smin, 1, MANY, 0,
@@ -3448,7 +3440,7 @@ The value is always a number; markers are converted to numbers.
3448usage: (min NUMBER-OR-MARKER &rest NUMBERS-OR-MARKERS) */) 3440usage: (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
3454DEFUN ("logand", Flogand, Slogand, 0, MANY, 0, 3446DEFUN ("logand", Flogand, Slogand, 0, MANY, 0,