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使用mb_check_encoding检查字符串在指定的编码里是否有效
Nov 07 PHP
php实现保存submit内容之后禁止刷新
Mar 19 PHP
Cygwin中安装PHP方法步骤
Jul 04 PHP
PHP常见数组函数用法小结
Mar 21 PHP
PHP实现适用于文件内容操作的分页类
Jun 15 PHP
Yii框架扩展CGridView增加导出CSV功能的方法
May 24 PHP
yii2中LinkPager增加总页数和总记录数的实例
Aug 28 PHP
ecshop添加菜单及权限分配问题
Nov 21 PHP
php-app开发接口加密详解
Apr 18 PHP
PHP PDOStatement::closeCursor讲解
Jan 30 PHP
PHP设计模式(七)组合模式Composite实例详解【结构型】
May 02 PHP
phpstudy隐藏index.php的方法
Sep 21 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中ini_set与ini_get用法实例
2014/11/04 PHP
yum命令安装php7和相关扩展
2016/07/04 PHP
PHP构造二叉树算法示例
2017/06/21 PHP
php实现的统计字数函数定义与使用示例
2017/07/26 PHP
PHP+百度AI OCR文字识别实现了图片的文字识别功能
2019/05/08 PHP
PHP实现单例模式建立数据库连接的方法分析
2020/02/11 PHP
js中的布尔运算符使用介绍
2013/11/20 Javascript
javascript中的return和闭包函数浅析
2014/06/06 Javascript
js Object2String方便查看js对象内容
2014/11/24 Javascript
javascript实现修改微信分享的标题内容等
2014/12/11 Javascript
原生javascript实现隔行换色
2015/01/04 Javascript
javascript简单进制转换实现方法
2016/11/24 Javascript
JavaScript两个变量交换值的实现方法
2017/03/01 Javascript
nodejs mysql 实现分页的方法
2017/06/06 NodeJs
前端构建工具之gulp的配置与搭建详解
2017/06/12 Javascript
JavaScript之面向对象_动力节点Java学院整理
2017/06/29 Javascript
JavaScript模拟文件拖选框样式v1.0的实例
2017/08/04 Javascript
详解Node.js利用node-git-server快速搭建git服务器
2017/09/27 Javascript
javascript数组定义的几种方法
2017/10/06 Javascript
小程序scroll-view组件实现滚动的示例代码
2018/09/20 Javascript
微信小程序BindTap快速连续点击目标页面跳转多次问题处理
2019/04/08 Javascript
JS中的算法与数据结构之队列(Queue)实例详解
2019/08/20 Javascript
python修改操作系统时间的方法
2015/05/18 Python
Python-嵌套列表list的全面解析
2016/06/08 Python
浅谈Python由__dict__和dir()引发的一些思考
2017/10/30 Python
深入理解Python中的super()方法
2017/11/20 Python
python使用PyQt5的简单方法
2019/02/27 Python
Django-xadmin+rule对象级权限的实现方式
2020/03/30 Python
python中def是做什么的
2020/06/10 Python
如何设定的weblogic的热启动模式(开发模式)与产品发布模式
2012/09/08 面试题
介绍一下内联、左联、右联
2013/12/31 面试题
节水倡议书范文
2014/04/15 职场文书
社区平安建设汇报材料
2014/08/14 职场文书
自我检讨书范文
2015/01/28 职场文书
2015年员工工作总结范文
2015/04/08 职场文书
2015毕业生实习期工作总结
2015/04/09 职场文书