Bug #15834
closedt3lib_div::trimExplode() is heavily called and can be optimized => nearly 1% improvement in fronEnd (both cached and uncached)
0%
Description
/!\ PHP >= 4.0.6
Using xDebug Profiler, I have noticed nearly 1% improvement in fronEnd (both cached or uncached) !
It seems like a 1% "almost free" optimization...
function trimExplode($delim, $string, $onlyNonEmptyValues=0) {
if ($onlyNonEmptyValues) {
return array_diff(array_map('trim', explode($delim, $string)), array(''));
} else {
return array_map('trim', explode($delim, $string));
}
}
"Unit" TEST :
function trimExplodeNEW($delim, $string, $onlyNonEmptyValues=0) {
if ($onlyNonEmptyValues) {
return array_diff(array_map('trim', explode($delim, $string)), array(''));
} else {
return array_map('trim', explode($delim, $string));
}
}
function trimExplodeOLD($delim, $string, $onlyNonEmptyValues=0) {
$temp = explode($delim,$string);
$newtemp=array();
while(list($key,$val)=each($temp)) {
if (!$onlyNonEmptyValues || strcmp('',trim($val))) {
$newtemp[]=trim($val);
}
}
reset($newtemp);
return $newtemp;
}
function array_strict_compare($res1, $res2) {
$res1 = implode(',', $res1);
$res2 = implode(',', $res2);
if ($res1 !== $res2) return false;
return true;
}
$tests = array(
', a, 1 ,c ,dddd , ,',
", 0, , \n , , aaa , 00,",
'1,2,3,4,5.6,7,8,9,10,11',
implode(',', array_fill(0, 1000, 'test')),
implode(' , ', array_fill(0, 100, 'test')),
' ,, ,,'.implode(' , ', array_fill(0, 100, 'test')).',, ,, ',
' ,, ,,'.implode(' , , ', array_fill(0, 100, 'test')).',, ,, ',
implode(',', range(0, 1000)),
implode(' , ', range(0, 1000)),
);
print "
\n"; foreach ($tests as $test) { foreach (array(true, false) as $onlyNonEmptyValues) { $res1 = trimExplodeOLD(',', $test, $onlyNonEmptyValues); $res2 = trimExplodeNEW(',', $test, $onlyNonEmptyValues); if (! array_strict_compare($res1, $res2)) { print '$test = '.$test."\n"; print '$res1 = '.implode(',', $res1)."\n"; print '$res2 = '.implode(',', $res2)."\n"; print "===> KO\n\n"; } else { //print '$test = '.$test."\n"; //print '$res1 = '.implode(',', $res1)."\n"; //print '$res2 = '.implode(',', $res2)."\n"; print "===> OK\n\n"; } } } print "\n";
?>
"Live TEST" (in class.t3lib_div.php):
/**
* Explodes a string and trims all values for whitespace in the ends.
* If $onlyNonEmptyValues is set, then all blank ('') values are removed.
* Usage: 256
*
* @param string Delimiter string to explode with
* @param string The string to explode
* @param boolean If set, all empty values (='') will NOT be set in output
* @return array Exploded values
*/
function trimExplode($delim, $string, $onlyNonEmptyValues=0) {
$res1 = t3lib_div::trimExplodeOLD($delim, $string, $onlyNonEmptyValues);
$res2 = t3lib_div::trimExplodeNEW($delim, $string, $onlyNonEmptyValues);
$res1t = implode(',', $res1);
$res2t = implode(',', $res2);
if ($res1t !== $res2t) {
t3lib_div::debug($res1, 'trimExplodeNEW() error $res1');
t3lib_div::debug($res2, 'trimExplodeNEW() error $res2');
return $res1;
}
return $res2;
}
function trimExplodeNEW($delim, $string, $onlyNonEmptyValues=0) {
if ($onlyNonEmptyValues) {
return array_diff(array_map('trim', explode($delim, $string)), array(''));
} else {
return array_map('trim', explode($delim, $string));
}
}
function trimExplodeOLD($delim, $string, $onlyNonEmptyValues=0) {
$temp = explode($delim,$string);
$newtemp=array();
while(list($key,$val)=each($temp)) {
if (!$onlyNonEmptyValues || strcmp('',trim($val))) {
$newtemp[]=trim($val);
}
}
reset($newtemp);
return $newtemp;
}
(issue imported from #M2883)
Updated by Martin Kutschker over 18 years ago
Wolfgang, your our optimization hero. What do you think of this?
Updated by Wolfgang Klinger over 18 years ago
Here are some numbers:
----------------- Result *old*: 39.97 ----------------- Result *new*: 28.69 -----------------
Updated by Guillaume Crico over 18 years ago
Excuse me Wolfgang, but what do these numbers explain ?
Is there any "benchmarking protocol" for Typo3 ?
Running a (quite minimalist) page in typo3 4.0, I used xDebug profiling traces in winCacheGrind to find most-used/most-time-consuming methods =>
OLD trimExplode: total Cumulative Time = 1.68%
NEW trimExplode: total Cumulative Time = 0.24%
Updated by Kasper Skårhøj over 18 years ago
I didn't look at the code but it must be completely certain that indexes in the array are numerical from 0 and forward plus that removed values are string compared - not just =="" or so.
Unless this is absolutely sure that it is 110% compatible its a -1. I take no chances with that function.
Updated by Guillaume Crico over 18 years ago
Sorry, the code was only "10% compatible"...
Keys didn't start from zero, if NonEmptyValues=1 and an EmptyValues was present at the beginning of the list.
(Anybody wants to write a unit test for t3lib_div, ... during his next holidays ? ;-)
THE CODE SHOULD BE (Tested with PHP 4.4.2 and PHP 5.1.2) :
function trimExplode($delim, $string, $onlyNonEmptyValues=0) {
if ($onlyNonEmptyValues) {
return array_merge(array_diff(array_map('trim', explode($delim, $string)), array('')));
} else {
return array_map('trim', explode($delim, $string));
}
}
And here is a better test (uses var_export() to compare arrays) :
error_reporting(E_ALL);
class t3lib_div {
/**
* Explodes a string and trims all values for whitespace in the ends.
* If $onlyNonEmptyValues is set, then all blank ('') values are removed.
* Usage: 256
*
* @param string Delimiter string to explode with
* @param string The string to explode
* @param boolean If set, all empty values (='') will NOT be set in output
* @return array Exploded values
*/
function trimExplode($delim, $string, $onlyNonEmptyValues=0) {
$res1 = t3lib_div::trimExplodeOLD($delim, $string, $onlyNonEmptyValues);
$res2 = t3lib_div::trimExplodeNEW($delim, $string, $onlyNonEmptyValues);
//*** strict array compare
$res1export = var_export($res1, true);
$res2export = var_export($res2, true);
if ($res1export !== $res2export) {
trigger_error('trimExplode() New implementation failed.', E_USER_WARNING);
t3lib_div::debug($string, 'TEST ($onlyNonEmptyValues='.($onlyNonEmptyValues?'1':'0').') $string');
t3lib_div::debug($res1export, 'trimExplodeNEW() error $res1export');
t3lib_div::debug($res2export, 'trimExplodeNEW() error $res2export');
}
return $res1;
}
function trimExplodeNEW($delim, $string, $onlyNonEmptyValues=0) {
if ($onlyNonEmptyValues) {
return array_merge(array_diff(array_map('trim', explode($delim, $string)), array('')));
} else {
return array_map('trim', explode($delim, $string));
}
}
function trimExplodeOLD($delim, $string, $onlyNonEmptyValues=0) {
$temp = explode($delim,$string);
$newtemp=array();
while(list($key,$val)=each($temp)) {
if (!$onlyNonEmptyValues || strcmp('',trim($val))) {
$newtemp[]=trim($val);
}
}
reset($newtemp);
return $newtemp;
}
function debug($msg, $title) {
print $title.': '.$msg."\n";
}
}
$tests = array(
', a, 1 ,c ,dddd , ,',
", 0, , \n , , aaa , 00,",
'1,2,3,4,5.6,7,8,9,10,11',
implode(',', array_fill(0, 1000, 'test')),
implode(' , ', array_fill(0, 100, 'test')),
' ,, ,,'.implode(' , ', array_fill(0, 100, 'test')).',, ,, ',
' ,, ,,'.implode(' , , ', array_fill(0, 100, 'test')).',, ,, ',
implode(',', range(0, 1000)),
implode(' , ', range(0, 1000)),
);
print "
\n"; foreach ($tests as $test) { foreach (array(true, false) as $onlyNonEmptyValues) { $res = t3lib_div::trimExplode(',', $test, $onlyNonEmptyValues); } } print "\n";
print "If NO Error message, it's OK.\n";
?>
Updated by Martin Kutschker over 17 years ago
Another version:
function trimExplodeMASI($delim, $string, $onlyNonEmptyValues=0) {
$array = explode($delim, $string);
if ($onlyNonEmptyValues) {
foreach($array as $index => &$value) {
$value = trim($value);
if ($value == '') {
unset($array[$index]);
}
}
$array = array_merge($array);
} else {
foreach($array as $key => &$value) {
$value = trim($value);
}
}
return $array;
}
Using the arrays from the unit test my test (using t3lib_dib::milliseconds for emseauring) I get the follwing numbers (in ms):
OLD: 45
NEW: 26
MASI: 23
Updated by Martin Kutschker about 17 years ago
Version from comment #14928 has been commited to SVN.