php for 循环 try_PHP 操作码 (OP)

PHP的操作码全称是:Operation Code(OpCode) 。在写之前也在网上查了相关需要的资料,发现有些人理解的不是很正确。比如:将操作码理解为“底层代码”,这个是不能这样理解的。首先来看什么是“底层代码”。

c808faca441a113f3e706e9b0e37d145.png

上述代码既是“底层代码”又不是“底层代码”,为什么这么说?真正的底层代码应该是机器码,也就是CPU能直接进行执行的代码。但是上述代码扔给CPU,CPU是没办法执行的,因为上述代码是:汇编代码。汇编语言是一种低级语言,不要被名字迷惑,实际低级语言较高级语言而言更加的强大。大致可分为:低级语言 中级语言 高级语言。这里有一个中级语言是因为C语言比较特殊,它既是高级语言又是低级语言。这个是很早以前的一个概念,有些人认同有些人不认同。所以大概了解一下就行。机器语言以CPU为例,大致是0和1。最早的“键盘”实际只有两个按键0和1。为什么是这样?在我的理解看来是为了贴近电。如果学过汇编语言,那么开篇的内容几乎千篇一律讲解:各种总线。这个可能理解起来比较麻烦,但是如果学过模电或者数电应该很好理解,在高电平情况下是1在低电平情况下是0。于是通过数据总线传输数据,通过时钟总线传输时间。通过时间来进行数据的读取。这里拿I2C来举例。

bcfa4bf28a079896ad92ed05e00b7b04.png

SCL:时钟线

SDA:数据线

在高电平的时候数据不允许发生改变,在低电平的时候数据可以发送改变。这里的时钟控制就是以晶振来做时钟源的。晶振是什么?嗯,发现越扯越远。回到上面的内容,为什么说Opcode不是底层代码。是因为Opcode代码最终的执行者不是CPU,而是解释器。有解释器的语言基本上都是脚本语言,这里的PHP就是一个典型的脚本语言,因为其中的Zend引擎就是专门解析PHP代码并且执行的。但是执行的前提是把代码编译为操作码(Opcode),Zend才能执行,所以Opcode在PHP里也叫中间代码。这里是因为编译的一个过程。代码的编译过程大致如下:分割Token、词法分析、语法分析、编译、执行。这里的Token就是一个不可分割的最小单元。嗯。感觉又要说远了。总的来说在语法分析阶段产生了中间码(Opcode)。实际不要这个中间码感觉也行。但是要考虑一种情况。那就是PHP毕竟是Web语言,如果没有中间码会出现执行率下降问题。而有了中间码,可以将中间码缓存,下次直接执行。这也是OpCache的原因,很多的PHP代码加密工具就是对Opcode进行了二次处理。

下面来分析一下php的中间码(OpCode),这里以php7.3.4静态分析举例。把下面代码编译成Opcode。

<?php     echo 123;

Opcode

99800036f8af6085a95352c3ad4975dd.png

看这一行:2   0  E >  EXT_STMI   "123" ,有几个关键的点需要注意。

2:PHP源码的行号

0:Opcode的行号

EXT_STMT:中间码的操作标识

123:中间码的操作值

这里要说一下Opcode的数据结构如下

struct _zend_op_array {  /* Common elements */  zend_uchar type;  zend_uchar arg_flags[3]; /* bitset of arg_info.pass_by_reference */  uint32_t fn_flags;  zend_string *function_name;  zend_class_entry *scope;  zend_function *prototype;  uint32_t num_args;  uint32_t required_num_args;  zend_arg_info *arg_info;  /* END of common elements */  int cache_size;     /* number of run_time_cache_slots * sizeof(void*) */  int last_var;       /* number of CV variables */  uint32_t T;         /* number of temporary variables */  uint32_t last;      /* number of opcodes */  zend_op *opcodes;  void **run_time_cache;  HashTable *static_variables;  zend_string **vars; /* names of CV variables */  uint32_t *refcount;  int last_live_range;  int last_try_catch;  zend_live_range *live_range;  zend_try_catch_element *try_catch_array;  zend_string *filename;  uint32_t line_start;  uint32_t line_end;  zend_string *doc_comment;  int last_literal;  zval *literals;  void *reserved[ZEND_MAX_RESERVED_RESOURCES];};

然后看一下万年不变的问题:For和ForEach到底谁快

<?php   $arr=array(1,2,3);  foreach ($arr as $value)    echo $value;
<?php   $arr=array(1,2,3);  for($i=0;$i<3;$i++)    echo $arr[$i];

Opcode

3aa8eb0ad83c387bbe2c1f64ca02ff77.png

7ae3cccc195175e72d0e855db48c13ff.png

从中间码(Opcode)的数量上来看ForEach胜了,在从流程上来看ForEach的中间码大致意思是:创建一个迭代器!0(FE_RESET_R)从迭代器里取值给!1(FE_FETCH_R)。如果取不到或者迭代器为空的话则跳到(FE_FREE)释放掉$3对应的内存区域。输出值(!1)以后跳转到(FE_RESET_R)继续从迭代器取值,然后进行判断是否为空。这样就构成了一个循环。但是发现它并没有对(!1)进行释放,所以这块内存是一直不释放。然后看For语句内存引用达到6个之多,而且还有一些运算操作,效率很低。一直以为在高版本的PHP中已经解决了这个问题,毕竟很多静态语言已经处理了这个问题,但是没想到PHP到7了,还存在这个效率问题。头大...
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值