🎨
Notes
  • 持续更新中...
  • articles
    • browser
      • 深入理解现代浏览器 - 导航
      • 深入理解现代浏览器 - 架构
      • 深入理解现代浏览器 - 交互
      • 深入理解现代浏览器 - 渲染器进程
    • dsa
      • DSA - 并查集
      • DSA - 哈希表
      • DSA - AVL 树
      • DSA - 二叉树
      • 快速选择
      • Big O 算法复杂度
      • DSA - 栈和队列
      • DSA - 前缀树 Trie
      • DSA - 图
      • DSA - 链表
      • DSA - 递归
    • typescript
      • TypeScript 学习笔记 - 任意属性 (Indexable Types)
      • 力扣的 TypeScript 面试题
      • TypeScript 学习笔记 - as const
      • TypeScript 学习笔记 - infer
    • network
      • Internet Protocol (IP)
      • 计算机网络基础
      • 如何分辨同源和同站
      • DNS 如何查询 IP 地址?
    • vue
      • Nuxt.js 入门
      • 从零实现一个 Mini Vue
      • 从零实现一个简单的 VDOM 引擎
      • 从零实现一个响应式状态管理
    • sorting
      • 排序 - 归并排序
      • 排序 - 冒泡排序
      • 排序 - 选择排序
      • 排序 - 计数排序
      • 排序 - 插入排序
    • compile
      • Compiler and Interpreter
      • Just-In-Time (JIT) Compilers
      • 编译流程
    • others
      • 什么是上下文无关语法
      • 如何在终端打印出有颜色的字
    • dev-ops
      • github-actions
        • GitHub Action 简介
        • GitHub Actions for CI
    • workflow
      • 用 Node 写一个 cli
      • 如何规范 git commit 信息
      • 如何监听 git hooks
      • 如何规范代码风格 - prettier
      • 如何发布一个 npm package
      • 如何规范代码质量 - eslint
    • design-pattern
      • 代理模式
      • 单例模式
      • 策略模式
    • security
      • 点击劫持
      • CSP 内容安全策略
    • javascript
      • 尾调用优化
      • 4种常见的内存泄漏及解决方法
    • unit-test
      • Test Vuejs Application - Chapter 2
      • Test Vuejs Application - Chapter 1
      • Vue Unit Test Intro
    • performance
      • HTTP 缓存
      • 如何优化图片资源
Powered by GitBook
On this page
  • Introduction
  • How JIT Works
  • How JIT Compiles Codes
  • Baseline Compiler
  • Optimizing Compiler
  • How JIT Knows Which To Use
  • Extension
  • 优化和反优化(optimization and deoptimization)
  • 额外的内存占用

Was this helpful?

  1. articles
  2. compile

Just-In-Time (JIT) Compilers

PreviousCompiler and InterpreterNext编译流程

Last updated 4 years ago

Was this helpful?

Introduction

这篇介绍了 Compiler 和 Interpreter 以及它们各自的优缺点,我们先来简单回顾一下。

  • Compiler: 事先把源码编译成机器语言,在需要执行程序的时候直接运行编译后的代码。因为编译工作是事先完成的,所以 Compiler 在编译的时候有充足的时间好好分析源码,并对其进行优化,生成运行速度更快的代码。

  • Interpreter:在需要执行程序的时候才开始边编译,边运行编译后的代码。如果是在运行一个循环语句,那代码每次循环都要重新编译一次。而且由于是一边编译一边运行,时间很紧,Interpreter 也不能在编译代码的过程中做太多优化的工作,生成的代码运行速度相对慢一些。

而 JIT 则是结合了 Compiler 和 Interpreter 的优点,是两者的一个完美结合。

How JIT Works

  • 在程序刚开始运行的时候,JIT 像 Interpreter 一样一行一行地编译并运行代码;

  • 在这个过程中,如果它发现了一些代码需要重复地运行(比如循环中的代码),就会像 Compiler 一样把这部分代码进行编译并存起来,下次再需要运行这部分代码时,就从内存中取出编译后的代码直接运行;

How JIT Compiles Codes

如果 JIT 发现了需要多次运行的代码,它会把这部分代码进行编译,但编译的方式分两种,分别由 Baseline Compiler 和 Optimizing Compiler 来负责。

Baseline Compiler

Baseline Compiler 负责做一些简单的编译工作,编译时间不长,但生成的代码优化程度也不高。如果把代码优化的过程比喻成论文修改的话,Baseline Compiler 就只是改改标点符号和错别字而已。

Optimizing Compiler

Optimizing Compiler 负责将源代码编译成优化程度更高的代码,编译时间较长,不过生成的代码运行速度更快,相当于对论文的结构和内容进行了优化。

How JIT Knows Which To Use

那 JIT 怎么知道一行代码是否需要优化?

在代码执行的时候,JS 引擎会用一个 monitor(aka. profiler) 来监测并记录代码的执行频率。

  • 如果一行代码被重复执行了几次,monitor 会将它标记为 warm,JIT 就会把它送到 Baseline Compiler 去编译,然后把编译后的代码存起来。

  • 如果一行代码被重复执行了很多很多次,它就会被标记为 hot,JIT 就会把它送到 Optimizing Compiler 去编译,然后把编译后的代码存起来。

Extension

通过监测代码执行频率以及编译优化代码,JIT 提高了 JS 的运行速度,不过它同时也带来了一些 overhead:

优化和反优化(optimization and deoptimization)

上文没有提到的是,Compiler 在优化代码的时候会作出一些假设。

  • 比如说函数 function foo(a) {} 由于执行较频繁被送到了 Optimizing Compiler 去优化;

  • 前提:foo 在最初几次执行的时候接收到的参数 a 都是字符串类型的;

  • Compiler 会假设在之后的调用中,传入的 a 也都是字符串,并基于这个假设对 foo 进行优化;

  • 如果程序之后调用 foo 时都是传入字符串,那一切就都很美好;

  • 不过,假如有一次调用中传入了数字,因为对数字和字符串的操作不一样,之前基于假设 a 是字符串 优化的代码就完全用不上了,这时 Compiler 就会将代码反优化并重新进行编译和优化;

  • 假如在程序运行中 Compiler 做了很多这样错误的假设,一直在优化和反优化同一段代码的话,有可能消耗的时间比运行没有优化的代码还长;

  • 不过,浏览器对此也做了一些处理,如果优化和反优化这个过程重复了太多次的话,就直接放弃了对这段代码进行优化。

额外的内存占用

  • monitor 记录的代码执行频率等信息需要占用一部分内存

  • 编译后的代码也需要地方储存起来

Compiler & Interpreter