Sample text:
$text = \'Administration\\Controller\\UserController::Save\';
Task - extract everything before ::
Option 1:
Using the time
command on Linux. The first one measured in at 0m0.024s
and the second at 0m0.011s
.
The second one appears to be faster. I ran it multiple times and the result (bar one time) seemed to be the same.
EDIT: As suggested, the other user said to run it in a loop of 5000. That completed with the same results.
On my system:
~/pb$ uname -a && php -v
Linux hostname 3.2.0-4-amd64 #1 SMP Debian 3.2.46-1+deb7u1 x86_64 GNU/Linux
PHP 5.4.19-1~dotdeb.1 (cli) (built: Aug 27 2013 00:42:43)
Copyright (c) 1997-2013 The PHP Group
Zend Engine v2.4.0, Copyright (c) 1998-2013 Zend Technologies
with XCache v3.0.3, Copyright (c) 2005-2013, by mOo
with Xdebug v2.2.3, Copyright (c) 2002-2013, by Derick Rethans
with XCache Cacher v3.0.3, Copyright (c) 2005-2013, by mOo
I have results:
~/pb$ ./test ListVsSubstr
[============================================================>] 1000 u | 8134 u/s | Est: 0.0 s | Mem: 335.74 KB | Max: 357.96 KB
[============================================================>] 1000 u | 7808 u/s | Est: 0.0 s | Mem: 336.14 KB | Max: 357.96 KB
Test name Repeats Result Performance
list+explode 1000 0.044890 sec +0.00%
substr+strpos 1000 0.052825 sec -17.68%
Test code here: link.
From time to time results slightly different, but list+explode
is always faster more than 15%.
Different systems and PHP versions may have different results. You must check it by yourself and for sure in environment configuration identical to your production.
substr+strpos
will be faster and take less cpu time and use less memeroy.
Let's find out the answer from php soruce code.
explode
first:
PHP_FUNCTION(explode)
{
// other codes
array_init(return_value);
if (str_len == 0) {
if (limit >= 0) {
add_next_index_stringl(return_value, "", sizeof("") - 1, 1);
}
return;
}
// other code
if (limit > 1) {
php_explode(&zdelim, &zstr, return_value, limit);
} else if (limit < 0) {
php_explode_negative_limit(&zdelim, &zstr, return_value, limit);
} else {
add_index_stringl(return_value, 0, str, str_len, 1);
}
}
PHPAPI void php_explode(zval *delim, zval *str, zval *return_value, long limit)
{
char *p1, *p2, *endp;
endp = Z_STRVAL_P(str) + Z_STRLEN_P(str);
p1 = Z_STRVAL_P(str);
p2 = php_memnstr(Z_STRVAL_P(str), Z_STRVAL_P(delim), Z_STRLEN_P(delim), endp);
if (p2 == NULL) {
add_next_index_stringl(return_value, p1, Z_STRLEN_P(str), 1);
} else {
do {
add_next_index_stringl(return_value, p1, p2 - p1, 1);
p1 = p2 + Z_STRLEN_P(delim);
} while ((p2 = php_memnstr(p1, Z_STRVAL_P(delim), Z_STRLEN_P(delim), endp)) != NULL &&
--limit > 1);
if (p1 <= endp)
add_next_index_stringl(return_value, p1, endp-p1, 1);
}
}
explode
will call php_memnstr
multiple times and add_next_index_stringl
multiple times which will operate the result list
.
Now strpos
:
PHP_FUNCTION(strpos)
{
zval *needle;
char *haystack;
char *found = NULL;
char needle_char[2];
long offset = 0;
int haystack_len;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz|l", &haystack, &haystack_len, &needle, &offset) == FAILURE) {
return;
}
if (offset < 0 || offset > haystack_len) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Offset not contained in string");
RETURN_FALSE;
}
if (Z_TYPE_P(needle) == IS_STRING) {
if (!Z_STRLEN_P(needle)) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Empty needle");
RETURN_FALSE;
}
found = php_memnstr(haystack + offset,
Z_STRVAL_P(needle),
Z_STRLEN_P(needle),
haystack + haystack_len);
} else {
if (php_needle_char(needle, needle_char TSRMLS_CC) != SUCCESS) {
RETURN_FALSE;
}
needle_char[1] = 0;
found = php_memnstr(haystack + offset,
needle_char,
1,
haystack + haystack_len);
}
if (found) {
RETURN_LONG(found - haystack);
} else {
RETURN_FALSE;
}
}
PHP_FUNCTION(substr)
{
// other code about postion
RETURN_STRINGL(str + f, l, 1);
}
It calls php_memnstr
only one time, and substr
operates the input string in memery, return the sub one.
I ran a test and seems like the first solution is faster. Here is the code for testing it:
function microtime_float()
{
list($usec, $sec) = explode(" ", microtime());
return ((float)$usec + (float)$sec);
}
function solution1($text)
{
for($i = 0; $i < 10000; $i++)
list($module) = explode('::',$text);
}
function solution2($text)
{
for($i = 0; $i < 10000; $i++)
$module = substr($text, 0, strpos($text, '::'));
}
$text = 'Administration\Controller\UserController::Save';
$time_start = microtime_float();
solution1($text);
$time_end = microtime_float();
$time = $time_end - $time_start;
echo "Did solution1 in $time seconds.\n";
$time_start = microtime_float();
solution2($text);
$time_end = microtime_float();
$time = $time_end - $time_start;
echo "Did solution2 in $time seconds.\n";
Test 1: Did solution1 in 0.19701099395752 seconds. Did solution2 in 0.38502216339111 seconds.
Test 2: Did solution1 in 0.1990110874176 seconds. Did solution2 in 0.37402105331421 seconds.
Test 3: Did solution1 in 0.19801092147827 seconds. Did solution2 in 0.37002205848694 seconds.
Tested: explode with limit (solution3) Tested: preg_match
note: 10000 is not enough for me, so I run with 10 000 000 x 3
<?php
function microtime_float()
{
list($usec, $sec) = explode(" ", microtime());
return ((float)$usec + (float)$sec);
}
function solution1($text)
{
for($i = 0; $i < 10000000; $i++)
list($module) = explode('::',$text);
}
function solution2($text)
{
for($i = 0; $i < 10000000; $i++)
$module = substr($text, 0, strpos($text, '::'));
}
function solution3($text)
{
for($i = 0; $i < 10000000; $i++)
list($module) = explode('::',$text, 2);
}
function solution4($text)
{
for($i = 0; $i < 10000000; $i++)
preg_match('/^(.*)::/', $text, $m) && $module = $m[1];
}
$text = 'Administration\Controller\UserController::Save';
for ($i=0; $i < 3; $i++) {
$time_start = microtime_float();
solution1($text);
$time_end = microtime_float();
$time = $time_end - $time_start;
echo "Did solution1 in $time seconds.\n";
$time_start = microtime_float();
solution2($text);
$time_end = microtime_float();
$time = $time_end - $time_start;
echo "Did solution2 in $time seconds.\n";
$time_start = microtime_float();
solution3($text);
$time_end = microtime_float();
$time = $time_end - $time_start;
echo "Did solution3 in $time seconds.\n";
}
And the results are:
Did solution1 in 6.4486601352692 seconds.
Did solution2 in 9.4331159591675 seconds.
Did solution3 in 6.6791591644287 seconds.
Did solution4 in 9.3652379512787 seconds.
Did solution1 in 7.1072399616241 seconds.
Did solution2 in 10.755952835083 seconds.
Did solution3 in 7.5958750247955 seconds.
Did solution4 in 9.4377269744873 seconds.
Did solution1 in 7.4207429885864 seconds.
Did solution2 in 10.894104003906 seconds.
Did solution3 in 7.701789855957 seconds.
Did solution4 in 9.5081558227539 seconds.
The solution3 is take longer than the solution1!
ran on cli, 100% cpu usage on 1 thread
efficient ? if you mean by execution time. then run each in a loop multiple (1000) time and check the execution time.