自定义博客皮肤VIP专享

*博客头图:

格式为PNG、JPG,宽度*高度大于1920*100像素,不超过2MB,主视觉建议放在右侧,请参照线上博客头图

请上传大于1920*100像素的图片!

博客底图:

图片格式为PNG、JPG,不超过1MB,可上下左右平铺至整个背景

栏目图:

图片格式为PNG、JPG,图片宽度*高度为300*38像素,不超过0.5MB

主标题颜色:

RGB颜色,例如:#AFAFAF

Hover:

RGB颜色,例如:#AFAFAF

副标题颜色:

RGB颜色,例如:#AFAFAF

自定义博客皮肤

-+
  • 博客(1903)
  • 收藏
  • 关注

原创 37 | 云编程:云计算会如何改变编程模式?

上一讲中,分享了当前 3 个技术发展趋势,以及其对编译技术的影响。今天我们把其中的云计算和编程模式、编译技术的之间的关系、前景再展开探讨一下。总的来说,现在编写程序是越来越云化了,所以,我们简单地称作云编程就好了。1. 编程本身是否也能上云?在云上编程会跟本地开发有什么不同?2. 如何编写云应用,来充分发挥云平台的能力?分为哪些不同的模式?3. 为什么编写云应用那么复杂?如何降低这些复杂度?云原生应用的开发平台,能否解决这些问题?

2024-04-25 18:03:30 713

原创 36 | 当前技术的发展趋势以及其对编译技术的影响

在 IT 领域,技术一直在飞速的进步,而每次进步,都会带来新的业态和新的发展机遇。退回到 10 年前,移动互联网刚兴起不久,谁也没想到它会催生现在这么多的业态。而云计算还在酝酿期,腾讯和百度的创始人都觉得它走不远,现在竟然这么普及。退回到 20 年前,互联网刚兴起,上网都要拨号。互联网的几个巨头,像阿里巴巴、百度、腾讯、新浪,还有网易,都是在那个时代展露头角的。

2024-04-25 17:52:39 724

原创 35 | 案例总结与热点问题答疑:后端部分真的比前端部分难吗?

第一批示例程序,与汇编代码有关,包括手写的汇编代码,以及从 playscript 生成汇编代码的程序。这部分内容,主要是打破你对汇编代码的畏惧心,知道它虽然细节很多,但并不难。在讲解后端技术部分时,我总是在提汇编代码,在 34 讲,我甚至写了一个黑客级的小程序,直接操作机器码。我希望经历了这些过程之后,你能对汇编代码亲切起来,产生可以掌握它的信心。第二批示例程序,是基于 LLVM 工具生成 IR 的示例代码。

2024-04-25 15:38:02 872

原创 34 | 运行时优化:即时编译的原理和作用

前面所讲的编译过程,都存在一个明确的编译期,编译成可执行文件后,再执行,这种编译方式。与之对应的,另一个编译方式,也就是,在需要运行某段代码的时候,再去编译。其实,Java、JavaScript 等语言,都是通过即时编译来提高性能的。什么时候用 AOT,什么时候用 JIT 呢?在讲运行期原理时,我提到程序编译后,会生成二进制的可执行文件,加载到内存以后,目标代码会放到代码区,然后开始执行。那么即时编译时,对应的过程是什么?目标代码会存放到哪里呢?

2024-04-25 15:16:30 975

原创 33 | 垃圾收集:能否不停下整个世界?

内存垃圾是一些保存在堆里的对象,但从程序里已经无法访问。在堆中申请一块内存时(比如 Java 中的对象实例),我们会用一个变量指向这块内存。这个变量可能是:全局变量、常量、栈里的变量、寄存器里的变量。我们把这些变量叫做 GC 根节点。它指向的对象中,可能还包含指向其他对象的指针。但是,如果给变量赋予一个新的地址,或者当栈桢弹出,该栈桢的变量全部失效,这时,变量所指向的内存就无用了(如图中的灰色块)。另外,如果 A 对象有一个成员变量指向 C 对象,那么如果 A 不可达,C 也会不可达,也就失效了。

2024-04-25 14:49:41 576

