technology-blog icon indicating copy to clipboard operation
technology-blog copied to clipboard

第 19 题:(开放题)a.b.c.d和a['b']['c']['d'],哪个性能更高

Open airuikun opened this issue 5 years ago • 32 comments

别看这题,题目上每个字都能看懂,但是里面涉及到的知识,暗藏杀鸡

这题要往深处走,会涉及ast抽象语法树、编译原理、v8内核对原生js实现问题

我觉得这个题是这篇文章里最难的一道题,所以我放在了开放题中的最后一题

大家多多思考,这题的答案也会在周五公

airuikun avatar Apr 14 '19 08:04 airuikun

从性能的角度会有区别吗?最终都需要通过索引去访问吧,而不是在编译阶段就确定好 offset? opcode 不知道会不会有区别?

qile222 avatar Apr 15 '19 09:04 qile222

从知乎看到这个问题,觉得很有意思,本人对v8了解也不是很深,跳过来插个眼,简单写了下a.b.c.d v8的实现:

#include <node.h>

namespace __objectTest__ {
    using v8::Isolate;
    using v8::HandleScope;
    using v8::Context;
    using v8::MaybeLocal;
    using v8::Local;
    using v8::Object;
    using v8::String;
    using v8::Number;
    using v8::Value;

    Local<Object> createD(Isolate* isolate) {
        Local<Context> context = isolate->GetCurrentContext();
        Local<Object> objD = Object::New(isolate);
        MaybeLocal<String> mkey = String::NewFromUtf8(isolate, "d");
        Local<String> key = mkey.ToLocalChecked();
        Local<Number> val = Number::New(isolate, 23333);
        objD->Set(context, key, val).FromJust();
        return objD;
    }

    Local<Object> createC(Isolate* isolate) {
        Local<Context> context = isolate->GetCurrentContext();
        Local<Object> objC = Object::New(isolate);
        MaybeLocal<String> mkey = String::NewFromUtf8(isolate, "c");
        Local<String> key = mkey.ToLocalChecked();
        Local<Object> val = createD(isolate);
        objC->Set(context, key, val).FromJust();
        return objC;
    }

    void Init(Local<Object> exports) {
        Isolate* isolate = Isolate::GetCurrent();
        HandleScope scope(isolate);
        Local<Context> context = isolate->GetCurrentContext();
        Local<Object> objA = Object::New(isolate);
        MaybeLocal<String> mkey = String::NewFromUtf8(isolate, "b");
        Local<String> key = mkey.ToLocalChecked();
        Local<Object> val = createC(isolate);
        objA->Set(context, key, val).FromJust();
        exports->Set(String::NewFromUtf8(isolate, "a"), objA);
        Local<Value> finalValB = objA->Get(context, key).ToLocalChecked();
        Local<Object> oFinalValB = finalValB->ToObject(context).ToLocalChecked();
        Local<Value> finalValC = oFinalValB->Get(context, String::NewFromUtf8(isolate, "c")).ToLocalChecked();
        Local<Object> oFinalValC = finalValC->ToObject(context).ToLocalChecked();
        Local<Value> sFinalVal = oFinalValC->Get(context, String::NewFromUtf8(isolate, "d")).ToLocalChecked();
        Local<Number> finalVal = sFinalVal->ToNumber(context).ToLocalChecked();
        exports->Set(String::NewFromUtf8(isolate, "val"), finalVal);

    }
    NODE_MODULE(objectTest, Init)
}

benchmark代码:

'use strict'

const test = require('./object/build/Release/object.node')

const Benchmark = require('benchmark')

let a = test.a

console.log(a.b.c.d, a['b']['c']['d'], test.val)
let suite = new Benchmark.Suite

suite.add('obj', function () {
 a.b.c.d === 23333
}).add('string', function() {
    a['b']['c']['d'] = 23333
}).add('c ++', function() {
    test.val === 23333
}).on('cycle', function(event) {
  console.log(String(event.target));
}).on('complete', function() {
  console.log('Fastest is ' + this.filter('fastest').map('name'));
}).run()

benchmark结果基本都比较恒定:

$ node bench.js
$ 23333 23333 23333
$ 
$ obj x 491,218,774 ops/sec ±0.88% (85 runs sampled)
$ string x 430,301,317 ops/sec ±2.19% (86 runs sampled)
$ c ++ x 514,275,549 ops/sec ±0.78% (87 runs sampled)
$ Fastest is c ++

benchmark结果是a.b.c.d效率高。难道是因为在v8中a['b']['c']['d']不能用数组下标obj->Get(context, index)进行查找,而强制转成了obj->Get(context, key)么。。。

xtx1130 avatar Apr 15 '19 12:04 xtx1130

插个眼

lufeing avatar Apr 16 '19 01:04 lufeing

