基本程序及一些底层原理解释

编程语言

编程语言(programming language),是用来定义计算机程序的形式语言。它是一种被标准化的交流技巧,用来向计算机发出指令。

​ Java作为一种高级编程语言,和其他高级编程语言一样,是一种功能强大和多用途的编程语言,可用于开发运行在移动设备、台式计算机以及服务器的软件。

  • 低级编程语言(Low-level programming language)

    接近硬件,机器相关,由0、1串组成,往往不可移植,难于阅读和对人不友好(human-friendly)。

    比如你能看出下面的代码是啥么?

    0000100100101110011001100110100101101100011001010000100100100010011011000110010101100011011101000111010101110010011001010011000100101110011000110010001000001010011001110110001101100011001100100101111101100011011011110110110101110000011010010110110001100101011001000010111000111010000010100010111001110011011001010110001101110100011010010110111101101110000010010010001000101110011101000110010101111000011101000010001000001010000010010010111001100001011011000110100101100111011011100010000000110100000010100000100100101110011001110110110001101111011000100110000101101100001000000110110101100001011010010110111000001010000010010010111001110100011110010111000001100101000010010010000001101101011000010110100101101110...
    
  • 汇编语言(Assembly language)

    汇编语言使用助记符代替一些机器指令,用地址符号或标号代替指令操作数的地址。开始变得对人友好,可阅读性和便捷性比起机器语言有所提高。但汇编语言只是将机器语言做了简单编译,并没有从本质上解决机器语言的特定性,还是和机器自身和编程环境相关。

  • 高级编程语言(High-level programming language)

    接近自然语言和数学公式,具有更强的表达能力,易于学习和掌握,易于移植。

    • Fortran:第一个科学计算语言
    • Cobol:第一个商业数据处理语言
    • Lisp:第一个函数式程序语言

程序基本运行过程

​ 用高级语言编写的程序成为源程序(source program)或者源代码(source code)。由于计算机不能直接运行源程序,源程序必须被翻译为可执行的机器代码。翻译可由解释器(interpreter)或者编译器(compiler)来完成。由解释器解释的语言叫做解释型语言,由编译器编译的语言叫做编译型语言

编译器把源代码转化成目标代码,再执行目标代码从而运行程序。解释器不生成目标代码,直接执行指令从而运行代码。

请添加图片描述

编译器

请添加图片描述
解释器

编译器和解释器的不同
  • 编译器将源代码翻译成可以直接运行在目标机器上的机器代码;解释器不需要先翻译成机器代码,直接执行源代码中的每一条指令。
  • 编译器花更多时间分析代码关系从而优化代码;解释器只是简单的转化每一条指令然后执行。
  • 解释器直接执行程序直到遇到第一个可以中断执行的错误;编译器保证代码只有完全通过编译才能执行。
Java程序执行过程

​ Java是一个奇怪的语言,你可以说他是编译型语言,也可以说他是解释型语言。但在我看来,说的准确一点,Java是一个编译型与解释型混合语言。以下为Java程序的执行过程:

请添加图片描述

​ Java源程序首先经过Java编译器生成后缀为.class的Java字节码文件,然后送到JVM执行。JVM首先会使用一个叫做类加载器(class loader)的程序将类的字节码加载到内存中。如果程序使用到了其他类,类加载程序会在需要他们之前动态的加载它们。当加载完成后,JVM使用一个称为字节码验证器(bytecode verifier)的程序来检验字节码的合法性,确保字节码不会违反Java安全规范。Java强制执行严格的安全规范,以确保来自网络的Java程序不会篡改和危害你的计算机。之后即可通过解释器执行Java程序。Java程序最初是通过解释器进行解释执行的,当虚拟机发现某个方法或代码块的运行特别频繁时,就会把这些代码认定为“热点代码”。为了提高热点代码的执行效率,在运行时,虚拟机将会把这些代码编译成与本地平台相关的机器码,并进行各种层次的优化,完成这个任务的编译器称为即时编译器(Just In Time Compiler,JIT)。Java虚拟机规范并没有规定Java虚拟机内必须要有即时编译器存在,但是由于解释器的性能较低,主流的Java虚拟机都采用了编译器解释器并存的架构。

​ 从Java执行过程来看,把Java叫做编译型与解释型混合语言更准确一点。

常见的编译型语言:C、C++、Pascal、Go等
常见的解释型语言:JavaScript、Perl、Python、Ruby等

一般编译器构造

​ 一般编译器按照逻辑可划分为7个部分,词法分析器(lexical analyzer)、语法分析器(syntax analyzer)、语义分析器(semantic analyzer)、中间代码生成器(intermediate code generator)、机器无关代码优化器(machine-independent code optimizer)、代码生成器(code generator)和机器相关代码优化器(machine-dependent code optimizer)。

请添加图片描述