原创 32 | 字节码生成:为什么Spring技术很强大?

它的 IoC(依赖反转)和 AOP(面向切面编程)功能非常强大、易用。而它背后的字节码生成技术(在运行时,根据需要修改和生成 Java 字节码的技术)就是一项重要的支撑技术。Java 字节码能够在 JVM(Java 虚拟机)上解释执行,或即时编译执行。其实,除了 Java,JVM 上的 Groovy、Kotlin、Closure、Scala 等很多语言,也都需要生成字节码。另外,playscript 也可以生成字节码,从而在 JVM 上高效地运行!

2024-04-25 11:55:51 726

原创 31 | 内存计算:对海量数据做计算,到底可以有多快?

内存计算是近十几年来,在数据库和大数据领域的一个热点。随着内存越来越便宜,CPU 的架构越来越先进,整个数据库都可以放在内存中,并通过 SIMD 和并行计算技术,来提升数据处理的性能。:做 1.6 亿条数据的汇总计算,需要花费多少时间呢?几秒?几十秒?还是几分钟?如果你经常使用数据库,肯定会知道,我们不会在数据库的一张表中保存上亿条的数据,因为处理速度会很慢。但今天,会带你采用内存计算技术,提高海量数据处理工作的性能。与此同时,我还会介绍 SIMD 指令、高速缓存和局部性、动态优化等知识点。

2024-04-25 10:55:55 720

原创 30 | 目标代码的生成和优化(二):如何适应各种硬件架构?

我们可以通过重新排列指令,让代码的整体执行效率加快。那你可能会问了:就算重新排序了,每一条指令还是要执行啊?怎么就会变快了呢?别着急,本节就带你探究其中的原理和算法,来了解这个问题。而且,我还会带你了解 LLVM 是怎么把指令选择、寄存器分配、指令重排序这三项工作组织成一个完整流程,完成目标代码生成的任务的。这样,你会对编译器后端的代码生成过程形成完整的认知,为正式做一些后端工作打下良好的基础。首先,我们来看看指令重排序的问题。

2024-04-25 10:11:23 871

原创 29 | 目标代码的生成和优化(一):如何适应各种硬件架构?

a[i] = b 这个表达式的意思是,给数组 a 的第 i 个元素赋值为 b。假设 a 和 b 都是栈里的本地变量,i 是放在寄存器 ri 中。这个表达式可以用一个 AST 表示。你可能觉得这棵树看着像 AST,但又不大像,那是因为里面有 mem 节点(意思是存入内存)、mov 节点、栈指针 (fp)。

2024-04-25 09:07:51 552

原创 28 | 数据流分析:你写的程序,它更懂

到目前为止,我们发现:全局优化总体来说跟本地优化很相似,唯一的不同,就是要基于多个分支计算集合的内容(也就是 V 值)。在进入基本块 1 时,2 和 3 两个分支相遇(meet),我们取了 2 和 3V 值的并集。

2024-04-24 17:48:31 867

原创 27 | 代码优化:为什么你的代码比他的更高效?

一个优化可能导致另一个优化,比如,拷贝传播导致 y 不再被使用,我们又可以进行死代码删除的优化。所以,一般进行多次优化、多次扫描。了解了优化的场景之后,你能直观地知道代码优化到底做了什么事情,不过知其然还要知其所以然,你还需要了解这些优化都是怎么实现的。

2024-04-24 17:25:08 753

原创 26 | 生成IR:实现静态编译的语言

检查这个函数的正确性。这相当于是做语义检查,比如,基本块的最后一个语句就必须是一个正确的返回指令。

2024-04-24 16:42:51 617

原创 25 | 后端技术的重用:LLVM不仅仅让你高效

在编译器后端,做代码优化和为每个目标平台生成汇编代码,工作量是很大的。那么,有什么办法能降低这方面的工作量,提高我们的工作效率呢?。在前端部分,我就带你使用 Antlr 生成了词法分析器和语法分析器。那么在后端部分,我们也可以获得类似的帮助,比如利用 LLVM 和 GCC 这两个后端框架。相比前端的编译器工具,如 Lex(Flex)、Yacc(Bison)和 Antlr 等,对于后端工具,了解的人比较少,资料也更稀缺,如果你是初学者,那么上手的确有一些难度。

