aboutsummaryrefslogtreecommitdiffstats
path: root/src
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
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')
-rw-r--r--src/bytecode.c10
-rw-r--r--src/data.c160
-rw-r--r--src/fileio.c2
-rw-r--r--src/lisp.h23
4 files changed, 96 insertions, 99 deletions
diff --git a/src/bytecode.c b/src/bytecode.c
index 75f9f1d0ac7..ce075c86afd 100644
--- a/src/bytecode.c
+++ b/src/bytecode.c
@@ -1242,7 +1242,7 @@ exec_byte_code (Lisp_Object fun, ptrdiff_t args_template,
1242 if (FIXNUMP (v1) && FIXNUMP (v2)) 1242 if (FIXNUMP (v1) && FIXNUMP (v2))
1243 TOP = BASE_EQ (v1, v2) ? Qt : Qnil; 1243 TOP = BASE_EQ (v1, v2) ? Qt : Qnil;
1244 else 1244 else
1245 TOP = arithcompare (v1, v2, ARITH_EQUAL); 1245 TOP = arithcompare (v1, v2) & Cmp_EQ ? Qt : Qnil;
1246 NEXT; 1246 NEXT;
1247 } 1247 }
1248 1248
@@ -1253,7 +1253,7 @@ exec_byte_code (Lisp_Object fun, ptrdiff_t args_template,
1253 if (FIXNUMP (v1) && FIXNUMP (v2)) 1253 if (FIXNUMP (v1) && FIXNUMP (v2))
1254 TOP = XFIXNUM (v1) > XFIXNUM (v2) ? Qt : Qnil; 1254 TOP = XFIXNUM (v1) > XFIXNUM (v2) ? Qt : Qnil;
1255 else 1255 else
1256 TOP = arithcompare (v1, v2, ARITH_GRTR); 1256 TOP = arithcompare (v1, v2) & Cmp_GT ? Qt : Qnil;
1257 NEXT; 1257 NEXT;
1258 } 1258 }
1259 1259
@@ -1264,7 +1264,7 @@ exec_byte_code (Lisp_Object fun, ptrdiff_t args_template,
1264 if (FIXNUMP (v1) && FIXNUMP (v2)) 1264 if (FIXNUMP (v1) && FIXNUMP (v2))
1265 TOP = XFIXNUM (v1) < XFIXNUM (v2) ? Qt : Qnil; 1265 TOP = XFIXNUM (v1) < XFIXNUM (v2) ? Qt : Qnil;
1266 else 1266 else
1267 TOP = arithcompare (v1, v2, ARITH_LESS); 1267 TOP = arithcompare (v1, v2) & Cmp_LT ? Qt : Qnil;
1268 NEXT; 1268 NEXT;
1269 } 1269 }
1270 1270
@@ -1275,7 +1275,7 @@ exec_byte_code (Lisp_Object fun, ptrdiff_t args_template,
1275 if (FIXNUMP (v1) && FIXNUMP (v2)) 1275 if (FIXNUMP (v1) && FIXNUMP (v2))
1276 TOP = XFIXNUM (v1) <= XFIXNUM (v2) ? Qt : Qnil; 1276 TOP = XFIXNUM (v1) <= XFIXNUM (v2) ? Qt : Qnil;
1277 else 1277 else
1278 TOP = arithcompare (v1, v2, ARITH_LESS_OR_EQUAL); 1278 TOP = arithcompare (v1, v2) & (Cmp_LT | Cmp_EQ) ? Qt : Qnil;
1279 NEXT; 1279 NEXT;
1280 } 1280 }
1281 1281
@@ -1286,7 +1286,7 @@ exec_byte_code (Lisp_Object fun, ptrdiff_t args_template,
1286 if (FIXNUMP (v1) && FIXNUMP (v2)) 1286 if (FIXNUMP (v1) && FIXNUMP (v2))
1287 TOP = XFIXNUM (v1) >= XFIXNUM (v2) ? Qt : Qnil; 1287 TOP = XFIXNUM (v1) >= XFIXNUM (v2) ? Qt : Qnil;
1288 else 1288 else
1289 TOP = arithcompare (v1, v2, ARITH_GRTR_OR_EQUAL); 1289 TOP = arithcompare (v1, v2) & (Cmp_GT | Cmp_EQ) ? Qt : Qnil;
1290 NEXT; 1290 NEXT;
1291 } 1291 }
1292 1292
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,
diff --git a/src/fileio.c b/src/fileio.c
index fa280f2db00..22a566a1881 100644
--- a/src/fileio.c
+++ b/src/fileio.c
@@ -5741,7 +5741,7 @@ DEFUN ("car-less-than-car", Fcar_less_than_car, Scar_less_than_car, 2, 2, 0,
5741 Lisp_Object ca = Fcar (a), cb = Fcar (b); 5741 Lisp_Object ca = Fcar (a), cb = Fcar (b);
5742 if (FIXNUMP (ca) && FIXNUMP (cb)) 5742 if (FIXNUMP (ca) && FIXNUMP (cb))
5743 return XFIXNUM (ca) < XFIXNUM (cb) ? Qt : Qnil; 5743 return XFIXNUM (ca) < XFIXNUM (cb) ? Qt : Qnil;
5744 return arithcompare (ca, cb, ARITH_LESS); 5744 return arithcompare (ca, cb) & Cmp_LT ? Qt : Qnil;
5745} 5745}
5746 5746
5747/* Build the complete list of annotations appropriate for writing out 5747/* Build the complete list of annotations appropriate for writing out
diff --git a/src/lisp.h b/src/lisp.h
index 976b7a15251..8ac65ca429c 100644
--- a/src/lisp.h
+++ b/src/lisp.h
@@ -4224,16 +4224,21 @@ extern void notify_variable_watchers (Lisp_Object, Lisp_Object,
4224 Lisp_Object, Lisp_Object); 4224 Lisp_Object, Lisp_Object);
4225extern Lisp_Object indirect_function (Lisp_Object); 4225extern Lisp_Object indirect_function (Lisp_Object);
4226extern Lisp_Object find_symbol_value (Lisp_Object); 4226extern Lisp_Object find_symbol_value (Lisp_Object);
4227enum Arith_Comparison { 4227
4228 ARITH_EQUAL, 4228enum {
4229 ARITH_NOTEQUAL, 4229 Cmp_Bit_EQ,
4230 ARITH_LESS, 4230 Cmp_Bit_LT,
4231 ARITH_GRTR, 4231 Cmp_Bit_GT
4232 ARITH_LESS_OR_EQUAL,
4233 ARITH_GRTR_OR_EQUAL
4234}; 4232};
4235extern Lisp_Object arithcompare (Lisp_Object num1, Lisp_Object num2, 4233
4236 enum Arith_Comparison comparison); 4234/* code indicating a comparison outcome */
4235typedef enum {
4236 Cmp_EQ = 1 << Cmp_Bit_EQ, /* = */
4237 Cmp_LT = 1 << Cmp_Bit_LT, /* < */
4238 Cmp_GT = 1 << Cmp_Bit_GT /* > */
4239} cmp_bits_t;
4240
4241extern cmp_bits_t arithcompare (Lisp_Object num1, Lisp_Object num2);
4237 4242
4238/* Convert the Emacs representation CONS back to an integer of type 4243/* Convert the Emacs representation CONS back to an integer of type
4239 TYPE, storing the result the variable VAR. Signal an error if CONS 4244 TYPE, storing the result the variable VAR. Signal an error if CONS