PHP代码优化之成员变量获取速度对比


Posted in PHP onFebruary 28, 2014

有如下4个代码示例,你认为他们创建对象,并且获得成员变量的速度排序是怎样的?

1:将成员变量设置为public,通过赋值操作给成员变量赋值,直接获取变量

<?php
class Foo {
    public $id;
}
$data = new Foo;
$data->id = 10;
echo $data->id;
?>

2:将成员变量设置为public,通过构造函数设置成员变量的值,直接获取变量
<?php
class Foo2 {
 public $id;
 public function __construct($id) {
  $this->id = $id;
 }
}
$data = new Foo2(10);
echo $data->id;
?>

3:将成员变量设置为protected,通过构造函数设置成员变量的值,通过魔术方法获取变量
<?php
class Foo3 {
 protected $id;
 public function __construct($id) {
  $this->id = $id;
 }
 public function getId() {
  return $this->id;
 }
}
$data = new Foo3(10);
echo $data->getId();
?>

4:将成员变量设置为protected,通过构造函数设置成员变量的值,通过成员方法获取变量
<?php
class Foo4 {
  protected $id;
  public function __construct($id) {
   $this->id = $id;
  }

  public function __get($key) {
   return $this->id;
  }
}
$data = new Foo4(10);
echo $data->id;
?>
按执行速度快慢排序: 1243
咱们先看其opcode:
1:

1  ZEND_FETCH_CLASS 4  :4  'Foo'
2  NEW         $5 :4
3  DO_FCALL_BY_NAME   0          
4  ASSIGN         !0, $5
5  ZEND_ASSIGN_OBJ   !0, 'id'
6  ZEND_OP_DATA    10
7  FETCH_OBJ_R   $9 !0, 'id'
8  ECHO            $9

2:
1  ZEND_FETCH_CLASS 4  :10 'Foo2'
2  NEW               $11 :10
3  SEND_VAL           10
4  DO_FCALL_BY_NAME  1 
5  ASSIGN        !1, $11
6  FETCH_OBJ_R   $14 !1, 'id'
7  ECHO            $14

3:
1  ZEND_FETCH_CLASS 4  :15 'Foo3'
2  NEW            $16 :15
3  SEND_VAL        10
4  DO_FCALL_BY_NAME   1          
5  ASSIGN         !2, $16
6  ZEND_INIT_METHOD_CALL !2, 'getId'
7  DO_FCALL_BY_NAME  0  $20     
8  ECHO           $20

4:
1  ZEND_FETCH_CLASS 4  :21 'Foo4'
2  NEW            $22 :21
3  END_VAL         10
4  DO_FCALL_BY_NAME  1          
5  ASSIGN           !3, $22
6  FETCH_OBJ_R    $25 !3, 'id'
7   ECHO      $25

根据上面的opcode,参照其在zend_vm_execute.h文件对应的opcode实现,我们可以发现什么?

一、PHP内核创建对象的过程分为三步:

ZEND_FETCH_CLASS 根据类名获取存储类的变量,其实现为一个hashtalbe EG(class_table) 的查找操作
NEW 初始化对象,将EX(call)->fbc指向构造函数指针。
调用构造函数,其调用和其它的函数调用是一样,都是调用zend_do_fcall_common_helper_SPEC

二、魔术方法的调用是通过条件触发的,并不是直接调用,如我们示例中的成员变量id的获取

(zend_std_read_property),其步骤为:
获取对象的属性,如果存在,转第二步;如果没有相关属性,转第三步
从对象的properties查找是否存在与名称对应的属性存在,如果存在返回结果,如果不存在,转第三步
如果存在__get魔术方法,则调用此方法获取变量,如果不存在,报错
回到排序的问题:

一、第一个和第二个的区别是什么?

第二个的opcode比第一个要少,反而比第一个要慢一些,因为构造函数多了参数,多了一个参数处理的opcode。参数处理是一个比较费时的操作,当我们在做代码优化时,一些不必要的参数能去掉就去掉;当一个函数有多个参数时,可以考虑通过一个数组将其封装后传递进来。

二、为啥第三个最慢?

因为其获取参数其本质上是一次对象成员方法的调用,方法的调用成本高于变量的获取

三、为啥第四个比第三个要快?

因为第四个的操作实质上获取变量,只不过其内部实现了魔术方法的调用,相对于用户定义的方法,内部函数的调用的效率会高。因此,当我们有一些PHP内核实现的方法可以调用时就不要重复发明轮子了。
四、为啥第四个比第二个要慢?
因为在PHP的对象获取变量的过程中,当成员变量在类的定义不在在时,会去调用PHP特有的魔术方法__get,多了一次魔术方法的调用。