从知乎看到这个问题,觉得很有意思,本人对v8了解也不是很深,跳过来插个眼,简单写了下a.b.c.d v8的实现:

#include <node.h>

namespace __objectTest__ {
    using v8::Isolate;
    using v8::HandleScope;
    using v8::Context;
    using v8::MaybeLocal;
    using v8::Local;
    using v8::Object;
    using v8::String;
    using v8::Number;
    using v8::Value;

    Local<Object> createD(Isolate* isolate) {
        Local<Context> context = isolate->GetCurrentContext();
        Local<Object> objD = Object::New(isolate);
        MaybeLocal<String> mkey = String::NewFromUtf8(isolate, "d");
        Local<String> key = mkey.ToLocalChecked();
        Local<Number> val = Number::New(isolate, 23333);
        objD->Set(context, key, val).FromJust();
        return objD;
    }

    Local<Object> createC(Isolate* isolate) {
        Local<Context> context = isolate->GetCurrentContext();
        Local<Object> objC = Object::New(isolate);
        MaybeLocal<String> mkey = String::NewFromUtf8(isolate, "c");
        Local<String> key = mkey.ToLocalChecked();
        Local<Object> val = createD(isolate);
        objC->Set(context, key, val).FromJust();
        return objC;
    }

    void Init(Local<Object> exports) {
        Isolate* isolate = Isolate::GetCurrent();
        HandleScope scope(isolate);
        Local<Context> context = isolate->GetCurrentContext();
        Local<Object> objA = Object::New(isolate);
        MaybeLocal<String> mkey = String::NewFromUtf8(isolate, "b");
        Local<String> key = mkey.ToLocalChecked();
        Local<Object> val = createC(isolate);
        objA->Set(context, key, val).FromJust();
        exports->Set(String::NewFromUtf8(isolate, "a"), objA);
        Local<Value> finalValB = objA->Get(context, key).ToLocalChecked();
        Local<Object> oFinalValB = finalValB->ToObject(context).ToLocalChecked();
        Local<Value> finalValC = oFinalValB->Get(context, String::NewFromUtf8(isolate, "c")).ToLocalChecked();
        Local<Object> oFinalValC = finalValC->ToObject(context).ToLocalChecked();
        Local<Value> sFinalVal = oFinalValC->Get(context, String::NewFromUtf8(isolate, "d")).ToLocalChecked();
        Local<Number> finalVal = sFinalVal->ToNumber(context).ToLocalChecked();
        exports->Set(String::NewFromUtf8(isolate, "val"), finalVal);

    }
    NODE_MODULE(objectTest, Init)
}

benchmark代码:

'use strict'

const test = require('./object/build/Release/object.node')

const Benchmark = require('benchmark')

let a = test.a

console.log(a.b.c.d, a['b']['c']['d'], test.val)
let suite = new Benchmark.Suite

suite.add('obj', function () {
 a.b.c.d === 23333
}).add('string', function() {
    a['b']['c']['d'] = 23333
}).add('c ++', function() {
    test.val === 23333
}).on('cycle', function(event) {
  console.log(String(event.target));
}).on('complete', function() {
  console.log('Fastest is ' + this.filter('fastest').map('name'));
}).run()

benchmark结果基本都比较恒定:

$ node bench.js
$ 23333 23333 23333
$ 
$ obj x 491,218,774 ops/sec ±0.88% (85 runs sampled)
$ string x 430,301,317 ops/sec ±2.19% (86 runs sampled)
$ c ++ x 514,275,549 ops/sec ±0.78% (87 runs sampled)
$ Fastest is c ++

benchmark结果是a.b.c.d效率高。难道是因为在v8中a['b']['c']['d']不能用数组下标obj->Get(context, index)进行查找,而强制转成了obj->Get(context, key)么。。。

厉害了,这个题如果对v8不是很清楚确实回答不上来,比如我这种菜鸟级别的。。。

jinggk avatar Apr 16 '19 01:04 jinggk

卧槽 你们这么秀 我觉得我不适合写代码了!!!

belief-cyf avatar Apr 16 '19 08:04 belief-cyf

这个题从AST角度看就很简单了,a['b']['c']和a.b.c,转换成AST前者的的树是含计算的,后者只是string literal,天然前者会消耗更多的计算成本,时间也更长

slashhuang avatar Apr 16 '19 08:04 slashhuang

这个题用 byte code 看就很明了了,以 Jerryscript 为例(熟悉 V8 的同学可以把下面的代码编译成 V8 的字节码), 参考下面这段代码

  var a = { b: { c: { d: 1 } } };
  var v1 = a.b.c.d
  var v2 = a['b']['c']['d']