2024-04-24 15:53:34 830

原创 24 | 中间代码:兼容不同的语言和硬件

前几节带你尝试不通过 IR,直接生成汇编代码,这是为了帮你快速破冰,建立直觉。在这个过程中,你也遇到了一些挑战,比如:你要对生成的代码进行优化,才有可能更好地使用寄存器和内存,同时也能减少代码量;另外,针对不同的 CPU 和操作系统,你需要调整生成汇编代码的逻辑。这些实际体验,都进一步验证了20 讲中,IR 的作用:我们能基于 IR 对接不同语言的前端,也能对接不同的硬件架构,还能做很多的优化。既然 IR 有这些作用,那你可能会问,IR 都是什么样子的呢?有什么特点?如何生成 IR 呢?

2024-04-24 15:24:40 962

原创 23 | 生成汇编代码(二):把脚本编译成可执行文件

学完两节课之后,对于后端编译过程,你可能还会产生一些疑问,比如:1. 大致知道汇编程序怎么写,却不知道如何从 AST 生成汇编代码,中间有什么挑战。2. 编译成汇编代码之后需要做什么,才能生成可执行文件。本节会带你真正动手,基于 AST 把 playscript 翻译成正确的汇编代码,并将汇编代码编译成可执行程序。通过这样一个过程,可以实现从编译器前端到后端的完整贯通,帮你对编译器后端工作建立比较清晰的认识。

2024-04-24 14:41:49 999

原创 | 汇编代码编程与栈帧管理

在22 讲中,我们侧重讲解了汇编语言的基础知识,包括构成元素、汇编指令和汇编语言中常用的寄存器。学习完基础知识之后,你要做的就是多加练习,和汇编语言“混熟”。小窍门是查看编译器所生成的汇编代码,跟着学习体会。不过,可能你是初次使用汇编语言,对很多知识点还会存在疑问,比如:在汇编语言里调用函数(过程)时,传参和返回值是怎么实现的呢?21 讲中运行期机制所讲的栈帧,如何通过汇编语言实现?条件语句和循环语句如何实现?……为此,针对性地讲解这样几个实际场景,希望帮你加深对汇编语言的理解。

2024-04-24 12:00:33 667

原创 22 | 生成汇编代码(一):汇编语言其实不难学

敲黑板:课程用的是 GNU 汇编器,macOS 和 Linux 已内置,本文的汇编语言的写法是 GNU 汇编器规定的写法。Windows 系统可安装 MinGW 或 Linux 虚拟机。对于静态编译型语言,比如 C 语言和 Go 语言,编译器后端的任务就是生成汇编代码,然后再由汇编器生成机器码,生成的文件叫目标文件,最后再使用链接器就能生成可执行文件或库文件了。就算像 JavaScript 这样的解释执行的语言,也要在运行时利用类似的机制生成机器码,以便调高执行的速度。

2024-04-24 10:06:47 748

原创 21 | 运行时机制:突破现象看本质,透过语法看运行时

编译器的任务,是要生成能够在计算机上运行的代码,但要生成代码,我们必须对程序的运行环境和运行机制有比较透彻的了解。你要知道,大型的、复杂一点儿的系统,比如像淘宝一样的电商系统、搜索引擎系统等等,都存在一些技术任务,是需要你深入了解底层机制才能解决的。比如淘宝的基础技术团队就曾经贡献过,Java 虚拟机即时编译功能中的一个补丁。这反映出掌握底层技术能力的重要性,所以,如果你想进阶成为这个层次的工程师,不能只学学上层的语法,而是要把计算机语言从上层的语法到底层的运行机制都了解透彻。

2024-04-24 09:10:05 825

原创 20 | 高效运行:编译器的后端技术

