Project

General

Profile

Actions

Bug #15834

closed

t3lib_div::trimExplode() is heavily called and can be optimized => nearly 1% improvement in fronEnd (both cached and uncached)

Added by Guillaume Crico over 18 years ago. Updated over 16 years ago.

Status:
Closed
Priority:
Should have
Category:
Communication
Target version:
-
Start date:
2006-03-15
Due date:
% Done:

0%

Estimated time:
TYPO3 Version:
4.0
PHP Version:
5.0
Tags:
Complexity:
Is Regression:
Sprint Focus:

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)

Actions #1

Updated by Martin Kutschker over 18 years ago

Wolfgang, your our optimization hero. What do you think of this?

Actions #2

Updated by Wolfgang Klinger over 18 years ago

Here are some numbers:

-----------------
Result *old*: 39.97
-----------------
Result *new*: 28.69
-----------------
Actions #3

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%

Actions #4

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.

Actions #5

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";

?>

Actions #6

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

Actions #7

Updated by Martin Kutschker about 17 years ago

Version from comment #14928 has been commited to SVN.

Actions

Also available in: Atom PDF