对应的字节码如下:


   0 : CBC_CREATE_OBJECT
   1 : CBC_CREATE_OBJECT
   2 : CBC_CREATE_OBJECT
   3 : CBC_PUSH_NUMBER_POS_BYTE number:1
   5 : CBC_SET_PROPERTY idx:11(lit)->string(d)
   7 : CBC_SET_PROPERTY idx:10(lit)->string(c)
   9 : CBC_SET_PROPERTY idx:9(lit)->string(b)
  11 : CBC_ASSIGN_SET_IDENT idx:6(reg)->var_ident(a)
  13 : CBC_PUSH_PROP_LITERAL_LITERAL idx:6(reg)->var_ident(a) idx:9(lit)->string(b)
  16 : CBC_PUSH_PROP_LITERAL idx:10(lit)->string(c)
  18 : CBC_PUSH_PROP_LITERAL idx:11(lit)->string(d)
  20 : CBC_ASSIGN_SET_IDENT idx:7(reg)->var_ident(v1)
  22 : CBC_PUSH_PROP_LITERAL_LITERAL idx:6(reg)->var_ident(a) idx:9(lit)->string(b)
  25 : CBC_PUSH_PROP_LITERAL idx:10(lit)->string(c)
  27 : CBC_PUSH_PROP_LITERAL idx:11(lit)->string(d)
  29 : CBC_ASSIGN_SET_IDENT idx:8(reg)->var_ident(v2)
  31 : CBC_RETURN_WITH_BLOCK

0-2 行创建了3个对象,3行设置了 d 的值为 1,5-9行设置了 property,11行将对象赋值 a,13-20行 赋值 v1,22-29行赋值 v2 所以可以看到两种方式在 Jerryscript 中都是以字面量的方式进行访问的,流程也是一样的,编译过程可能会有一些区别,具体还要看 vm 代码

qile222 avatar Apr 16 '19 09:04 qile222

假如通过变量访问

  var a = { b: { c: { d: 1 } } };
  var v1 = a.b.c.d
  var k1 = 'd'
  var v2 = a['b']['c'][k1]

字节码如下

   0 : CBC_CREATE_OBJECT
   1 : CBC_CREATE_OBJECT
   2 : CBC_CREATE_OBJECT
   3 : CBC_PUSH_NUMBER_POS_BYTE number:1
   5 : CBC_SET_PROPERTY idx:12(lit)->string(d)
   7 : CBC_SET_PROPERTY idx:11(lit)->string(c)
   9 : CBC_SET_PROPERTY idx:10(lit)->string(b)
  11 : CBC_ASSIGN_SET_IDENT idx:6(reg)->var_ident(a)
  13 : CBC_PUSH_PROP_LITERAL_LITERAL idx:6(reg)->var_ident(a) idx:10(lit)->string(b)
  16 : CBC_PUSH_PROP_LITERAL idx:11(lit)->string(c)
  18 : CBC_PUSH_PROP_LITERAL idx:12(lit)->string(d)
  20 : CBC_ASSIGN_SET_IDENT idx:7(reg)->var_ident(v1)
  22 : CBC_ASSIGN_LITERAL_SET_IDENT idx:12(lit)->string(d) idx:8(reg)->var_ident(k1)
  25 : CBC_PUSH_PROP_LITERAL_LITERAL idx:6(reg)->var_ident(a) idx:10(lit)->string(b)
  28 : CBC_PUSH_PROP_LITERAL idx:11(lit)->string(c)
  30 : CBC_PUSH_PROP_LITERAL idx:8(reg)->var_ident(k1)
  32 : CBC_ASSIGN_SET_IDENT idx:9(reg)->var_ident(v2)
  34 : CBC_RETURN_WITH_BLOCK

变化其实不大,只有2点变化:

  1. 新增了 22 行的赋值k1='d'
  2. 30行通过变量去访问 property,之前是通过字面量

所以这两种方式在运行时性能上没有区别(基于 jerry)

qile222 avatar Apr 16 '19 09:04 qile222

@lolBig 兄弟跳槽吗

slashhuang avatar Apr 16 '19 09:04 slashhuang

  1. 对应于a['b']['c']['d']上来说,得到的ast如下:
{
  "type": "Program",
  "start": 0,
  "end": 16,
  "body": [
    {
      "type": "ExpressionStatement",
      "start": 0,
      "end": 16,
      "expression": {
        "type": "MemberExpression",
        "start": 0,
        "end": 16,
        "object": {
          "type": "MemberExpression",
          "start": 0,
          "end": 11,
          "object": {
            "type": "MemberExpression",
            "start": 0,
            "end": 6,
            "object": {
              "type": "Identifier",
              "start": 0,
              "end": 1,
              "name": "a"
            },
            "property": {
              "type": "Literal",
              "start": 2,
              "end": 5,
              "value": "b",
              "raw": "'b'"
            },
            "computed": true
          },
          "property": {
            "type": "Literal",
            "start": 7,
            "end": 10,
            "value": "c",
            "raw": "'c'"
          },
          "computed": true
        },
        "property": {
          "type": "Literal",
          "start": 12,
          "end": 15,
          "value": "d",
          "raw": "'d'"
        },
        "computed": true
      }
    }
  ],
  "sourceType": "module"
}

