从接触PHP以来就一直有单引号比双引号的快的思想,今天看了一篇文章,也是关于单引号和双引号的测试,但是我认为那片文章没有测完整,所以自己重测了一遍,发现了一个比较有意思的问题:单引号与双引号快不快可能看写法,写法不同,产生的opcode也是不一样的,自然速度也不一样了。

测试环境:

1
2
3
4
    CPU : AMD Athlon(tm) II Dual-Core M300 × 2
    OS  : ubuntu 12.04 32bit
    PHP : PHP Version 5.3.10-1ubuntu3.10
    tool: vld

代码1:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
$single_str 'Hello quotes';
$double_str "Hello quotes";
echo $single_str;
echo $double_str;
 
$tail 'quotes';
$single_str_tail 'Hello '.$tail;
$double_str_tail "Hello ".$tail;
echo $single_str_tail;
echo $double_str_tail;
 
$head 'Hello';
$single_str_head $head.' quotes';
$double_str_head $head." quotes";
echo $single_str_head;
echo $double_str_head;
?>

通过vld产生的结果如下

line     # *  op              fetch   ext  return  operands
-------------------------------------------------------------
   2     0  >   EXT_STMT                             
         1      ASSIGN                               !0, 'Hello+quotes'
   3     2      EXT_STMT                             
         3      ASSIGN                               !1, 'Hello+quotes'
   4     4      EXT_STMT                             
         5      ECHO                                 !0
   5     6      EXT_STMT                             
         7      ECHO                                 !1
   7     8      EXT_STMT                             
         9      ASSIGN                               !2, 'quotes'
   8    10      EXT_STMT                             
        11      CONCAT                       ~3      'Hello+', !2
        12      ASSIGN                               !3, ~3
   9    13      EXT_STMT                             
        14      CONCAT                       ~5      'Hello+', !2
        15      ASSIGN                               !4, ~5
  10    16      EXT_STMT                             
        17      ECHO                                 !3
  11    18      EXT_STMT                             
        19      ECHO                                 !4
  13    20      EXT_STMT                             
        21      ASSIGN                               !5, 'Hello'
  14    22      EXT_STMT                             
        23      CONCAT                       ~8      !5, '+quotes'
        24      ASSIGN                               !6, ~8
  15    25      EXT_STMT                             
        26      CONCAT                       ~10     !5, '+quotes'
        27      ASSIGN                               !7, ~10
  16    28      EXT_STMT                             
        29      ECHO                                 !6
  17    30      EXT_STMT                             
        31      ECHO                                 !7
  20    32      EXT_STMT                             
        33    > RETURN                               1

可以发现产生的opcode都是一样的,也就是单引号和双引号是一样的,但是我们改一下代码,把双引号的部分换一种写法,也就是把变量放在双引号里面,代码2:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
$single_str 'Hello quotes';
$double_str "Hello quotes";
echo $single_str;
echo $double_str;
 
$tail 'quotes';
$single_str_tail 'Hello '.$tail;
$double_str_tail "Hello $tail";
echo $single_str_tail;
echo $double_str_tail;
 
$head 'Hello';
$single_str_head $head.' quotes';
$double_str_head "$head quotes";
echo $single_str_head;
echo $double_str_head;
?>

产生的opcode如下:

line     # *  op              fetch   ext  return  operands
-------------------------------------------------------------
   2     0  >   EXT_STMT                             
         1      ASSIGN                               !0, 'Hello+quotes'
   3     2      EXT_STMT                             
         3      ASSIGN                               !1, 'Hello+quotes'
   4     4      EXT_STMT                             
         5      ECHO                                 !0
   5     6      EXT_STMT                             
         7      ECHO                                 !1
   7     8      EXT_STMT                             
         9      ASSIGN                               !2, 'quotes'
   8    10      EXT_STMT                             
        11      CONCAT                       ~3      'Hello+', !2
        12      ASSIGN                               !3, ~3
   9    13      EXT_STMT                             
        14      ADD_STRING                   ~5      'Hello+'
        15      ADD_VAR                      ~5      ~5, !2
        16      ASSIGN                               !4, ~5
  10    17      EXT_STMT                             
        18      ECHO                                 !3
  11    19      EXT_STMT                             
        20      ECHO                                 !4
  13    21      EXT_STMT                             
        22      ASSIGN                               !5, 'Hello'
  14    23      EXT_STMT                             
        24      CONCAT                       ~8      !5, '+quotes'
        25      ASSIGN                               !6, ~8
  15    26      EXT_STMT                             
        27      ADD_VAR                      ~10     !5
        28      ADD_STRING                   ~10     ~10, '+quotes'
        29      ASSIGN                               !7, ~10
  16    30      EXT_STMT                             
        31      ECHO                                 !6
  17    32      EXT_STMT                             
        33      ECHO                                 !7
  20    34      EXT_STMT                             
        35    > RETURN                               1

现在就能看出区别了,对比11,12以及14,15,16,或者24,25以及27,28,29,就可以发现使用单引号产生了CONCAT和ASSIGN两个动作,使用双引号产生了ADD_STRING,ADD_VAR,ASSIGN三个动作,貌似是单引号要快,因为少一个动作,但是我们还没测CONCAT 是否真的比ADD_STRING,ADD_VAR快,这个可以采用基准测试,或者直接查看对应的C函数代码比较得知。

 

总结: 采用代码1中的写法,单引号和双引号的作用是一样的,速度自然也是一样的,采用代码2中的写法,单引号和双引号产生的opcode不一样,虽然多双引号的opcode多一个动作,但是单从opcode还是无法判断单引号要比双引号要快,如果是这样的话后面还是需要其他的测试。

还有一种造成速度差异的是在分析语法和解释阶段,我看过的那片文章提到了这样一句话:"至于编译阶段,双引号和单引号的区别也是很大的, 我就举个数字来说明: 在scanning阶段, 对于双引号的词法规则有14条,而对于单引号,仅仅只有6条。"

如果是这样的话,说明双引号在解释编译阶段要慢,那是不是可以通过opcode缓存来消除差异?