盒子
盒子
文章目录
  1. 原型模式
  2. clone方式
    1. clone方式性能测试
  3. new方式
    1. new方式性能测试
  4. 业务场景下的clone
    1. 业务场景下的clone性能测试
  5. 结论

PHP原型模式之性能探索

原型模式

有些时候,我们需要创建多个类似的大对象。如果直接通过new对象,开销很大,而且new完还得进行重复的初始化工作。可能把初始化工作封装起来的,但是对于系统来说,你封不封装,初始化工作还是要执行。

原型模式则不同,原型模式是先创建好一个原型对象,然后通过clone这个原型对象来创建新的对象,这样就免去了重复的初始化工作,系统仅需内存拷贝即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
interface IPrototype {
public function copy();
}

class Student implements IPrototype {
private $_name;
private $_hobbies = [];

public function __construct($name, $hobbies)
{
$this->_name = $name;
$this->_hobbies = $hobbies;
}

public function setName($name)
{
$this->_name = $name;

}

public function setHobby($hobbies)
{
$this->_hobbies = $hobbies;
}

public function copy()
{
return clone $this;
}
}

$Alan = new Student('Alan',['basketball','swimming']);
$Bob = $Alan->copy();
$Bob->setName('Bob');

$Alan->setHobby(['traveling','chess']);

var_dump($Alan);
var_dump($Bob);

// object(Student)#1 (2) {
// ["_name":"Student":private]=>
// string(4) "Alan"
// ["_hobbies":"Student":private]=>
// array(2) {
// [0]=>
// string(9) "traveling"
// [1]=>
// string(5) "chess"
// }
// }
// object(Student)#2 (2) {
// ["_name":"Student":private]=>
// string(3) "Bob"
// ["_hobbies":"Student":private]=>
// array(2) {
// [0]=>
// string(10) "basketball"
// [1]=>
// string(8) "swimming"
// }
// }

我对这里的性能差异有些兴趣,分别对new和clone做一些性能测试

clone方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
define("NEWLINE",chr(10));

interface IPrototype {
public function copy();
}

class Student implements IPrototype {
private $_name;
private $_hobbies = [];

public function __construct($name, $hobbies)
{
$this->_name = $name;
$this->_hobbies = $hobbies;
}

public function setName($name)
{
$this->_name = $name;

}

public function setHobby($hobbies)
{
$this->_hobbies = $hobbies;
}

public function copy()
{
return clone $this;
}
}

$bigArr = range(1,100000);

$m1 = memory_get_usage();

$Alan = new Student('Alan', $bigArr);
$Bob = $Alan->copy();

$m2 = memory_get_usage();
$memory = $m2 - $m1;
echo '内存占用:' . $memory . NEWLINE;

clone方式性能测试

1
2
3
4
5
6
7
8
$ php index.php 
内存占用:664
$ php index.php
内存占用:664
$ php index.php
内存占用:664
$ php index.php
内存占用:664

new方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
define("NEWLINE",chr(10));

interface IPrototype {
public function copy();
}

class Student implements IPrototype {
private $_name;
private $_hobbies = [];

public function __construct($name, $hobbies)
{
$this->_name = $name;
$this->_hobbies = $hobbies;
}

public function setName($name)
{
$this->_name = $name;

}

public function setHobby($hobbies)
{
$this->_hobbies = $hobbies;
}

public function copy()
{
return clone $this;
}
}

$bigArr = range(1,100000);

$m1 = memory_get_usage();

$Alan = new Student('Alan', $bigArr);
$Alan2 = new Student('Alan', $bigArr);

$m2 = memory_get_usage();
$memory = $m2 - $m1;
echo '内存占用:' . $memory . NEWLINE;

new方式性能测试

1
2
3
4
5
6
7
8
$ php index.php 
内存占用:712
$ php index.php
内存占用:712
$ php index.php
内存占用:712
$ php index.php
内存占用:712

可以看出,clone确实比new消耗的内存更小,但是,回到上面,我们真正的业务是copy完这个对象后,要对这个对象做一些修改,比如修改这个clone完后的对象的name,看看会发生什么事情

业务场景下的clone

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
define("NEWLINE",chr(10));

interface IPrototype {
public function copy();
}

class Student implements IPrototype {
private $_name;
private $_hobbies = [];

public function __construct($name, $hobbies)
{
$this->_name = $name;
$this->_hobbies = $hobbies;
}

public function setName($name)
{
$this->_name = $name;

}

public function setHobby($hobbies)
{
$this->_hobbies = $hobbies;
}

public function copy()
{
return clone $this;
}
}

$bigArr = range(1,100000);

$m1 = memory_get_usage();

$Alan = new Student('Alan', $bigArr);
$Bob = $Alan->copy();
$Bob->setName('Bob');

$m2 = memory_get_usage();
$memory = $m2 - $m1;
echo '内存占用:' . $memory . NEWLINE;

业务场景下的clone性能测试

1
2
3
4
5
6
7
8
$ php index.php 
内存占用:744
$ php index.php
内存占用:744
$ php index.php
内存占用:744
$ php index.php
内存占用:744

我们发现,对新对象做完名字重置的操作后,内存占用更大了,比new的操作更大!

结论

  • clone一个对象带来的内存消耗比new一个对象更少
  • 在某些业务下,比如对新对象做属性修改时,直接new一个对象带来的内存消耗更少
请我喝一杯咖啡
扫一扫,支持funsoul
  • 微信扫一扫