总结一下:

1.使用PHP内置函数
2.并不是事必面向对象(OOP),面向对象往往开销很大,每个方法和对象调用都会消耗很多内存。
3.尽量少用魔术方法 -- 除非有必要,不要用框架,因为框架都有大量的魔术方法使用。
4.在性能优先的应用场景中,将成员变量不失为一种比较好的方法,当你需要用到OOP时。
5.能使用PHP语法结构的不要用函数,能使用内置函数的不要自己写,能用函数的不要用对象

PHP 相关文章推荐
php实现rc4加密算法代码
Apr 25 PHP
php多文件上传实现代码
Feb 20 PHP
php类声明和php类使用方法示例分享
Mar 29 PHP
php实现的Timer页面运行时间监测类
Sep 24 PHP
PHP中的Streams详细介绍
Nov 12 PHP
PHP获取服务器端信息的方法
Nov 28 PHP
thinkPHP查询方式小结
Jan 09 PHP
php使用CutyCapt实现网页截图保存的方法
Oct 03 PHP
DEDE实现转跳属性文档在模板上调用出转跳地址
Nov 04 PHP
centos下file_put_contents()无法写入文件的原因及解决方法
Apr 01 PHP
thinkPHP实现的联动菜单功能详解
May 05 PHP
PHP 7.1中AES加解密方法mcrypt_module_open()的替换方案
Oct 17 PHP
php递归方法实现无限分类实例代码
Feb 28 #PHP
PHP中常用的转义函数
Feb 28 #PHP
PHP中鲜为人知的10个函数
Feb 28 #PHP
php中调用其他系统http接口的方法说明
Feb 28 #PHP
PHP URL参数获取方式的四种例子
Feb 28 #PHP
php使用多个进程同时控制文件读写示例
Feb 28 #PHP
php使用curl抓取qq空间的访客信息示例
Feb 28 #PHP
You might like
用PHP和ACCESS写聊天室(一)
2006/10/09 PHP
Ext.data.PagingMemoryProxy分页一次性读取数据的实现代码
2010/04/07 PHP
php+mysql实现简单的增删改查功能
2015/07/13 PHP
深入理解PHP之源码目录结构与功能说明
2016/06/01 PHP
浅谈php中urlencode与rawurlencode的区别
2016/09/05 PHP
Laravel框架实现的批量删除功能示例
2019/01/16 PHP
javascript入门·图片对象(无刷新变换图片)\滚动图像
2007/10/01 Javascript
css与javascript跨浏览器兼容性总结
2014/09/15 Javascript
全面解析Bootstrap表单使用方法(表单控件)
2015/11/24 Javascript
JavaScript设计模式经典之工厂模式
2016/02/24 Javascript
AngularJS自定义控件实例详解
2016/12/13 Javascript
vuejs响应用户事件(如点击事件)
2017/03/14 Javascript
详解Angular CLI + Electron 开发环境搭建
2017/07/20 Javascript
webpack2.0配置postcss-loader的方法
2017/08/17 Javascript
vue.js todolist实现代码
2017/10/29 Javascript
利用PHP实现递归删除链表元素的方法示例
2020/10/23 Javascript
Python基本数据类型详细介绍
2014/03/11 Python
python中使用OpenCV进行人脸检测的例子
2014/04/18 Python
Python SQLite3数据库日期与时间常见函数用法分析
2017/08/14 Python
python中利用Future对象异步返回结果示例代码
2017/09/07 Python
Python绘制七段数码管实例代码
2017/12/20 Python
Python cookbook(数据结构与算法)找到最大或最小的N个元素实现方法示例
2018/02/13 Python
python读取Excel实例详解
2018/08/17 Python
Python3.6使用tesseract-ocr的正确方法
2018/10/17 Python
python dataframe向下向上填充,fillna和ffill的方法
2018/11/28 Python
python 读取Linux服务器上的文件方法
2018/12/27 Python
python实现字符串和数字拼接
2020/03/02 Python
django修改models重建数据库的操作
2020/03/31 Python
俄罗斯苹果优质经销商商店:iPort
2020/05/27 全球购物
四议两公开实施方案
2014/03/28 职场文书
医院党员公开承诺书
2014/08/30 职场文书
争先创优演讲稿
2014/09/15 职场文书
入党自荐书范文
2015/03/05 职场文书
事业单位财务人员岗位职责
2015/04/14 职场文书
2015中秋祝酒词
2015/08/12 职场文书
JavaCV实现照片马赛克效果
2022/01/22 Java/Android