编译器优化

作业一——编译原理概述

匆匆过客 提交于 2019-11-29 04:14:37
2019年9月3日 作业一——编译原理概述 1)简述编译程序与翻译程序、汇编程序的联系与区别。   ① 编译程序是现代计算机系统的基本组成部分之一,从功能上看,一个编译程序就是一个语言翻译程序。但是编译语言不等同于翻译语言,编译语言是源语言想Pascal或者C这样的高级语言,目标语言是像汇编语言或机器语言那样的低级语言,这样的翻译程序才叫做编译程序。   ② 翻译程序就是把一种语言(源语言)书写的程序翻译成另一种语言(目标语言)的等价程序。   ③ 汇编程序是一个翻译程序,它把汇编语言程序翻译成机器语言程序。 2)编译过程包括哪几个主要阶段及每个阶段的主要功能。   编译过程包括词法分析、语法分析、语义分析、中间代码生成、代码优化和目标代码生成6个阶段。 阶段 主要功能 ①词法分析 是编译过程的第一个阶段,是从左到右一个字符一个字符地读入源程序,对构成源程序地字符流进行扫描和分解,从而识别出一个个单词。 ②语法分析 是编译过程的第二个阶段,任务是在词法分析的基础上将单词序列分解成各类语法短语。这种语法短语也成为语法单位,可以表示成语法树。 ③语义分析 审查源程序有无语义错误,为代码生成阶段收集类型信息。 ④中间代码生成 将源程序变成一种内部表示形式,这种内部表示形式叫做中间语言或中间代码。所谓中间代码是一种结构简单、含义明确的记号系统,这种记号系统可以设计为多种多样的形式。

第一次作业:编译原理概述

吃可爱长大的小学妹 提交于 2019-11-29 04:14:34
1) 翻译程序就是将高级语言源程序翻译成机器语言源程序的软件。它分为2种:一种是编译程序,它将高级语言源程序一次性全部翻译成目标程序,每次执行程序时,只要执行目标程序。另一种是解释程序,它的执行过程是翻译一句执行一句,并且不会生成目标程序。编译程序是先完整编译后运行的程序,如C、C++等;解释程序是一句一句翻译且边翻译边执行的程序,如JS、Python等。汇编程序也是一种语言翻译程序,它把汇编语言源程序翻译成机器语言程序。编译程序与汇编程序的区别:如果源语言是诸如C、C++、Java等“高级语言”,而目标语言是诸如汇编语言或机器语言,这样的一个翻译程序称为编译程序。如果源语言是汇编语言,而目标语言是机器语言,这样的一个翻译程序称为汇编程序。 2) 编译程序的工作划分为五个阶段:词法分析、语法分析、语义分析与中间代码产生、优化、目标代码生成。 词法分析:输入源程序,对构成源程序的字符串进行扫描和分解,识别出一个个的单词(亦称单词符号或简称符号),如基本字,标识符、常数、运算符和界符。 语法分析:在词法分析的基础上,根据语言的语法规则,把单词符号串分解成各类语法单位(语法范畴),如“短语”、“句子”、“程序段”和“程序”等。通过语法分析,确定整个输入串是否构成语法上正确的“程序”。语法分析所依循的是语言的语法规则。语法规则通常用上下文无关文法描述。词法分析是一种线性分析

深入浅出计算机组成原理:Superscalar和VLIW-如何让CPU的吞吐率超过1?(第26讲)

不想你离开。 提交于 2019-11-29 03:10:51
一、引子 到今天为止,专栏已经过半了。过去的20多讲里,我给你讲的内容,很多都是围绕着怎么提升CPU的性能这个问题展开的。我们先回顾一下第4讲,不知道你是否还记得这个公式: 程序的CPU执行时间 = 指令数 × CPI × Clock Cycle Time 这个公式里,有一个叫CPI的指标。我们知道,CPI的倒数,又叫作IPC(Instruction Per Clock),也就是一个时钟周期里面能够执行的指令数,代表了CPU的吞吐率。那么,这个指标,放在我们前面几节反复优化流 水线架构的CPU里,能达到多少呢? 答案是,最佳情况下,IPC也只能到1。因为无论做了哪些流水线层面的优化,即使做到了指令执行层面的乱 这说明,无论指令后续能优化得多好,一个时钟周期也只能执行完这样一条指令,CPI只能是1。但是,我们现在用的Intel CPU或者ARM的CPU,一般的CPI都能做到2以上,这是怎么做到的呢? 今天,我们就一起来看看,现代CPU都使用了什么 “黑科技”。 二、多发射与超标量:同一实践执行的两条指令 1、整数和浮点数计算的电路,在CPU层面也是分开的 之前讲CPU的硬件组成的时候,我们把所有算术和逻辑运算都抽象出来,变成了一个ALU这样的“黑盒子”。你应该还记得第13讲到第16讲,关于加法器、乘法器、乃至浮点数计算的部分,其实整数的计算和 浮点数的计算过程差异还是不小的。实际上

关于arm 的字节对齐

