php - (string) 'hard-copy' 是字符串吗?

标签 php string copy-on-write

PHP uses a copy-on-modification system.

$a = (string) $a; ($a is a already string) 是否修改和复制任何内容?


特别是,这是我的问题:

参数 1 mixed/我想允许传递非字符串并将它们转换为字符串。
但有时这些字符串非常大。所以我想省略参数的复制,它已经是一个字符串。

我可以使用版本 Foo 还是必须使用版本 Bar

class Foo {
    private $_foo;
    public function __construct($foo) {
        $this->_foo = (string) $foo;
    }
}

class Bar {
    private $_bar;
    public function __construct($bar) {
        if (is_string($bar)) {
            $this->_bar = $bar;
        } else {
            $this->_bar = (string) $bar;
        }
    }
}

最佳答案

答案是肯定的,它确实复制了字符串。有点……不是真的。嗯,这取决于你对“复制”的定义......

>= 5.4

要了解发生了什么,让我们查看源代码。执行者处理变量转换 in 5.5 here .

    zend_make_printable_zval(expr, &var_copy, &use_copy);
    if (use_copy) {
        ZVAL_COPY_VALUE(result, &var_copy);
        // if optimized out
    } else {
        ZVAL_COPY_VALUE(result, expr);
        // if optimized out
        zendi_zval_copy_ctor(*result);
    }

如您所见,调用使用了 zend_make_printable_zval()如果 zval 已经是一个字符串,它就会短路。

所以执行复制的代码是(else 分支):

ZVAL_COPY_VALUE(result, expr);

现在,让我们看看the definition of ZVAL_COPY_VALUE :

#define ZVAL_COPY_VALUE(z, v)                   \
    do {                                        \
        (z)->value = (v)->value;                \
        Z_TYPE_P(z) = Z_TYPE_P(v);              \
    } while (0)

注意它在做什么。字符串本身是 NOT 复制的(存储在 zval 的 ->value block 中)。它只是被引用(指针保持不变,所以字符串值相同,没有副本)。但它正在创建一个新变量(包装值的 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 字符串,它是什么意思?

<= 5.3

在 5.3 中,不存在驻留字符串。所以字符串总是被复制。这真的是唯一的区别......

基准时间:

好吧,在这样的情况下:

$a = "foo";
$b = (string) $a;

在 5.4 中不会复制字符串,但在 5.3 中会复制。

但在这种情况下:

$a = str_repeat("a", 10);
$b = (string) $a;

副本出现在所有版本中。那是因为在 PHP 中,并不是所有的字符串都是 interned...

让我们在基准测试中尝试一下: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(不包括其他 alpha,因为差异很小,不会造成根本性差异)

    Static Var
    220152
    220200
    Dynamic Var
    220152
    220520
    

    所以静态变量增加了48字节,动态变量增加了368字节。

  • 5.3.11 到 5.3.22:

    Static Var
    624472
    625408
    Dynamic Var
    624472
    624840
    

    静态变量增加了936字节,动态变量增加了368字节。

请注意,在 5.3 中,静态和动态变量都被复制了。所以字符串总是重复的。

但是在带有静态字符串的 5.4 中,只复制了 zval 结构。这意味着被驻留的字符串本身保持不变并且不会被复制......

另一件事

另一件需要注意的事情是,以上所有内容都没有实际意义。您将变量作为参数传递给函数。然后你在函数内部进行转换。所以写时复制将由您的行触发。所以运行 总是(好吧,在 99.9% 的情况下)会触发变量复制。所以充其量(实习字符串)你在谈论 zval 重复和相关的开销。最坏的情况是,你在谈论字符串重复...

关于php - (string) 'hard-copy' 是字符串吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15139673/

相关文章:

php - 如何在 MySQL 和 PHP 中执行反向查找类型处理?

c - 扫描以 32 个十六进制值表示的 128 位输入

java - 这个生成了多少 Java 对象 - new String ("abcd")

string - 递归查找文件中的文本 (PowerShell)

perl - 避免在 Perl 中退出时只读 forked() RAM 分配

c - 我如何在 fork(), linux 中演示 COPY ON WRITE

php - 使用 XML 而不是 MySQL

php - jQuery 从 ajax 响应中读取数组

javascript - CSS calc如何根据一定的li的量来使用?

rust - 如何将 SmallVec 与 Cow 一起使用