​ 其中,词法分析器、语法分析器、语义分析器和中间代码生成器4部分叫做编译器前端,编译器前端对源程序进行分析,把源程序切分成一些基本块并生成中间语言表示IR(intermediate representation),并且收集源程序信息存到符号表(symbol table)。

请添加图片描述

​ 后端负责从由前端传递过来的中间代码和符号表生成目标程序,并且在处理过程中执行代码优化。

请添加图片描述

Lexical Analysis(scanning,词法分析)

请添加图片描述

词法分析器将源代码分成一个一个的词素(lexemes)或者词(words),对于每一个词素,产生一个词法单元(token)。词素(lexeme),是一个字符串,程序设计语言中的语法单元。词法单元(token),是词素的一个语法集合。比如在英语中的名词、动词、形容词;编程语言中的标识符(identifier),关键字(keyword)等。一个基本的token形式:<token-name, attribute-value>

如:

请添加图片描述

Syntax Analysis(parsing,语法分析)

请添加图片描述

词法分析器使用语法分析器生成的tokens表示语法结构,这个结构通常是一个语法树(syntax tree)。语法树的中间节点表示操作(operation),孩子节点表示操作的参数(arguments)。

如:

请添加图片描述

Semantic Analysis (语义分析)

请添加图片描述

语义分析器使用语法树和符号表中的信息检查语言定义的语义一致性,同时为类型检查、类型转化和中间代码生成收集类型信息。

  • 语法(syntax)描述程序的适当形式。
  • 语义(semantic)描述程序的意思。

比如在英语中:

Jack said Jerry left his assignment at home.(Jack说Jerry把他的作业放过家里了)

这句话是符合语法的,但是我们无法知道这里的“his”指代的是Jack还是Jerry,这就造成了语义不明。语义分析就是为了确定每一条指令的具体意思。

Intermediate Code Generation (中间代码生成)

请添加图片描述

当完成语义分析后,编译器生成一个中间表示,通常是三地址码(three-address code)。一张图说明生成中间表示的重要意义。

请添加图片描述

如果没有合适的中间表示,在有M种语言和N种机器的情况下,就需要M*N个不同的编译器。但是存在一个合适的中间表示的情况下,就只需要M+N个不同的编译器。大家都知道O(n)和O(n2)的区别吧。

三地址码(TAC)

​ 最多只有一个运算符在指令右边,通常形式为 x = y op z。因为涉及三个变量,因此被称为三地址码。三地址码可以有不同的表现形式,典型的表现形式为四元式(Quadruples)、三元式(Triples)和间接三元式(Indirect triples)。

  • 四元式表示法一般形式:<op arg1 arg2 result>
  • 三元式表示一般形式:<op arg1 arg2>

如赋值语句 a = b ∗ −c + b ∗ −c,以下为对应的三元式和四元式表示。

请添加图片描述

感觉三元式似乎比四元式简单,但其实三元式存在着一个缺点,无法交换指令顺序从而执行后序代码优化。例如:

请添加图片描述

交换指令1和2并不会影响四元式的执行结果,但是在三元式中,得到了错误的答案。

  • 间接三元式相对三元式增加了一个指令列表,可以实现指令交换。

请添加图片描述

执行交换时:

请添加图片描述

Machine-Independent Code Optimization (机器无关的代码优化)

请添加图片描述

机器无关代码优化提高中间代码的质量,目的是为了让它运行的更快,占用更少的内存,代码更短,消耗更少的资源等等。常见的优化点有死代码消除,局部/全局子表达式消除,复制传播和代码移动等。

例如:

请添加图片描述

Code Generation (代码生成)

请添加图片描述

代码生成器负责把中间表示(IR)翻译成对应目标机器代码,这里需要处理给变量分配寄存器和内存。

例如:

请添加图片描述

Machine-dependent Code Optimization (机器相关的代码优化)

请添加图片描述

​ 机器相关的代码优化是在目标代码生成之后根据目标机器架构转换代码。它涉及CPU寄存器,可能有绝对内存引用而不是相对引用。机器相关的代码优化努力最大限度地利用内存层次结构。

比如某CPU架构将执行一条指令分为5部分

  • IF: 获取指令(Instruction fetch from memory)
  • ID: 指令解码和寄存器读(Instruction decode & register read)
  • EX: 指令执行(Execute operation or calculate address)
  • MEM: 内存访问(Access memory operand)
  • WB: 结果写回寄存器(Write result back to register)

现有指令:

add $s0, $t0, $t1

sub $t2, $s0, $t3

如果要顺序执行这两条指令,将会带来两个时钟周期的浪费,因为第二条指令的s0依赖于第一条指令的计算结果,需要等待第一条指令写回寄存器后才能计算。

请添加图片描述

这时可以调换指令执行顺序,将一些执行顺序无关的指令在这两个空时钟周期执行。(其实这里也可以通过一个叫Forwarding/Bypassing的技术解决,这里主要想突出根据CPU结构优化代码)。

  • 5
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值