[亡魂溺海] 提交于 2019-11-29 01:44:18
一.什么是 字节对齐 ,为什么要对齐?   现代计算机中内存空间都是按照byte划分的,从理论上讲似乎对任何类型的变量的访问可以从任何地址开始,但实际情况是在访问特定类型变量的时候经常在特定的内存地址访问,这就需要各种类型数据按照一定的规则在空间上排列,而不是顺序的一个接一个的排放,这就是对齐。   对齐的作用和原因:各个硬件平台对存储空间的处理上有很大的不同。一些平台对某些特定类型的数据只能从某些特定地址开始存取。比如有些架构的CPU在访问一个没有进行对齐的变量的时候会发生错误,那么在这种架构下编程必须保证字节对齐.其他平台可能没有这种情况,但是最常见的是如果不按照适合其平台要求对数据存放进行对齐,会在存取效率上带来损失。比如有些平台每次读都是从偶地址开始,如果一个int型(假设为32位系统)如果存放在偶地址开始的地方,那么一个读周期就可以读出这32bit,而如果存放在奇地址开始的地方,就需要2个读周期,并对两次读出的结果的高低字节进行拼凑才能得到该32bit数据。显然在读取效率上下降很多。 二.编译器是按照什么样的原则进行对齐的?   先让我们看四个重要的基本概念:   1.数据类型自身的对齐值:对于char型数据,其自身对齐值为1,对于short型为2,对于int,float,double类型,其自身对齐值为4,单位字节。   2.结构体的自身对齐值

嵌入式开发常遇的问题收集汇总(持续更新)

ぃ、小莉子 提交于 2019-11-28 20:10:37
STM32部分: 1、 问题: 在调试公司项目的时候,将使用 DMA2 的 5 通道采集 adc3 的子工程代码移植到总工程代码中,发现程序在进入到主循环的时候总是卡死。 寻找问题: 使用mdk DEBUG仿真,停止仿真程序会停在 HardFault_Handler 函数里的死循环while(1)中。这说明STM32出现了硬件错误。最后发现总工程中也用到了 DMA2 的通道 5 ,是用在 uart4_TX 上的,在硬件资源分配上产生了冲突,最后导致程序卡死。 解决办法 :使用 ADC3 多通道采集的 非DMA 方式, 让出DMA资源 。 2、 问题: 使用 MDK 写程序并编译出现 ..\Vehicle\pedal.c(166): error: #18: expected an expression 寻找问题: 最后在网络上找到了问题所在 Misc Controls 在MDK中默认是C90,将其改为C99即可解决 解决办法 :如下图所示添加--c99 3、 问题: 定义结构体导致出现内存大小异常问题 4、 问题: 在调试别人的程序过程中发现程序卡死。 寻找问题: 经过仿真发现程序总是在启动文件的 DMA2_Channel2_IRQHandler DMA2_Channel3_IRQHandler DMA2_Channel4_5_IRQHandler B . ENDP ALIGN

漫话:如何给女朋友解释鸿蒙OS是怎样实现跨平台的?