对应于a.b.c.d来说,得到的ast如下:

{
  "type": "Program",
  "start": 0,
  "end": 7,
  "body": [
    {
      "type": "ExpressionStatement",
      "start": 0,
      "end": 7,
      "expression": {
        "type": "MemberExpression",
        "start": 0,
        "end": 7,
        "object": {
          "type": "MemberExpression",
          "start": 0,
          "end": 5,
          "object": {
            "type": "MemberExpression",
            "start": 0,
            "end": 3,
            "object": {
              "type": "Identifier",
              "start": 0,
              "end": 1,
              "name": "a"
            },
            "property": {
              "type": "Identifier",
              "start": 2,
              "end": 3,
              "name": "b"
            },
            "computed": false
          },
          "property": {
            "type": "Identifier",
            "start": 4,
            "end": 5,
            "name": "c"
          },
          "computed": false
        },
        "property": {
          "type": "Identifier",
          "start": 6,
          "end": 7,
          "name": "d"
        },
        "computed": false
      }
    }
  ],
  "sourceType": "module"
}

如果是按照直接解释运行的话,无非就是a['b']的方式多了一个Literal的转换,实际上是基本没有什么区别。

ErosZy avatar Apr 16 '19 10:04 ErosZy

按照V8 ByteCode考虑,a.b.c.d的流程如下:

> console.log(aaa.b.c.d)
[generated bytecode for function: ]
Parameter count 1
Frame size 48
    0 E> 0x2bd62d9eb1ba @    0 : a1                StackCheck
    0 S> 0x2bd62d9eb1bb @    1 : 13 00 00          LdaGlobal [0], [0]
         0x2bd62d9eb1be @    4 : 26 f9             Star r2
         0x2bd62d9eb1c0 @    6 : 12 01             LdaConstant [1]
         0x2bd62d9eb1c2 @    8 : 26 f7             Star r4
         0x2bd62d9eb1c4 @   10 : 27 f9 f8          Mov r2, r3
    8 E> 0x2bd62d9eb1c7 @   13 : 61 0c f8 02       InvokeIntrinsic [_GetProperty], r3-r4
         0x2bd62d9eb1cb @   17 : 26 fa             Star r1
   12 E> 0x2bd62d9eb1cd @   19 : 13 02 02          LdaGlobal [2], [2]
         0x2bd62d9eb1d0 @   22 : 26 f8             Star r3
         0x2bd62d9eb1d2 @   24 : 12 03             LdaConstant [3]
         0x2bd62d9eb1d4 @   26 : 26 f6             Star r5
         0x2bd62d9eb1d6 @   28 : 27 f8 f7          Mov r3, r4
   16 E> 0x2bd62d9eb1d9 @   31 : 61 0c f7 02       InvokeIntrinsic [_GetProperty], r4-r5
         0x2bd62d9eb1dd @   35 : 26 f8             Star r3
         0x2bd62d9eb1df @   37 : 12 04             LdaConstant [4]
         0x2bd62d9eb1e1 @   39 : 26 f6             Star r5
         0x2bd62d9eb1e3 @   41 : 27 f8 f7          Mov r3, r4
   18 E> 0x2bd62d9eb1e6 @   44 : 61 0c f7 02       InvokeIntrinsic [_GetProperty], r4-r5
         0x2bd62d9eb1ea @   48 : 26 f8             Star r3
         0x2bd62d9eb1ec @   50 : 12 05             LdaConstant [5]
         0x2bd62d9eb1ee @   52 : 26 f6             Star r5
         0x2bd62d9eb1f0 @   54 : 27 f8 f7          Mov r3, r4
   20 E> 0x2bd62d9eb1f3 @   57 : 61 0c f7 02       InvokeIntrinsic [_GetProperty], r4-r5
         0x2bd62d9eb1f7 @   61 : 26 f8             Star r3
    8 E> 0x2bd62d9eb1f9 @   63 : 57 fa f9 f8 04    CallProperty1 r1, r2, r3, [4]
         0x2bd62d9eb1fe @   68 : 26 fb             Star r0
   23 S> 0x2bd62d9eb200 @   70 : a5                Return
Constant pool (size = 6)
Handler Table (size = 0)
Thrown:

a['b']['c']['d']的ByteCode是这个

