答案是:是的,它确实复制了字符串。有点儿...其实不是。好吧,这取决于你对“复制”的定义...
>=5.4
为了了解正在发生什么,让我们看看源代码。执行器处理一个变量转换
在这里使用5.5。
zend_make_printable_zval(expr, &var_copy, &use_copy);
if (use_copy) {
ZVAL_COPY_VALUE(result, &var_copy);
} else {
ZVAL_COPY_VALUE(result, expr);
zendi_zval_copy_ctor(*result);
}
正如您所看到的,该调用使用
zend_make_printable_zval()
,如果zval已经是字符串,则会直接短路处理。
因此,执行复制操作的代码是(else分支):
ZVAL_COPY_VALUE(result, expr);
现在,让我们来看一下
ZVAL_COPY_VALUE
的定义:
do { \
(z)->value = (v)->value
Z_TYPE_P(z) = Z_TYPE_P(v)
} while (0)
请注意这段代码的作用。字符串本身并没有被复制(它存储在zval的
->value
块中)。它只是被引用了(指针保持不变,因此字符串值相同,没有复制)。但它创建了一个新变量(包装值的zval部分)。
现在,我们进入
zendi_zval_copy_ctor
调用。它在内部执行一些有趣的操作。请注意:
case IS_STRING:
CHECK_ZVAL_STRING_REL(zvalue);
if (!IS_INTERNED(zvalue->value.str.val)) {
zvalue->value.str.val = (char *) estrndup_rel(zvalue->value.str.val, zvalue->value.str.len);
}
break;
基本上,这意味着如果它是一个interned字符串,它不会被复制。但如果不是,它将会被复制...那什么是interned字符串,这是什么意思?
<= 5.3
在5.3中,interned字符串不存在。所以字符串总是被复制。这就是唯一的区别...
基准测试时间:
好吧,在这种情况下:
$a = "foo"
$b = (string) $a
在5.4中不会发生字符串的复制,但在5.3中会发生复制。
但是在这种情况下:
$a = str_repeat("a", 10);
$b = (string) $a;
所有版本都将会发生复制。这是因为在 PHP 中,不是所有字符串都被内部化了...
让我们在基准测试中试一下:http://3v4l.org/HEelW
$a = "foobarbizbazbuztestingthisoutfoobarbizbazbuztestingthisoutfoobarbizbazbuztestingthisoutfoobarbizbazbuztestingthisoutfoobarbizbazbuztestingthisoutfoobarbizbazbuztestingthisoutfoobarbizbazbuztestingthisoutfoobarbizbazbuztestingthisoutfoobarbizbazbuztestingthisoutfoobarbizbazbuztestingthisoutfoobarbizbazbuztestingthisoutfoobarbizbazbuztestingthisoutfoobarbizbazbuztestingthisoutfoobarbizbazbuztestingthisoutfoobarbizbazbuztestingthisoutfoobarbizbazbuztestingthisoutfoobarbizbazbuztestingthisoutfoobarbizbazbuztestingthisoutfoobarbizbazbuztestingthisoutfoobarbizbazbuztestingthisoutfoobarbizbazbuztestingthisoutfoobarbizbazbuztestingthisoutfoobarbizbazbuztestingthisoutfoobarbizbazbuztestingthisoutfoobarbizbazbuztestingthisoutfoobarbizbazbuztestingthisoutfoobarbizbazbuztestingthisoutfoobarbizbazbuztestingthisoutfoobarbizbazbuztestingthisoutfoobarbizbazbuztestingthisout";
$b = str_repeat("a", 300);
echo "Static Var\n";
testCopy($a);
echo "Dynamic Var\n";
testCopy($b);
function testCopy($var) {
echo memory_get_usage() . "\n";
$var = (string) $var;
echo memory_get_usage() . "\n";
}
结果:
5.4 - 5.5 alpha 1 (not including other alphas, as the differences are minor enough to not make a fundamental difference)
Static Var
220152
220200
Dynamic Var
220152
220520
So the static var increased by 48 bytes, and the dynamic var increased by 368 bytes.
5.3.11 to 5.3.22:
Static Var
624472
625408
Dynamic Var
624472
624840
The static var increased by 936 bytes while dynamic var increased by 368 bytes.
请注意,在5.3中,静态变量和动态变量都被复制了。因此,字符串总是被复制的。
但是在5.4中,对于静态字符串,只有zval结构被复制。这意味着被内部化的字符串本身保持不变,不会被复制...
另外需要注意的是,上述所有内容都是无用的。您将变量作为参数传递给函数。然后您在函数内部进行转换。因此,写时复制将由您的代码行触发。因此,运行该代码将始终(在99.9%的情况下)触发变量复制。所以最好的情况(内部化字符串)是zval复制和相关开销。在最坏的情况下,您正在谈论字符串复制...