Skip to content

Commit a0476fd

Browse files
authored
Micro-optimize double comparison (#11061)
When using ZEND_NORMALIZE_BOOL(a - b) where a and b are doubles, this generates the following instruction sequence on x64: subsd xmm0, xmm1 pxor xmm1, xmm1 comisd xmm0, xmm1 ... whereas if we use ZEND_THREEWAY_COMPARE we get two instructions less: ucomisd xmm0, xmm1 The only difference is that the threeway compare uses *u*comisd instead of comisd. The difference is that it will cause a FP signal if a signaling NAN is used, but as far as I'm aware this doesn't matter for our use case. Similarly, the amount of instructions on AArch64 is also quite a bit lower for this code compared to the old code. ** Results ** Using the benchmark https://gist.github.com/nielsdos/b36517d81a1af74d96baa3576c2b70df I used hyperfine: hyperfine --runs 25 --warmup 3 './sapi/cli/php sort_double.php' No extensions such as opcache used during benchmarking. BEFORE THIS PATCH ----------------- Time (mean ± σ): 255.5 ms ± 2.2 ms [User: 251.0 ms, System: 2.5 ms] Range (min … max): 251.5 ms … 260.7 ms 25 runs AFTER THIS PATCH ---------------- Time (mean ± σ): 236.2 ms ± 2.8 ms [User: 228.9 ms, System: 5.0 ms] Range (min … max): 231.5 ms … 242.7 ms 25 runs
1 parent 8d5e06d commit a0476fd

File tree

3 files changed

+9
-18
lines changed

3 files changed

+9
-18
lines changed

Diff for: Zend/zend_operators.c

+7-16
Original file line numberDiff line numberDiff line change
@@ -2109,7 +2109,7 @@ ZEND_API int ZEND_FASTCALL numeric_compare_function(zval *op1, zval *op2) /* {{{
21092109
d1 = zval_get_double(op1);
21102110
d2 = zval_get_double(op2);
21112111

2112-
return ZEND_NORMALIZE_BOOL(d1 - d2);
2112+
return ZEND_THREEWAY_COMPARE(d1, d2);
21132113
}
21142114
/* }}} */
21152115

@@ -2131,8 +2131,7 @@ static int compare_long_to_string(zend_long lval, zend_string *str) /* {{{ */
21312131
}
21322132

21332133
if (type == IS_DOUBLE) {
2134-
double diff = (double) lval - str_dval;
2135-
return ZEND_NORMALIZE_BOOL(diff);
2134+
return ZEND_THREEWAY_COMPARE((double) lval, str_dval);
21362135
}
21372136

21382137
zend_string *lval_as_str = zend_long_to_str(lval);
@@ -2150,15 +2149,11 @@ static int compare_double_to_string(double dval, zend_string *str) /* {{{ */
21502149
uint8_t type = is_numeric_string(ZSTR_VAL(str), ZSTR_LEN(str), &str_lval, &str_dval, 0);
21512150

21522151
if (type == IS_LONG) {
2153-
double diff = dval - (double) str_lval;
2154-
return ZEND_NORMALIZE_BOOL(diff);
2152+
return ZEND_THREEWAY_COMPARE(dval, (double) str_lval);
21552153
}
21562154

21572155
if (type == IS_DOUBLE) {
2158-
if (dval == str_dval) {
2159-
return 0;
2160-
}
2161-
return ZEND_NORMALIZE_BOOL(dval - str_dval);
2156+
return ZEND_THREEWAY_COMPARE(dval, str_dval);
21622157
}
21632158

21642159
zend_string *dval_as_str = zend_double_to_str(dval);
@@ -2180,17 +2175,13 @@ ZEND_API int ZEND_FASTCALL zend_compare(zval *op1, zval *op2) /* {{{ */
21802175
return Z_LVAL_P(op1)>Z_LVAL_P(op2)?1:(Z_LVAL_P(op1)<Z_LVAL_P(op2)?-1:0);
21812176

21822177
case TYPE_PAIR(IS_DOUBLE, IS_LONG):
2183-
return ZEND_NORMALIZE_BOOL(Z_DVAL_P(op1) - (double)Z_LVAL_P(op2));
2178+
return ZEND_THREEWAY_COMPARE(Z_DVAL_P(op1), (double)Z_LVAL_P(op2));
21842179

21852180
case TYPE_PAIR(IS_LONG, IS_DOUBLE):
2186-
return ZEND_NORMALIZE_BOOL((double)Z_LVAL_P(op1) - Z_DVAL_P(op2));
2181+
return ZEND_THREEWAY_COMPARE((double)Z_LVAL_P(op1), Z_DVAL_P(op2));
21872182

21882183
case TYPE_PAIR(IS_DOUBLE, IS_DOUBLE):
2189-
if (Z_DVAL_P(op1) == Z_DVAL_P(op2)) {
2190-
return 0;
2191-
} else {
2192-
return ZEND_NORMALIZE_BOOL(Z_DVAL_P(op1) - Z_DVAL_P(op2));
2193-
}
2184+
return ZEND_THREEWAY_COMPARE(Z_DVAL_P(op1), Z_DVAL_P(op2));
21942185

21952186
case TYPE_PAIR(IS_ARRAY, IS_ARRAY):
21962187
return zend_compare_arrays(op1, op2);

Diff for: ext/spl/spl_heap.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -247,7 +247,7 @@ static int spl_ptr_pqueue_elem_cmp_long(void *x, void *y, zval *object) {
247247
static int spl_ptr_pqueue_elem_cmp_double(void *x, void *y, zval *object) {
248248
double a = Z_DVAL(((spl_pqueue_elem*) x)->priority);
249249
double b = Z_DVAL(((spl_pqueue_elem*) y)->priority);
250-
return ZEND_NORMALIZE_BOOL(a - b);
250+
return ZEND_THREEWAY_COMPARE(a, b);
251251
}
252252

253253
static spl_ptr_heap *spl_ptr_heap_init(spl_ptr_heap_cmp_func cmp, spl_ptr_heap_ctor_func ctor, spl_ptr_heap_dtor_func dtor, size_t elem_size) /* {{{ */

Diff for: ext/standard/array.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ static zend_always_inline int php_array_key_compare_numeric_unstable_i(Bucket *f
164164
} else {
165165
d2 = (double)(zend_long)s->h;
166166
}
167-
return ZEND_NORMALIZE_BOOL(d1 - d2);
167+
return ZEND_THREEWAY_COMPARE(d1, d2);
168168
}
169169
}
170170
/* }}} */

0 commit comments

Comments
 (0)