> console.log(aa['b']['c']['d'])
[generated bytecode for function: ]
Parameter count 1
Frame size 48
    0 E> 0x2bd62d9eb532 @    0 : a1                StackCheck
    0 S> 0x2bd62d9eb533 @    1 : 13 00 00          LdaGlobal [0], [0]
         0x2bd62d9eb536 @    4 : 26 f9             Star r2
         0x2bd62d9eb538 @    6 : 12 01             LdaConstant [1]
         0x2bd62d9eb53a @    8 : 26 f7             Star r4
         0x2bd62d9eb53c @   10 : 27 f9 f8          Mov r2, r3
    8 E> 0x2bd62d9eb53f @   13 : 61 0c f8 02       InvokeIntrinsic [_GetProperty], r3-r4
         0x2bd62d9eb543 @   17 : 26 fa             Star r1
   12 E> 0x2bd62d9eb545 @   19 : 13 02 02          LdaGlobal [2], [2]
         0x2bd62d9eb548 @   22 : 26 f8             Star r3
         0x2bd62d9eb54a @   24 : 12 03             LdaConstant [3]
         0x2bd62d9eb54c @   26 : 26 f6             Star r5
         0x2bd62d9eb54e @   28 : 27 f8 f7          Mov r3, r4
   14 E> 0x2bd62d9eb551 @   31 : 61 0c f7 02       InvokeIntrinsic [_GetProperty], r4-r5
         0x2bd62d9eb555 @   35 : 26 f8             Star r3
         0x2bd62d9eb557 @   37 : 12 04             LdaConstant [4]
         0x2bd62d9eb559 @   39 : 26 f6             Star r5
         0x2bd62d9eb55b @   41 : 27 f8 f7          Mov r3, r4
   19 E> 0x2bd62d9eb55e @   44 : 61 0c f7 02       InvokeIntrinsic [_GetProperty], r4-r5
         0x2bd62d9eb562 @   48 : 26 f8             Star r3
         0x2bd62d9eb564 @   50 : 12 05             LdaConstant [5]
         0x2bd62d9eb566 @   52 : 26 f6             Star r5
         0x2bd62d9eb568 @   54 : 27 f8 f7          Mov r3, r4
   24 E> 0x2bd62d9eb56b @   57 : 61 0c f7 02       InvokeIntrinsic [_GetProperty], r4-r5
         0x2bd62d9eb56f @   61 : 26 f8             Star r3
    8 E> 0x2bd62d9eb571 @   63 : 57 fa f9 f8 04    CallProperty1 r1, r2, r3, [4]
         0x2bd62d9eb576 @   68 : 26 fb             Star r0
   31 S> 0x2bd62d9eb578 @   70 : a5                Return
Constant pool (size = 6)
Handler Table (size = 0)
Thrown:

事实证明,根本没有任何区别。硬要说有区别,可能就是后者AST会大一些,但在AST解析上消耗的这点时间基本可以忽略不计。这些字节码的[4][5]来源于Constant Pool,这需要开启V8的一个编译flag才会被显示出来;手头现在没有V8,就不扩展了。

当然,SpiderMonkey / Chakra 可能有其他的方式。

顺手jsperf了一下,基本可以认为性能相等吧,没啥参考价值: image image

不如说,编译器/运行时开发者早就把我们能想到的坑都踩了。动态还好说,这种静态字面量的就算有不同也早该被优化了。这种死扣细节的题大概没啥价值。

zsxsoft avatar Apr 16 '19 15:04 zsxsoft

[北京的月亮和上海的月亮, 哪个更圆?]

别看这题,题目上每个字都能看懂,但是里面涉及到的知识,暗藏杀鸡 这题要往深处走,会涉及天文学、气象学、地球经纬设计等底层知识

chenzeZzz avatar Apr 16 '19 17:04 chenzeZzz

[北京的月亮和上海的月亮, 哪个更圆?]

别看这题,题目上每个字都能看懂,但是里面涉及到的知识,暗藏杀鸡 这题要往深处走,会涉及天文学、气象学、地球经纬设计等底层知识

老哥稳

wang125309 avatar Apr 17 '19 06:04 wang125309

{0: 10, b: 11, b-c: 20}

lizhongyi avatar Apr 18 '19 11:04 lizhongyi

[北京的月亮和上海的月亮, 哪个更圆?]

别看这题,题目上每个字都能看懂,但是里面涉及到的知识,暗藏杀鸡 这题要往深处走,会涉及天文学、气象学、地球经纬设计等底层知识

可以说很形象了!点赞

FinchJohnRen avatar Apr 19 '19 09:04 FinchJohnRen

a.b 和 a['a_b_c_d_e_f'.split('_').join('').charAt(1)] 的区别吧

Xheldon avatar Apr 22 '19 04:04 Xheldon

@lolBig 兄弟跳槽吗

黄老湿!!!我居然在这里见到你了!!!黄老湿好!!!

CoderMageFox avatar Apr 29 '19 07:04 CoderMageFox

@CoderMageFox 你好呀,给你送个火箭,哈哈

slashhuang avatar Apr 29 '19 07:04 slashhuang