前 18 节我们主要探讨了编译器的前端技术,它的重点,是让编译器能够读懂程序。无结构的代码文本,经过前端的处理以后,就变成了 Token、AST 和语义属性、符号表等结构化的信息。基于这些信息,我们可以实现简单的脚本解释器,这也从另一个角度证明了我们的前端处理工作确实理解了程序代码,否则程序不可能正确执行嘛。实际上,学完前端技术以后,我们已经能做很多事情了,比如让软件有自定义功能,就像我们在15 讲中提到的报表系统,这时,不需要涉及编译器后端技术。

2024-04-23 17:46:11 611

原创 19 | 案例总结与热点问题答疑:对于左递归的语法,为什么我的推导不是左递归的?

在前端部分,我们伴随着文稿提供了丰富的示例程序,我相信代码是程序员之间沟通的最好手段。第一批示例程序,是 lab/craft 目录下的。通过手工实现简单的词法分析和语法分析,获得第一手的感受,破除对于编译技术的神秘感。你会感觉到,如果要实现公式计算器甚至一个简单脚本,似乎也没那么难。第二批示例程序,是基于 Antlr 工具的。使用这个工具,实现了两个目的:第一,让你借鉴成熟的规则文件,高效实现词法分析和语法分析功能。

2024-04-23 17:06:39 911

原创 18 | 移进和规约:用LR算法推演一个实例

到目前为止,我们所讨论的语法分析算法,都是自顶向下的。与之相对应的,是自底向上的算法,比如本节课要探讨的 LR 算法家族。LR 算法是一种自底向上的算法,它能够支持更多的语法,而且没有左递归的问题。第一个字母 L,与 LL 算法的第一个 L 一样,代表从左向右读入程序。第二个字母 R,指的是 RightMost(最右推导),也就是在使用产生式的时候,是从右往左依次展开非终结符。例如,对于“add->add+mul”这样一个产生式,是优先把 mul 展开,然后再是 add。

2024-04-23 16:37:05 610

原创 17 | First和Follow集合:用LL算法推演一个实例

自顶向下分析的算法是一大类算法。总体来说,它是从一个非终结符出发,逐步推导出跟被解析的程序相同的 Token 串。这个过程可以看做是一张图的搜索过程,这张图非常大,因为针对每一次推导,都可能产生一个新节点。下面这张图只是它的一个小角落。算法的任务,就是在大图中,找到一条路径,能产生某个句子(Token 串)。比如,我们找到了三条橘色的路径,都能产生“2+3*5”这个表达式。根据搜索的策略,有深度优先(Depth First)和广度优先(Breadth First)两种,这两种策略的推导过程是不同的。

2024-04-23 16:25:03 952

原创 16 | NFA和DFA:如何自己实现一个正则表达式工具?

运行 DFA,看看它有什么特点。强调一下,不要被非确定的有限自动机、确定的有限自动机这些概念吓倒,我肯定让你学明白。

2024-04-23 15:48:05 759

原创 15 | 前端技术应用(二):如何设计一个报表工具?

我们首先要把报表定义形成 Java 对象。这里只是简单地生成了一个测试用的报表模板。

2024-04-23 15:18:59 679

原创 14 | 前端技术应用(一):如何透明地支持数据库分库分表?

因为执行每个 SQL 都需要做一次解析,而这个时间就加在了每一次数据库访问上。所以,SQL 解析的时间越少越好。因此,有的项目就会尽量提升解析效率。

2024-04-23 14:32:35 915

原创 13 | 继承和多态:面向对象运行期的动态特性

面向对象编程时,我们可以给某个类创建不同的子类,实现一些个性化的功能;写程序时,我们可以站在抽象度更高的层次上,不去管具体的差异。如果把上面的结论抽象成一般意义上的类型理论,就是。

2024-04-23 11:38:53 639

原创 12 | 语义分析(下):如何做上下文相关情况的处理?

所以,我们也把这种语义规则的定义叫做语法制导的定义(Syntax directed definition,SDD),如果变成计算动作,就叫做语法制导的翻译(Syntax directed translation,SDT)。属性计算,可以伴随着语法分析的过程一起进行,也可以在做完语法分析以后再进行。这两个阶段不一定完全切分开。

2024-04-23 10:50:25 643

