大家都知道这两个函数都是截取字符串的,一个是可以按字节截取,另外一个是可以按字截取
我写了一个例子:for($i = 0;$i< 100000; $i ++){
$a = substr($str, 0, 1);
}和for($i = 0;$i< 100000; $i ++){
$a = mb_substr($str, 0, 1);
}两个的执行时间在我的电脑显示
执行程序共耗费 0.097547054290771秒
执行程序共耗费 0.10249900817871秒但是如果我吧mb_substr 加上encode参数,for($i = 0;$i< 100000; $i ++){
$a = mb_substr($str, 0, 1, "gbk"); // 或utf-8
}则差别太大了
执行程序共耗费 0.097554922103882秒
执行程序共耗费 4.9396300315857秒为什么加上encode之后,mb_substr的执行效率会降这么多呢?
我写了一个例子:for($i = 0;$i< 100000; $i ++){
$a = substr($str, 0, 1);
}和for($i = 0;$i< 100000; $i ++){
$a = mb_substr($str, 0, 1);
}两个的执行时间在我的电脑显示
执行程序共耗费 0.097547054290771秒
执行程序共耗费 0.10249900817871秒但是如果我吧mb_substr 加上encode参数,for($i = 0;$i< 100000; $i ++){
$a = mb_substr($str, 0, 1, "gbk"); // 或utf-8
}则差别太大了
执行程序共耗费 0.097554922103882秒
执行程序共耗费 4.9396300315857秒为什么加上encode之后,mb_substr的执行效率会降这么多呢?
string.no_encoding = MBSTRG(current_internal_encoding); //默认为当前内部编码 argc = ZEND_NUM_ARGS();
switch (argc) {
case 2:
if (zend_get_parameters_ex(2, &arg1, &arg2) == FAILURE) {
WRONG_PARAM_COUNT;
}
break;
case 3:
if (zend_get_parameters_ex(3, &arg1, &arg2, &arg3) == FAILURE) {
WRONG_PARAM_COUNT;
}
break;
case 4:
if (zend_get_parameters_ex(4, &arg1, &arg2, &arg3, &arg4) == FAILURE) {
WRONG_PARAM_COUNT;
}
convert_to_string_ex(arg4);
string.no_encoding = mbfl_name2no_encoding(Z_STRVAL_PP(arg4));
if (string.no_encoding == mbfl_no_encoding_invalid) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown encoding \"%s\"", Z_STRVAL_PP(arg4));
RETURN_FALSE;
}
break;
default:
WRONG_PARAM_COUNT;
}
}enum mbfl_no_encoding
mbfl_name2no_encoding(const char *name)
{
const mbfl_encoding *encoding; encoding = mbfl_name2encoding(name);
if (encoding == NULL) {
return mbfl_no_encoding_invalid;
} else {
return encoding->no_encoding;
}
}const mbfl_encoding *
mbfl_name2encoding(const char *name)
{
const mbfl_encoding *encoding;
int i, j; if (name == NULL) {
return NULL;
} i = 0;
while ((encoding = mbfl_encoding_ptr_list[i++]) != NULL){
if (strcasecmp(encoding->name, name) == 0) {
return encoding;
}
} /* serch MIME charset name */
i = 0;
while ((encoding = mbfl_encoding_ptr_list[i++]) != NULL) {
if (encoding->mime_name != NULL) {
if (strcasecmp(encoding->mime_name, name) == 0) {
return encoding;
}
}
} /* serch aliases */
i = 0;
while ((encoding = mbfl_encoding_ptr_list[i++]) != NULL) {
if (encoding->aliases != NULL) {
j = 0;
while ((*encoding->aliases)[j] != NULL) {
if (strcasecmp((*encoding->aliases)[j], name) == 0) {
return encoding;
}
j++;
}
}
} return NULL;
}
可以看到,如果提供了第四个参数,php会解析这个参数
1. 将这个参数转换成字符串.
2. 调用mbfl_name2no_encoding获得编码器序号
3. 第二步会调用mbfl_name2encoding,这个函数在循环中使用了strcasecmpstrcasecmp是忽略大小写的字符串匹配,性能很低,
而且,如果第四个参数提供的 编码名字 不规范,会造成标准名字列表中找不到
还会做进一步的尝试,从MIME charset name中寻找,如果还是找不到
会继续尝试,通过别名来查找,这个过程是相当慢的,假如有20种编码,每种有10个别名
就要从20*10个字符串中,查找出匹配的,还是不区分大小写的匹配,相当的慢所以,尽量使用跟内部编码一致的编码,如果不能,编码名字一定要写的规范
加了encode之后,mb_substr需要逐字判别字边界。
虽然慢了,但准确度高了
扩展中把最近使用的10个编码名字,放到在zend_hash_table中
这样就不需要每次都查找那么多次
改成这个样子mb_internal_encoding("gbk");
for($i = 0; $i < 100000; $i ++){
$a = mb_substr($str, 0, 1);
}
substr只用于英文, mb_substr用于各种不同编码的文字符号, 甚至乱码也OK, 速度慢, 也就是6楼说的情况
-------------------------------
mb_substr($str, 0, 1, "gbk"); //传参时间 + 函数内部判断参数, 现在多了一个参数, 运行时间倍加是必然
其实不是因为多字节的缘故,
而是编码名字的查找,耗费了大量时间//写法一,在我这里耗费3.4秒
<?php
$str = "abc我";
for ($i=0; $i<1000000; $i++)
$a = mb_substr($str, 0, 2, "utf8");
?>
//
//
//写法二,在我这里耗费1.4秒
<?php
$str = "abc我";
for ($i=0; $i<1000000; $i++)
$a = mb_substr($str, 0, 2, "UTF-8");
?>
//
//
//写法三,在我这里耗费0.7秒
<?php
$str = "abc我";
mb_internal_encoding("UTF-8");
for ($i=0; $i<1000000; $i++)
$a = mb_substr($str, 0, 2);
?>
//
//
//
//减去解释器载入和初始化以及代码编译的时间
//各个方案的性能差别,就更大了