造火箭吧?

yangbean4 avatar Apr 30 '19 02:04 yangbean4

按照上面大佬们的解答,这两种写法没有性能差异呢?

jjeejj avatar Apr 30 '19 06:04 jjeejj

按照上面大佬们的解答,这两种写法没有性能差异呢?

基本无差异,dot可能好点,但是这点性能可以忽略

AlenQi avatar Apr 30 '19 08:04 AlenQi

按照V8 ByteCode考虑,a.b.c.d的流程如下:

> console.log(aaa.b.c.d)
[generated bytecode for function: ]
Parameter count 1
Frame size 48
    0 E> 0x2bd62d9eb1ba @    0 : a1                StackCheck
    0 S> 0x2bd62d9eb1bb @    1 : 13 00 00          LdaGlobal [0], [0]
         0x2bd62d9eb1be @    4 : 26 f9             Star r2
         0x2bd62d9eb1c0 @    6 : 12 01             LdaConstant [1]
         0x2bd62d9eb1c2 @    8 : 26 f7             Star r4
         0x2bd62d9eb1c4 @   10 : 27 f9 f8          Mov r2, r3
    8 E> 0x2bd62d9eb1c7 @   13 : 61 0c f8 02       InvokeIntrinsic [_GetProperty], r3-r4
         0x2bd62d9eb1cb @   17 : 26 fa             Star r1
   12 E> 0x2bd62d9eb1cd @   19 : 13 02 02          LdaGlobal [2], [2]
         0x2bd62d9eb1d0 @   22 : 26 f8             Star r3
         0x2bd62d9eb1d2 @   24 : 12 03             LdaConstant [3]
         0x2bd62d9eb1d4 @   26 : 26 f6             Star r5
         0x2bd62d9eb1d6 @   28 : 27 f8 f7          Mov r3, r4
   16 E> 0x2bd62d9eb1d9 @   31 : 61 0c f7 02       InvokeIntrinsic [_GetProperty], r4-r5
         0x2bd62d9eb1dd @   35 : 26 f8             Star r3
         0x2bd62d9eb1df @   37 : 12 04             LdaConstant [4]
         0x2bd62d9eb1e1 @   39 : 26 f6             Star r5
         0x2bd62d9eb1e3 @   41 : 27 f8 f7          Mov r3, r4
   18 E> 0x2bd62d9eb1e6 @   44 : 61 0c f7 02       InvokeIntrinsic [_GetProperty], r4-r5
         0x2bd62d9eb1ea @   48 : 26 f8             Star r3
         0x2bd62d9eb1ec @   50 : 12 05             LdaConstant [5]
         0x2bd62d9eb1ee @   52 : 26 f6             Star r5
         0x2bd62d9eb1f0 @   54 : 27 f8 f7          Mov r3, r4
   20 E> 0x2bd62d9eb1f3 @   57 : 61 0c f7 02       InvokeIntrinsic [_GetProperty], r4-r5
         0x2bd62d9eb1f7 @   61 : 26 f8             Star r3
    8 E> 0x2bd62d9eb1f9 @   63 : 57 fa f9 f8 04    CallProperty1 r1, r2, r3, [4]
         0x2bd62d9eb1fe @   68 : 26 fb             Star r0
   23 S> 0x2bd62d9eb200 @   70 : a5                Return
Constant pool (size = 6)
Handler Table (size = 0)
Thrown:

a['b']['c']['d']的ByteCode是这个

> console.log(aa['b']['c']['d'])
[generated bytecode for function: ]
Parameter count 1
Frame size 48
    0 E> 0x2bd62d9eb532 @    0 : a1                StackCheck
    0 S> 0x2bd62d9eb533 @    1 : 13 00 00          LdaGlobal [0], [0]
         0x2bd62d9eb536 @    4 : 26 f9             Star r2
         0x2bd62d9eb538 @    6 : 12 01             LdaConstant [1]
         0x2bd62d9eb53a @    8 : 26 f7             Star r4
         0x2bd62d9eb53c @   10 : 27 f9 f8          Mov r2, r3
    8 E> 0x2bd62d9eb53f @   13 : 61 0c f8 02       InvokeIntrinsic [_GetProperty], r3-r4
         0x2bd62d9eb543 @   17 : 26 fa             Star r1
   12 E> 0x2bd62d9eb545 @   19 : 13 02 02          LdaGlobal [2], [2]
         0x2bd62d9eb548 @   22 : 26 f8             Star r3
         0x2bd62d9eb54a @   24 : 12 03             LdaConstant [3]
         0x2bd62d9eb54c @   26 : 26 f6             Star r5
         0x2bd62d9eb54e @   28 : 27 f8 f7          Mov r3, r4
   14 E> 0x2bd62d9eb551 @   31 : 61 0c f7 02       InvokeIntrinsic [_GetProperty], r4-r5
         0x2bd62d9eb555 @   35 : 26 f8             Star r3
         0x2bd62d9eb557 @   37 : 12 04             LdaConstant [4]
         0x2bd62d9eb559 @   39 : 26 f6             Star r5
         0x2bd62d9eb55b @   41 : 27 f8 f7          Mov r3, r4
   19 E> 0x2bd62d9eb55e @   44 : 61 0c f7 02       InvokeIntrinsic [_GetProperty], r4-r5
         0x2bd62d9eb562 @   48 : 26 f8             Star r3
         0x2bd62d9eb564 @   50 : 12 05             LdaConstant [5]
         0x2bd62d9eb566 @   52 : 26 f6             Star r5
         0x2bd62d9eb568 @   54 : 27 f8 f7          Mov r3, r4
   24 E> 0x2bd62d9eb56b @   57 : 61 0c f7 02       InvokeIntrinsic [_GetProperty], r4-r5
         0x2bd62d9eb56f @   61 : 26 f8             Star r3
    8 E> 0x2bd62d9eb571 @   63 : 57 fa f9 f8 04    CallProperty1 r1, r2, r3, [4]
         0x2bd62d9eb576 @   68 : 26 fb             Star r0
   31 S> 0x2bd62d9eb578 @   70 : a5                Return