原创 11 | 语义分析(上):如何建立一个完善的类型系统?

在做语法分析时我们可以得到一棵语法树,而基于这棵树能做什么,是语义的事情。比如,+ 号的含义是让两个数值相加,并且通常还能进行缺省的类型转换。所以,如果要区分不同语言的差异,不能光看语言的语法。比如 Java 语言和 JavaScript 在代码块的语法上是一样的,都是用花括号,但在语义上是不同的,一个有块作用域,一个没有。这样看来,相比词法和语法的设计与处理,语义设计和分析似乎要复杂很多。虽然我们借作用域、生存期、函数等特性的实现涉猎了很多语义分析的场景,但离系统地掌握语义分析,还差一点儿火候。

2024-04-23 10:15:22 937

原创 10 | 闭包: 理解了原理,它就不反直觉了

在讲作用域和生存期时,我提到函数里的本地变量只能在函数内部访问,函数退出之后,作用域就没用了,它对应的栈桢被弹出,作用域中的所有变量所占用的内存也会被收回。但偏偏跑出来这个怪物。在 JavaScript 中,用外层函数返回一个内层函数之后,这个内层函数能一直访问外层函数中的本地变量。按理说,这个时候外层函数已经退出了,它里面的变量也该作废了。可闭包却非常执着,即使外层函数已经退出,但内层函数仿佛不知道这个事实一样,还继续访问外层函数中声明的变量,并且还真的能够正常访问。

2024-04-23 09:26:08 597

原创 09 | 面向对象:实现数据和方法的封装

在现代计算机语言中,面向对象是非常重要的特性,似乎常用的语言都支持面向对象特性,比如 Swift、C++、Java……不支持的反倒是异类了。而它重要的特点就是封装。也就是说,对象可以把数据和对数据的操作封装在一起,构成一个不可分割的整体,尽可能地隐藏内部的细节,只保留一些接口与外部发生联系。在对象的外部只能通过这些接口与对象进行交互,无需知道对象内部的细节。这样能降低系统的耦合,实现内部机制的隐藏,不用担心对外界的影响。那么它们是怎样实现的呢?

2024-04-22 17:46:31 524

原创 08 | 作用域和生存期:实现块作用域和函数

目前,我们已经用 Antlr 重构了脚本解释器,有了工具的帮助,我们可以实现更高级的功能,比如函数功能、面向对象功能。当然了,在这个过程中,我们还要克服一些挑战,比如:如果要实现函数功能,要升级变量管理机制;引入作用域机制,来保证变量的引用指向正确的变量定义;提升变量存储机制,不能只把变量和它的值简单地扔到一个 HashMap 里,要管理它的生存期,减少对内存的占用。本节将借实现块作用域和函数功能,带你探讨作用域和生存期及其实现机制,并升级变量管理机制。

2024-04-22 17:02:35 1001

原创 07 | 编译器前端工具(二):用Antlr重构脚本语言

上一讲,带你用 Antlr 生成了词法分析器和语法分析器,也带你分析了,跟一门成熟的语言相比,在词法规则和语法规则方面要做的一些工作。在词法方面,我们参考 Java 的词法规则文件,形成了一个 CommonLexer.g4 词法文件。在这个过程中,我们研究了更完善的字符串字面量的词法规则,还讲到要通过规则声明的前后顺序来解决优先级问题,比如关键字的规则一定要在标识符的前面。目前来讲,我们已经完善了词法规则,所以今天我们来补充和完善一下语法规则,看一看怎样用最高效的速度,完善语法功能。

2024-04-22 16:31:32 755

原创 06 | 编译器前端工具(一):用Antlr生成词法、语法分析器

前面的课程中,重点讲解了词法分析和语法分析,在例子中提到的词法和语法规则也是高度简化的。虽然这些内容便于理解原理,也能实现一个简单的原型,在实际应用中却远远不够。实际应用中,一个完善的编译程序还要在词法方面以及语法方面实现很多工作,这里特意画了一张图,可以直观地看一下。如果让编译程序实现上面这么多工作,完全手写效率会有点儿低,那么我们有什么方法可以提升效率呢?答案是借助工具。

