C++编译原理,O1O2O3编译优化
1.从.c⽂件到可执⾏⽂件,其间经历了⼏步?
⾼级语⾔是偏向⼈,按照⼈的思维⽅式设计的,机器对这些可是莫名奇妙,不知所谓。那从⾼级语⾔是如何过渡到机器语⾔的呢?这可是⼀个漫长的旅途呀!
其中,得经历这样的历程:C源程序->编译预处理->编译->汇编程序->链接程序->可执⾏⽂件
1.预处理 读取c源程序,对其中的伪指令(以#开头的指令)和特殊符号进⾏处理。伪指令主要包括以下四个⽅⾯:
(1)宏定义指令,如#define Name TokenString,#undef等。对于前⼀个伪指令,预编译所要作得的是将程序中的所有Name⽤TokenString替换,但作为字符串常量的Name则不被替换。对于后者,则将取消对某个宏的定义,使以后该串的出现不再被替换。
(2)条件编译指令,如#ifdef,#ifndef,#el,#elif,#endif,等等。这些伪指令的引⼊使得程序员可以通过定义不同的宏来决定编译程序对哪些代码进⾏处理。预编译程序将根据有关的⽂件,将那些不必要的代码过滤掉。
vegetable是可数名词吗
(3)加载头⽂件,如#include"FileName"或者#include<FileName>等。采⽤头⽂件的⽬的主要是为了使某些定义可以供多个不同的C源程序使⽤。因为在需要⽤到这些定义的C源程序中,只需加上⼀条#include语句即可,⽽不必再在此⽂件中将这些定义重复⼀遍。预编译程序将把头⽂件中的定义统统都加⼊到它所产⽣的输出⽂件中,以供编译程序对之进⾏处理。包含到c源程序中的头⽂件可以是系统提供的,这些头⽂件⼀般被放在/usr/include⽬录下。在程序中#include它们要使⽤尖括号(<>)。另外开发⼈员也可以定义⾃⼰的头⽂件,这些⽂件⼀般与c源程序放在同⼀⽬录下,此时在#include中要⽤双引号("")。
预编译是将.c ⽂件转化成 .i⽂件,
重定向使⽤的gcc命令是:gcc –E hello.c >hello.i
在预处理阶段是不做语法检查的。
2.编译阶段 :
需要进⾏三个步骤:词法分析、语法分析和语义分析
在linux环境中,输⼊命令:gcc–s hello.c 参数c告诉gcc命令只进⾏编译,不做其他处理。命令运⾏结束后产⽣hello.o的⽬标⽂件。
dedicated
3.汇编过程
编译过程实际上指把汇编语⾔代码翻译成⽬标机器指令的过程。对于被翻译系统处理的每⼀个C语⾔源程序,都将最终经过这⼀处理⽽得到相应的⽬标⽂件。⽬标⽂件中所存放的也就是与源程序等效的⽬标的机器语⾔代码。
输⼊命令:gcc –c hello.c
就会⽣成hello.o的⽬标⽂件。
韩语入门4.链接过程
链接就是将不同部分的代码和数据收集和组合成为⼀个单⼀⽂件的过程,这个⽂件可被加载或拷贝到存储器执⾏.
链接可以执⾏与编译时(源代码被翻译成机器代码时),也可以执⾏与加载时(在程序被 加载器加载到存储器并执⾏时),甚⾄执⾏与运⾏时,由应⽤程序来执⾏.在现代系统中, 链接是由链接器⾃动执⾏的.
链接器分为:静态链接器和动态链接器两种.
tinami
(1).静态链接器
静态链接器以⼀组可重定位⽬标⽂件和命令⾏参数作为输⼊,⽣成⼀个完全链接的可以加载和运⾏的可执⾏⽬标⽂件作为输出.
静态链接器主要完成两个任务:
1>符号解析:⽬标⽂件定义和引⽤符号.符号解析的⽬的在于将每个符号引⽤和⼀个符号定义联系起来.
2>重定位:编译器和汇编器⽣成从地址零开始的代码和数据节.链接器通过把每个符号定义和⼀个存储器位置联系起来,然后修改所有对这些符号的引⽤,使得他们执⾏这个存储位置,从⽽重定位这些节.
(2)动态链接器
共享库是⼀个⽬标模块,在运⾏时,可以加载到任意的存储器地址,并在存储器中和⼀个程序链接起来.这个过程称为动态链接,是由动态链接器完成的.
共享库的共享在两个⽅⾯有所不同.⾸先,在任何给定的⽂件系统中,对于⼀个库只有⼀个.so⽂件.所有引⽤该库德可执⾏⽬标⽂件共享这
个.so⽂件中的代码和数据,⽽不是像静态库的内容那样被拷贝和嵌⼊到引⽤它们的可执⾏的⽂件中.其次,在存储器中,⼀个共享库的.text只有⼀个副本可以被不同的正在运⾏的进程共享。
2.-O1,-O2,-O3为何⽅神圣,它们是如何优化编译⽂件的?
(1)⾸先,她们的真⾯⽬是:
-O1 提供基础级别的优化
-O2提供更加⾼级的代码优化,会占⽤更长的编译时间
-O3提供最⾼级的代码优化
可以使⽤-f命令⾏选项引⽤每个单独的优化技术。
1, 编译器优化级别1
在优化的第⼀个级别执⾏基础代码的优化
这个级别试图执⾏9种单独的优化功能:
(1).-fdefer-pop: 这种优化技术与汇编语⾔代码在函数完成时如何进⾏操作有关。
(2).-fmerge-constans: 使⽤这种优化技术, 编译器试图合并相同的常量.
(3) . -fthread-jumps: 使⽤这种优化技术与编译器如何处理汇编代码中的条件和⾮条件分⽀有关。 在某些情况下, ⼀条跳转指令可能转移到另⼀条分⽀语句。 通过⼀连串跳转, 编译器确定多个跳转之间的最终⽬标并且把第⼀个跳转重新定向到最终⽬标。
tackle
重庆翻译公司(4).-floop-optimize:通过优化如何⽣成汇编语⾔中的循环, 编译器可以在很⼤程序上提⾼应⽤程序的性能。通常, 程序由很多⼤型且复杂的循环构成。 通过删除在循环内没有改变值的变量赋值操作, 可以减少循环内执⾏指令的数量, 在很⼤程度上提⾼性能。 此外优化那些确定何时离开循环的条件分⽀,以便减少分⽀的影响。
(5).-fif-conversion: if-then语句应该是应⽤程序中仅次于循环的最消耗时间的部分。简单的if-then语句可能在最终的汇编语⾔代码中产⽣众多的条件分⽀。 通过减少或者删除条件分⽀, 以及使⽤条件传送 设置标志和使⽤运算技巧来替换他们, 编译器可以减少if-then语句中花费的时间量。
(6)-fif-conversion2: 这种技术结合更加⾼级的数学特性, 减少实现if-then语句所需的条件分⽀。
(7)-fdelayed-branch: 这种技术试图根据指令周期时间重新安排指令。 它还试图把尽可能多的指令移动到条件分⽀前, 以便最充分的利⽤处理器的治理缓存。
(8) -fguess-branch-probability:就像其名称所暗⽰的, 这种技术试图确定条件分⽀最可能的结果, 并且相应的移动指令, 这和延迟分⽀技术类似。因为在编译时预测代码的安排,所以使⽤这⼀选项两次编译相同的c或者c++代码很可能会产⽣不同的汇编语⾔代码,这取决于编译时编译器认为会使⽤那些分⽀。
(9)-fcprop-registers: 因为在函数中把寄存器分配给变量, 所以编译器执⾏第⼆次检查以便减少调度依赖性(两个段要求使⽤相同的寄存器)并且删除不必要的寄存器复制操作。
cousins2, 编译器优化级别2
结合了第⼀个级别的所有优化技术,再加上⼀下⼀些优化:
(1)-fforce-mem: 这种优化在任何指令使⽤变量前, 强制把存放再内存位置中的所有变量都复制到寄存器中。 对于只涉及单⼀指令的变量, 这样也许不会有很⼤的优化效果. 但是对于在很多指令(必须数学操作)中都涉及到的变量来说, 这会时很显著的优化, 因为和访问内存中的值相⽐ ,处理器访问寄存器中的值要快的多。
(2)-foptimize-sibling-calls: 这种技术处理相关的和/或者递归的函数调⽤。通常,递归的函数调⽤可以被展开为⼀系列⼀般的指令, ⽽不是使⽤分⽀。
(3)-fstrength-reduce: 这种优化技术对循环执⾏优化并且删除迭代变量。 迭代变量是捆绑到循环计数器的变量, ⽐如使⽤变量, 然后使⽤循环计数器变量执⾏数学操作的for-next循环。
(4)-fgc: 这些优化操作试图分析⽣成的汇编语⾔代码并且结合通⽤⽚段, 消除冗余的代码段。如果代码使⽤计算性的goto,gcc指令推荐
(5)-fc-follow-jumps: 这种特别的通⽤⼦表达式消除技术扫描跳转指令, 查找程序中通过任何其他途径都不会到达的⽬标代码。这种情况最常见的例⼦就式if-then-el语句的el部分。
(6)-frerun-c-after-loop: 这种技术在对任何循环已经进⾏过优化之后重新运⾏通⽤⼦表达式消除例程。这样确保在展开循环代码之后更进⼀步地优化还编代码。
(7)-fdelete-null-pointer-checks: 这种优化技术扫描⽣成的汇编语⾔代码, 查找检查空指针的代码。
英语速成教材
(8)-fextensive-optimizations: 这种技术执⾏从编译时的⾓度来说代价⾼昂的各种优化技术,但是它可能对运⾏时的性能产⽣负⾯影响。
(9)-fregmove: 编译器试图重新分配mov指令中使⽤的寄存器, 并且将其作为其他指令操作数, 以便最⼤化捆绑的寄存器的数量。
(10)-fschedule-insns: 编译器将试图重新安排指令, 以便消除等待数据的处理器。对于在进⾏浮点运算时有延迟的处理器来说, 这使处理器在等待浮点结果时可以加载其他指令。
(11)-fsched-interblock: 这种技术使编译器能够跨越指令块调度指令。 这可以⾮常灵活地移动指令以便等待期间完成的⼯作最⼤化。
(12)-fcaller-saves: 这个选项指⽰编译器对函数调⽤保存和恢复寄存器, 使函数能够访问寄存器值, ⽽且不必保存和恢复他们。 如果调⽤多个函数, 这样能够节省时间, 因为只进⾏⼀次寄存器的保存和恢复操作, ⽽不是在每个函数调⽤中都进⾏。
(13)-fpeephole2: 这个选项允许进⾏任何计算机特定的观察孔优化。
(14)-freorder-blocks: 这种优化技术允许重新安排指令块以便改进分⽀操作和代码局部性。
(15)-fstrict-aliasing: 这种技术强制实⾏⾼级语⾔的严格变量规则。 对于c和c++程序来说, 它确保不在数据类型之间共享变量. 例如, 整数变量不和单精度浮点变量使⽤相同的内存位置。
上海网球培训
(16)-funit-at-a-time:这种优化技术指⽰编译器在运⾏优化例程之前读取整个汇编语⾔代码。这使编译器可以重新安排不消耗⼤量时间的代码以便优化指令缓存。
(17)-falign-functions:这个选项⽤于使函数对准内存中特定边界的开始位置。⼤多数处理器按照页⾯读取内存,并且确保全部函数代码位于单⼀内存页⾯内, 就不需要叫化代码所需的页⾯。
(18)-fcrossjumping: 这是对跨越跳转的转换代码处理, 以便组合分散在程序各处的相同代码。 这样可以减少代码的长度,但是也许不会对程序性能有直接影响。leaking
3, 编译器优化级别3
它整合了第⼀和第⼆级别中的左右优化技巧, 还包括⼀下优化:
-finline-functions:这种优化技术不为函数创建单独的汇编语⾔代码,⽽是把函数代码包含在调度程序的
代码中。 对于多次被调⽤的函数来说, 为每次函数调⽤复制函数代码。 虽然这样对于减少代码长度不利, 但是通过最充分的利⽤指令缓存代码, ⽽不是在每次函数调⽤时进⾏分⽀操作, 可以提⾼性能。
-fweb: 构建⽤于保存变量的伪寄存器⽹络。 伪寄存器包含数据, 就像他们是寄存器⼀样, 但是可以使⽤各种其他优化技术进⾏优化, ⽐如c 和loop优化技术。
-fgc-after-reload:这中技术在完全重新加载⽣成的且优化后的汇编语⾔代码之后执⾏第⼆次gc优化,帮助消除不同优化⽅式创建的任何冗余段。