Constant pool (size = 6)
Handler Table (size = 0)
Thrown:

事实证明,根本没有任何区别。硬要说有区别,可能就是后者AST会大一些,但在AST解析上消耗的这点时间基本可以忽略不计。这些字节码的[4][5]来源于Constant Pool,这需要开启V8的一个编译flag才会被显示出来;手头现在没有V8,就不扩展了。

当然,SpiderMonkey / Chakra 可能有其他的方式。

顺手jsperf了一下,基本可以认为性能相等吧,没啥参考价值: image image

不如说,编译器/运行时开发者早就把我们能想到的坑都踩了。动态还好说,这种静态字面量的就算有不同也早该被优化了。这种死扣细节的题大概没啥价值。

你好,请问通过什么方式可以查看js编译后的字节码,搜了下,没找到相关资料,希望给点提示,谢谢

uninge avatar May 09 '19 09:05 uninge

作者咋不公布答案了?? 有什么区别?

MeCKodo avatar May 14 '19 11:05 MeCKodo

mark

iaong avatar May 27 '19 17:05 iaong

这知识它进不去脑子啊(ಥ_ಥ)

LoCielsys avatar May 28 '19 08:05 LoCielsys

看不懂 mark

RomtisonKao avatar Jun 14 '19 02:06 RomtisonKao

插眼,没看懂到底哪个更快

liujianwen-github avatar Aug 06 '19 02:08 liujianwen-github

看得我眼花缭乱,简直神仙过招啊

win7killer avatar Aug 15 '19 15:08 win7killer

@lolBig 厉害

pandly avatar Aug 20 '19 09:08 pandly

按照V8 ByteCode考虑,a.b.c.d的流程如下:

> console.log(aaa.b.c.d)
[generated bytecode for function: ]
Parameter count 1
Frame size 48
    0 E> 0x2bd62d9eb1ba @    0 : a1                StackCheck
    0 S> 0x2bd62d9eb1bb @    1 : 13 00 00          LdaGlobal [0], [0]
         0x2bd62d9eb1be @    4 : 26 f9             Star r2
         0x2bd62d9eb1c0 @    6 : 12 01             LdaConstant [1]
         0x2bd62d9eb1c2 @    8 : 26 f7             Star r4
         0x2bd62d9eb1c4 @   10 : 27 f9 f8          Mov r2, r3
    8 E> 0x2bd62d9eb1c7 @   13 : 61 0c f8 02       InvokeIntrinsic [_GetProperty], r3-r4
         0x2bd62d9eb1cb @   17 : 26 fa             Star r1
   12 E> 0x2bd62d9eb1cd @   19 : 13 02 02          LdaGlobal [2], [2]
         0x2bd62d9eb1d0 @   22 : 26 f8             Star r3
         0x2bd62d9eb1d2 @   24 : 12 03             LdaConstant [3]
         0x2bd62d9eb1d4 @   26 : 26 f6             Star r5
         0x2bd62d9eb1d6 @   28 : 27 f8 f7          Mov r3, r4
   16 E> 0x2bd62d9eb1d9 @   31 : 61 0c f7 02       InvokeIntrinsic [_GetProperty], r4-r5
         0x2bd62d9eb1dd @   35 : 26 f8             Star r3
         0x2bd62d9eb1df @   37 : 12 04             LdaConstant [4]
         0x2bd62d9eb1e1 @   39 : 26 f6             Star r5
         0x2bd62d9eb1e3 @   41 : 27 f8 f7          Mov r3, r4
   18 E> 0x2bd62d9eb1e6 @   44 : 61 0c f7 02       InvokeIntrinsic [_GetProperty], r4-r5
         0x2bd62d9eb1ea @   48 : 26 f8             Star r3
         0x2bd62d9eb1ec @   50 : 12 05             LdaConstant [5]
         0x2bd62d9eb1ee @   52 : 26 f6             Star r5
         0x2bd62d9eb1f0 @   54 : 27 f8 f7          Mov r3, r4
   20 E> 0x2bd62d9eb1f3 @   57 : 61 0c f7 02       InvokeIntrinsic [_GetProperty], r4-r5
         0x2bd62d9eb1f7 @   61 : 26 f8             Star r3
    8 E> 0x2bd62d9eb1f9 @   63 : 57 fa f9 f8 04    CallProperty1 r1, r2, r3, [4]
         0x2bd62d9eb1fe @   68 : 26 fb             Star r0
   23 S> 0x2bd62d9eb200 @   70 : a5                Return
