神之门V8-----Event loop的舞池盛宴(2)

在上一篇文章中,我们分析了node的EventEmitter与EventLoop的关系,结论就是它们真的没多大关系,代码表现出异步只是emit函数调用的时机被我们手动设定在eventloop里面。

OK,跟着独操引擎的男人继续我们的代码之旅~

这里写图片描述

JS对象与C++ Runtime 代码

这一部分涉及到V8引擎的代码优化,对这个不感兴趣的伙伴可以略过这一部分,直接看后面的loop分析,个人觉得理解了function在引擎的表示有助于理解eventloop的真实面目。

之前的v8系列文章一经介绍过HandlePersistent这两个模板类,分析了V8的内存管理以及GC的回收算法,JS的源代码经过v8 Full Complier 的前端编译生成AST,然后代码生成开始做一些代码优化,JS的对象模型我会在另一篇文章进行更详细的分析,这里主要介绍内联缓存(Inline Cache 简称 IC),为什么会有IC呢,其实是一种迭代优化算法,假设一个function编译后位于内存区的一个Stub X,相对于一次具体的调用过程P,编译器认为源代码并不需要这么复杂, 所以为它生成了一段优化代码的Stub X1,这个X1是个简单粗暴的形式,编译器并不能确保永远都执行这个Stub,因为执行器到现在只运行了一次invocation,所以它居然大胆地断言,AST中所有挂载这个function的结点都用这个Stub X1替代(mapping),这下好办了,C++ Runtime已经不用担心有自己的事了,因为编译器完全可以分配一个寄存器Rx,将X1在段中的偏移地址读到Rx,然后CPU每次调用直接高速读取Rx的地址,直接跳转过去就完事了,生成的代码大概是这样(假设Stub在R[ebx] + n), Rx = eax x86机器架构):

mov %eax -0x0n(%ebx)    ;load the address of stub, cache the code for all invocation
call <entry of the stub>    ;invocation
...
entry:
tst <if this stub works>
jne <missed>
...

missed:
<new stub X2 generated by C++ runtime>

所以一旦发生了另外一次调用P1,编译器发现这个stub无法满足优化它的条件,这时候就是cache miss了,C++ runtime代码负责善后,针对旧的Stub再次协调新的的调用需求,生成另外一个优化代码Stub X2,这下X2可能还是简单粗暴的版本,但相对于PP1两次调用,好像也没什么问题,但是让代码变得简单,可能只是简单的从寄存器读取或者偶尔的一两次加减操作,不涉及什么循环移位,消耗更少的CPU时间,好的,现在又可以将这个X2缓存起来,让所有的调用直接跳到X2的入口地址,如果不行再退回runtime代码处理新的优化,如此迭代,就可以生成一个有穷状态机,对于一个Pi,我都可以进入一个优化状态Si,比如下面一个switch语句的描述:

switch(P) {
    case Pi:
        //turn to Si
        __stub_pi_();
        break;
    case Pj:
        //turn to Sj
        __stub_pj_();
        break;  
    ...
    default:    
        //turn back to initialized state
        //notify runtime code to generate a new stub
        __runtime_to_generate_new_stub();
}

回想上一篇文章中的emit函数的实现,是不是觉得也有个跟上面很像的switch语句结构,没错那其实也相当于模拟了引擎层的IC,为个别的调用情况生成一些特例化的版本,跟一个回退的非优化版本,其实这种结构的代码在node的源代码中出现的还是比较多的。

一段神奇的代码

为了说明eventloop的各个阶段,我特意写了一段代码:

const timeOut = 10;
let horribleLoop = () => {
    //entry nextTick event invoked by Module.runMain() (within JS code stack), so the node::MakeCallback is never called util node will be exited
    process.nextTick(() => {
        logger.log('entry next tick task executed');
    });
    //check event
    setImmediate(() => {
        logger.log('end this phase of event loop');
        //but the AsyncWrap::MakeCallback invoked nextTick callback block this turn of event loop
        for (let i = 0; i < 5; i++) {
            process.nextTick(() => {
                logger.log('current stack phase id ==> ' + (++recursiveCounter));
            });
        }
    }, timeOut);
    //timer event
    setTimeout(() => {
        logger.log('timer task executed');
    }, timeOut);
    //poll event
    fs.readFile(path.resolve('../cherry'), (err, data) => {
        if(err) {
            logger.log(err.message);
        
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值