两盒软妹~` 提交于 2019-11-28 17:14:10
周末在家休息,女朋友在刷朋友圈,突然她问我: 鸿蒙OS回顾 2019年8月9日华为开发者大会上,华为消费者业务CEO余承东正式宣布发布自有操作系统鸿蒙,内核为Linux内核、鸿蒙微内核和LiteOS。未来将摆脱Linux内核和LiteOS,只有鸿蒙微内核。 鸿蒙(英语:Harmony OS,开发代号Ark)是华为自2012年开发的一款可能兼容Android app的跨平台操作系统。 图:鸿蒙OS的四大技术特性 1.分布式架构首次用于终端OS,实现跨终端无缝协同体验 2. 确定时延引擎和高性能IPC技术实现系统天生流畅 3. 基于微内核架构重塑终端设备可信安全 4. 通过统一IDE支撑一次开发,多端部署,实现跨终端生态共享 什么是跨平台 在以前, 平台 ≈ 操作系统 。所以,传统意义上的跨平台即不依赖于操作系统,也不依赖硬件环境。一个操作系统下开发的应用,放到另一个操作系统下依然可以运行。 但是随着科技的发展, 平台 ≈ 操作系统 已经不成立了,就像华为推出的鸿蒙OS,他可以支持到多种多样的设备,如手机、手表、电脑、汽车、智能家居设备等。 所以,今天我们谈的跨平台,指的是跨设备。即 平台 ≈ 设备 所以,华为希望鸿蒙OS可以运行在各种各样的设备上,所以,鸿蒙OS必然需要具备跨平台的能力。 而且,鸿蒙想要做的不仅仅是操作系统可以跨平台,更重要的是要让用户和开发者真正的感受到跨平台。

C/C++中extern关键字详解

笑着哭i 提交于 2019-11-28 14:25:07
1 基本解释 :extern可以置于 变量或者函数 前, 以标示变量或者函数的定义在别的文件中 , 提示编译器遇到此变量和函数时在其他模块中寻找其定义 。此外extern也可用来进行链接指定。 也就是说extern有两个作用,第一个,当它与"C"一起连用时,如: extern "C" void fun(int a, int b);则告诉编译器在编译fun这个函数名时按着C的规则去翻译相应的函数名而不是C++的,C++的规则在翻译这个函数名时会把fun这个名字变得面目全非,可能是fun@aBc_int_int#%$也可能是别的,这要看编译器的"脾气"了(不同的编译器采用的方法不一样),为什么这么做呢,因为C++支持函数的重载啊,在这里不去过多的论述这个问题,如果你有兴趣可以去网上搜索,相信你可以得到满意的解释! 第二,当extern不与"C"在一起修饰变量或函数时,如在头文件中: extern int g_Int; 它的作用就是声明函数或全局变量的作用范围的关键字,其声明的函数和变量可以在本模块活其他模块中使用,记住它是一个声明不是定义!也就是说B模块(编译单元)要是引用模块(编译单元)A中定义的全局变量或函数时,它只要包含A模块的头文件即可,在编译阶段,模块B虽然找不到该函数或变量,但它不会报错,它会在连接时从模块A生成的目标代码中找到此函数。 2 问题: extern 变量  

浅析volatile的用法

送分小仙女□ 提交于 2019-11-28 12:57:14
为什么需要volatile 在介绍volatile之前,先来了解一个GCC编译器优化的例子。 如我们有如下代码: static int foo; void bar( void ) { foo = 0 ; while (foo != 255 ) ; } 那么GCC或者其他编译器在编译的时候,发现上面的代码while处需要一直读取foo变量的值,但这个foo变量在代码中并没有被变化,就直接将其优化成下面的代码: void bar_optimized( void ) { foo = 0 ; while ( true ) ; } 这样的话,程序运行时将不用再去读取foo内存,而是直接执行while体内的代码,从而极大的提高了效率。 但是有一种情况上面的优化会出现问题。如果这个变量foo,我们将其绑定到某个io mapped memory中,这样的话,当硬件IO发生变化时,该变量可能会发生变化。但编译器却将原有代码中都该变量的正确读取优化掉了,从而产生错误。解决的办法,就是在foo变量定义的时候申明volatile,告诉编译器不要对这个变量相关的代码进行优化: static volatile int foo; volatile的好处 使用volatile,有以下好处: 1. 如前面所述,它保证编译器不对其变量进行优化,这样就能保证该IO MAPPED变量能够正常工作,见前面例子

高性能go服务之高效内存分配

雨燕双飞 提交于 2019-11-28 10:16:41
高性能go服务之高效内存分配 手动内存管理真的很坑爹(如C C++),好在我们有强大的自动化系统能够管理内存分配和生命周期,从而解放我们的双手。 但是呢,如果你想通过调整JVM垃圾回收器参数或者是优化go代码的内存分配模式话来解决问题的话,这是远远不够的。自动化的内存管理帮我们规避了大部分的错误,但这只是故事的一半。我们必须要合理有效构建我们的软件,这样垃圾回收系统可以有效工作。 在构建高性能go服务Centrifuge时我们学习到的内存相关的东西,在这里进行分享。Centrifuge每秒钟可以处理成百上千的事件。Centrifuge是Segment公司基础设施的关键部分。一致性、行为可预测是必须的。整洁、高效和精确的使用内存是实现一致性的重要部分。 这篇文章,我们将介绍导致低效率和与内存分配相关的生产意外的常见模式,以及消除这些问题的实用方法。我们会专注于分配器的核心机制,为广大开发人员提供一种处理内存使用的方法。 使用工具 首先我们建议的是避免过早进行优化。Go提供了出色的分析工具,能够直接指向内存分配密集的代码部分。没有必要重新造轮子,我们直接参考Go官方 这篇文章 即可。它为使用pprof进行CPU和分配分析提供了可靠的demo。我们在Segment中用于查找生产Go代码中的瓶颈的工具就是它,学会使用pprof是基本要求。 另外,使用数据去推动你的优化。 逃逸分析

java多线程:基础原理

送分小仙女□ 提交于 2019-11-28 08:07:24
java多线程:基础原理 java支持多线程编程,为了能够深入理解java多线程机制,以及解决多线程的安全问题,本文介绍多线程的基础知识和原理分析。 Part1 概念总结 线程的概念 线程(Thread)是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。 同一进程中的多条线程将共享该进程中的全部系统资源,如虚拟地址空间,文件描述符和信号处理等等。但同一进程中的多个线程有各自的调用栈(call stack),自己的寄存器环境(register context),自己的线程本地存储(thread-local storage)。 线程vs进程: A.多个进程的内部数据和状态都是完全独立的,(进程就是执行中的程序,程序是静态的概念,进程是动态的概念)。而多线程是共享一块内存空间和一组系统资源的,有可能互相影响(多个线程可以在一个进程中) B.线程本身的数据通常只有寄存器数据,以及一个程序执行时使用的堆栈,所以线程的切换比进程切换的负担要小。 多线程编程 在单个程序中可以同时运行多个不同的线程执行不同的任务。但是具体执行哪个线程是由cpu随机决定的。 多线程编程的目的,就是“最大限度地利用CPU资源”,当某一线程的处理不需要占用CPU而只和I/O等资源打交道时