Constant pool (size = 6)
Handler Table (size = 0)
Thrown:

a['b']['c']['d']的ByteCode是这个

> console.log(aa['b']['c']['d'])
[generated bytecode for function: ]
Parameter count 1
Frame size 48
    0 E> 0x2bd62d9eb532 @    0 : a1                StackCheck
    0 S> 0x2bd62d9eb533 @    1 : 13 00 00          LdaGlobal [0], [0]
         0x2bd62d9eb536 @    4 : 26 f9             Star r2
         0x2bd62d9eb538 @    6 : 12 01             LdaConstant [1]
         0x2bd62d9eb53a @    8 : 26 f7             Star r4
         0x2bd62d9eb53c @   10 : 27 f9 f8          Mov r2, r3
    8 E> 0x2bd62d9eb53f @   13 : 61 0c f8 02       InvokeIntrinsic [_GetProperty], r3-r4
         0x2bd62d9eb543 @   17 : 26 fa             Star r1
   12 E> 0x2bd62d9eb545 @   19 : 13 02 02          LdaGlobal [2], [2]
         0x2bd62d9eb548 @   22 : 26 f8             Star r3
         0x2bd62d9eb54a @   24 : 12 03             LdaConstant [3]
         0x2bd62d9eb54c @   26 : 26 f6             Star r5
         0x2bd62d9eb54e @   28 : 27 f8 f7          Mov r3, r4
   14 E> 0x2bd62d9eb551 @   31 : 61 0c f7 02       InvokeIntrinsic [_GetProperty], r4-r5
         0x2bd62d9eb555 @   35 : 26 f8             Star r3
         0x2bd62d9eb557 @   37 : 12 04             LdaConstant [4]
         0x2bd62d9eb559 @   39 : 26 f6             Star r5
         0x2bd62d9eb55b @   41 : 27 f8 f7          Mov r3, r4
   19 E> 0x2bd62d9eb55e @   44 : 61 0c f7 02       InvokeIntrinsic [_GetProperty], r4-r5
         0x2bd62d9eb562 @   48 : 26 f8             Star r3
         0x2bd62d9eb564 @   50 : 12 05             LdaConstant [5]
         0x2bd62d9eb566 @   52 : 26 f6             Star r5
         0x2bd62d9eb568 @   54 : 27 f8 f7          Mov r3, r4
   24 E> 0x2bd62d9eb56b @   57 : 61 0c f7 02       InvokeIntrinsic [_GetProperty], r4-r5
         0x2bd62d9eb56f @   61 : 26 f8             Star r3
    8 E> 0x2bd62d9eb571 @   63 : 57 fa f9 f8 04    CallProperty1 r1, r2, r3, [4]
         0x2bd62d9eb576 @   68 : 26 fb             Star r0
   31 S> 0x2bd62d9eb578 @   70 : a5                Return
Constant pool (size = 6)
Handler Table (size = 0)
Thrown:

事实证明,根本没有任何区别。硬要说有区别,可能就是后者AST会大一些,但在AST解析上消耗的这点时间基本可以忽略不计。这些字节码的[4][5]来源于Constant Pool,这需要开启V8的一个编译flag才会被显示出来;手头现在没有V8,就不扩展了。 当然,SpiderMonkey / Chakra 可能有其他的方式。 顺手jsperf了一下,基本可以认为性能相等吧,没啥参考价值: image image 不如说,编译器/运行时开发者早就把我们能想到的坑都踩了。动态还好说,这种静态字面量的就算有不同也早该被优化了。这种死扣细节的题大概没啥价值。

你好,请问通过什么方式可以查看js编译后的字节码,搜了下,没找到相关资料,希望给点提示,谢谢

node 的命令: node --print-bytecode

ytftianwen avatar Sep 15 '19 07:09 ytftianwen