Skip to content

Commit 1540245

Browse files
Girgiasnielsdos
andauthored
ext/standard/array.c: Optimize min/max functions for int/float (#11194)
Co-authored-by: Niels Dossche <[email protected]>
1 parent 82e761e commit 1540245

File tree

3 files changed

+218
-10
lines changed

3 files changed

+218
-10
lines changed

ext/standard/array.c

+96-10
Original file line numberDiff line numberDiff line change
@@ -1233,15 +1233,58 @@ PHP_FUNCTION(min)
12331233
}
12341234
} else {
12351235
/* mixed min ( mixed $value1 , mixed $value2 [, mixed $value3... ] ) */
1236-
zval *min, result;
1236+
zval *min;
12371237
uint32_t i;
12381238

12391239
min = &args[0];
1240+
zend_long min_lval;
1241+
double min_dval;
12401242

1241-
for (i = 1; i < argc; i++) {
1242-
is_smaller_function(&result, &args[i], min);
1243-
if (Z_TYPE(result) == IS_TRUE) {
1244-
min = &args[i];
1243+
if (Z_TYPE_P(min) == IS_LONG) {
1244+
min_lval = Z_LVAL_P(min);
1245+
1246+
for (i = 1; i < argc; i++) {
1247+
if (EXPECTED(Z_TYPE(args[i]) == IS_LONG)) {
1248+
if (min_lval > Z_LVAL(args[i])) {
1249+
min_lval = Z_LVAL(args[i]);
1250+
min = &args[i];
1251+
}
1252+
} else if (Z_TYPE(args[i]) == IS_DOUBLE && (zend_dval_to_lval((double) min_lval) == min_lval)) {
1253+
/* if min_lval can be exactly represented as a double, go to double dedicated code */
1254+
min_dval = (double) min_lval;
1255+
goto double_compare;
1256+
} else {
1257+
goto generic_compare;
1258+
}
1259+
}
1260+
1261+
RETURN_LONG(min_lval);
1262+
} else if (Z_TYPE_P(min) == IS_DOUBLE) {
1263+
min_dval = Z_DVAL_P(min);
1264+
1265+
for (i = 1; i < argc; i++) {
1266+
if (EXPECTED(Z_TYPE(args[i]) == IS_DOUBLE)) {
1267+
double_compare:
1268+
if (min_dval > Z_DVAL(args[i])) {
1269+
min_dval = Z_DVAL(args[i]);
1270+
min = &args[i];
1271+
}
1272+
} else if (Z_TYPE(args[i]) == IS_LONG && (zend_dval_to_lval((double) Z_LVAL(args[i])) == Z_LVAL(args[i]))) {
1273+
/* if the value can be exactly represented as a double, use double dedicated code otherwise generic */
1274+
if (min_dval > (double)Z_LVAL(args[i])) {
1275+
min_dval = (double)Z_LVAL(args[i]);
1276+
min = &args[i];
1277+
}
1278+
} else {
1279+
goto generic_compare;
1280+
}
1281+
}
1282+
} else {
1283+
for (i = 1; i < argc; i++) {
1284+
generic_compare:
1285+
if (zend_compare(&args[i], min) < 0) {
1286+
min = &args[i];
1287+
}
12451288
}
12461289
}
12471290

@@ -1279,15 +1322,58 @@ PHP_FUNCTION(max)
12791322
}
12801323
} else {
12811324
/* mixed max ( mixed $value1 , mixed $value2 [, mixed $value3... ] ) */
1282-
zval *max, result;
1325+
zval *max;
12831326
uint32_t i;
12841327

12851328
max = &args[0];
1329+
zend_long max_lval;
1330+
double max_dval;
12861331

1287-
for (i = 1; i < argc; i++) {
1288-
is_smaller_or_equal_function(&result, &args[i], max);
1289-
if (Z_TYPE(result) == IS_FALSE) {
1290-
max = &args[i];
1332+
if (Z_TYPE_P(max) == IS_LONG) {
1333+
max_lval = Z_LVAL_P(max);
1334+
1335+
for (i = 1; i < argc; i++) {
1336+
if (EXPECTED(Z_TYPE(args[i]) == IS_LONG)) {
1337+
if (max_lval < Z_LVAL(args[i])) {
1338+
max_lval = Z_LVAL(args[i]);
1339+
max = &args[i];
1340+
}
1341+
} else if (Z_TYPE(args[i]) == IS_DOUBLE && (zend_dval_to_lval((double) max_lval) == max_lval)) {
1342+
/* if max_lval can be exactly represented as a double, go to double dedicated code */
1343+
max_dval = (double) max_lval;
1344+
goto double_compare;
1345+
} else {
1346+
goto generic_compare;
1347+
}
1348+
}
1349+
1350+
RETURN_LONG(max_lval);
1351+
} else if (Z_TYPE_P(max) == IS_DOUBLE) {
1352+
max_dval = Z_DVAL_P(max);
1353+
1354+
for (i = 1; i < argc; i++) {
1355+
if (EXPECTED(Z_TYPE(args[i]) == IS_DOUBLE)) {
1356+
double_compare:
1357+
if (max_dval < Z_DVAL(args[i])) {
1358+
max_dval = Z_DVAL(args[i]);
1359+
max = &args[i];
1360+
}
1361+
} else if (Z_TYPE(args[i]) == IS_LONG && (zend_dval_to_lval((double) Z_LVAL(args[i])) == Z_LVAL(args[i]))) {
1362+
/* if the value can be exactly represented as a double, use double dedicated code otherwise generic */
1363+
if (max_dval < (double)Z_LVAL(args[i])) {
1364+
max_dval = (double)Z_LVAL(args[i]);
1365+
max = &args[i];
1366+
}
1367+
} else {
1368+
goto generic_compare;
1369+
}
1370+
}
1371+
} else {
1372+
for (i = 1; i < argc; i++) {
1373+
generic_compare:
1374+
if (zend_compare(&args[i], max) > 0) {
1375+
max = &args[i];
1376+
}
12911377
}
12921378
}
12931379

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
--TEST--
2+
Check max() optimisation for int and float types
3+
--SKIPIF--
4+
<?php if (PHP_INT_SIZE != 8) die("skip this test is for 64bit platform only"); ?>
5+
--FILE--
6+
<?php
7+
8+
echo "Start as int optimisation:\n";
9+
var_dump(max(10, 5, 3, 2));
10+
var_dump(max(2, 3, 5, 10));
11+
var_dump(max(10, 5, 3.5, 2));
12+
var_dump(max(2, 3.5, 5, 10));
13+
var_dump(max(10, 5, "3", 2));
14+
var_dump(max(2, "3", 5, 10));
15+
var_dump(max(2, 3, "15", 10));
16+
echo "Check that int not representable as float works:\n";
17+
var_dump(max(PHP_INT_MIN+1, PHP_INT_MIN, PHP_INT_MIN*2));
18+
var_dump(max(PHP_INT_MAX-1, PHP_INT_MAX, PHP_INT_MAX*2));
19+
// Has INF
20+
var_dump(max(PHP_INT_MAX-1, PHP_INT_MAX, PHP_INT_MAX**20));
21+
22+
echo "Start as float optimisation:\n";
23+
var_dump(max(10.5, 5.5, 3.5, 2.5));
24+
var_dump(max(2.5, 3.5, 5.5, 10.5));
25+
var_dump(max(10.5, 5.5, 3, 2.5));
26+
var_dump(max(2.5, 3, 5.5, 10.5));
27+
var_dump(max(10.5, 5.5, "3.5", 2.5));
28+
var_dump(max(2.5, "3.5", 5.5, 10.5));
29+
var_dump(max(2.5, 3.5, "15.5", 10.5));
30+
echo "Check that int not representable as float works:\n";
31+
var_dump(max(PHP_INT_MIN*2, PHP_INT_MIN, PHP_INT_MIN+1));
32+
var_dump(max(PHP_INT_MAX*2, PHP_INT_MAX, PHP_INT_MAX-1));
33+
// Has INF
34+
var_dump(max(PHP_INT_MAX**20, PHP_INT_MAX, PHP_INT_MAX-1));
35+
36+
?>
37+
--EXPECT--
38+
Start as int optimisation:
39+
int(10)
40+
int(10)
41+
int(10)
42+
int(10)
43+
int(10)
44+
int(10)
45+
string(2) "15"
46+
Check that int not representable as float works:
47+
int(-9223372036854775807)
48+
float(1.8446744073709552E+19)
49+
float(INF)
50+
Start as float optimisation:
51+
float(10.5)
52+
float(10.5)
53+
float(10.5)
54+
float(10.5)
55+
float(10.5)
56+
float(10.5)
57+
string(4) "15.5"
58+
Check that int not representable as float works:
59+
int(-9223372036854775807)
60+
float(1.8446744073709552E+19)
61+
float(INF)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
--TEST--
2+
Check min() optimisation for int and float types
3+
--SKIPIF--
4+
<?php if (PHP_INT_SIZE != 8) die("skip this test is for 64bit platform only"); ?>
5+
--FILE--
6+
<?php
7+
8+
echo "Start as int optimisation:\n";
9+
var_dump(min(10, 5, 3, 2));
10+
var_dump(min(2, 3, 5, 10));
11+
var_dump(min(10, 5, 3.5, 2));
12+
var_dump(min(2, 3.5, 5, 10));
13+
var_dump(min(10, 5, "3", 2));
14+
var_dump(min(2, "3", 5, 10));
15+
var_dump(min(2, 3, "1", 10));
16+
echo "Check that int not representable as float works:\n";
17+
var_dump(min(PHP_INT_MAX-1, PHP_INT_MAX, PHP_INT_MAX*2));
18+
var_dump(min(PHP_INT_MIN+1, PHP_INT_MIN, PHP_INT_MIN*2));
19+
// Has INF
20+
var_dump(min(PHP_INT_MAX-1, PHP_INT_MAX, PHP_INT_MAX**20));
21+
22+
echo "Start as float optimisation:\n";
23+
var_dump(min(10.5, 5.5, 3.5, 2.5));
24+
var_dump(min(2.5, 3.5, 5.5, 10.5));
25+
var_dump(min(10.5, 5.5, 3, 2.5));
26+
var_dump(min(2.5, 3, 5.5, 10.5));
27+
var_dump(min(10.5, 5.5, "3.5", 2.5));
28+
var_dump(min(2.5, "3.5", 5.5, 10.5));
29+
var_dump(min(2.5, 3.5, "1.5", 10.5));
30+
echo "Check that int not representable as float works:\n";
31+
var_dump(min(PHP_INT_MAX*2, PHP_INT_MAX, PHP_INT_MAX-1));
32+
var_dump(min(PHP_INT_MIN*2, PHP_INT_MIN, PHP_INT_MIN+1));
33+
// Has INF
34+
var_dump(min(PHP_INT_MAX**20, PHP_INT_MAX, PHP_INT_MAX-1));
35+
36+
?>
37+
--EXPECT--
38+
Start as int optimisation:
39+
int(2)
40+
int(2)
41+
int(2)
42+
int(2)
43+
int(2)
44+
int(2)
45+
string(1) "1"
46+
Check that int not representable as float works:
47+
int(9223372036854775806)
48+
float(-1.8446744073709552E+19)
49+
int(9223372036854775806)
50+
Start as float optimisation:
51+
float(2.5)
52+
float(2.5)
53+
float(2.5)
54+
float(2.5)
55+
float(2.5)
56+
float(2.5)
57+
string(3) "1.5"
58+
Check that int not representable as float works:
59+
int(9223372036854775806)
60+
float(-1.8446744073709552E+19)
61+
int(9223372036854775806)

0 commit comments

Comments
 (0)