2024-04-22 16:03:01 704

原创 05 | 语法分析(三):实现一门简单的脚本语言

前两节结束后,我们已经掌握了表达式的解析,并通过一个简单的解释器实现了公式的计算。但这个解释器还是比较简单的,看上去还不大像一门语言。那么如何让它支持更多的功能,更像一门脚本语言呢?本节课,会带你寻找答案。将继续带你实现一些功能,比如:支持变量声明和初始化语句,就像“int age” “int age = 45”和“int age = 17+8+20”;支持赋值语句“age = 45”;在表达式中可以使用变量,例如“age + 10 *2”;实现一个命令行终端,能够读取输入的语句并输出结果。

2024-04-22 15:27:26 1005

原创 04 | 语法分析(二):解决二元表达式中的难点

在“03 | 语法分析(一):纯手工打造公式计算器”中,我们已经初步实现了一个公式计算器。而且你还在这个过程中,直观地获得了写语法分析程序的体验,在一定程度上破除了对语法分析算法的神秘感。当然了,你也遇到了一些问题,比如怎么消除左递归,怎么确保正确的优先级和结合性。所以本节课的主要目的就是解决这几个问题,让你掌握像算术运算这样的二元表达式(Binary Expression)。

2024-04-22 14:44:06 778

原创 03 | 语法分析(一):纯手工打造公式计算器

我想你应该知道,公式是 Excel 电子表格软件的灵魂和核心。除此之外,在 HR 软件中,可以用公式自定义工资。而且,如果你要开发一款通用报表软件,也会大量用到自定义公式来计算报表上显示的数据。总而言之,很多高级一点儿的软件,都会用到自定义公式功能。既然公式功能如此常见和重要,我们不妨实现一个公式计算器,给自己的软件添加自定义公式功能吧!本节课将继续“手工打造”之旅,让你纯手工实现一个公式计算器,借此掌握我所举例的公式计算器支持加减乘除算术运算,比如支持“2 + 3 * 5”的运算。

2024-04-22 11:43:16 761

原创 02 | 正则文法和有限自动机:纯手工打造词法分析器

关键字是语言设计中作为语法要素的词汇,例如表示数据类型的 int、char,表示程序结构的 while、if,表述特殊数据取值的 null、NAN 等。除了关键字,还有一些词汇叫保留字。保留字在当前的语言设计中还没用到,但是保留下来,因为将来会用到。我们命名自己的变量、类名称,不可以用到跟关键字和保留字相同的字符串。那么我们在词法分析器中,如何把关键字和保留字跟标识符区分开呢?以“int age = 40”为例,我们把有限自动机修改成下面的样子,借此解决关键字和标识符的冲突。这个思路其实很简单。

2024-04-22 11:11:50 879

原创 01 | 理解代码:编译器的前端技术

在开篇词里,分享了一些使用编译技术的场景。其中有的场景,你只要掌握编译器的前端技术就能解决。比如文本分析场景,软件需要用户自定义功能的场景以及前端编程语言的翻译场景等。而且咱们大学讲的编译原理,也是侧重讲解前端技术,可见编译器的前端技术有多么重要。当然了,。它通常只跟语言的语法有关,跟目标机器无关。。为了方便理解,用一张图直观地展现了编译器的整个编译过程。你可以看到,编译器的“前端”技术分为和三个部分。而它主要涉及自动机和形式语言方面的基础的计算理论。

2024-04-22 10:04:03 730

原创 开篇词 | 为什么你要学习编译原理?

第三部分是对编译技术发展趋势的一些分析。

2024-04-22 09:08:05 720

原创 程序员需要读哪些数学书?

数学领域涉及的面很广,相关的书籍也很多。咱们这个专栏我从数学的三个主要方面,介绍程序员常用的数学知识,包括离散数学、概率和统计和线性代数。所以还是围绕这个专栏的三大模块,来推荐相应的书籍。

2024-04-19 18:06:26 709

空空如也

空空如也

TA创建的收藏夹 TA关注的收藏夹

TA关注的人

提示
确定要删除当前文章?
取消 删除