编译器优化

C语言字节对齐问题详解

我怕爱的太早我们不能终老 提交于 2019-12-04 03:48:45
本文转自: https://www.cnblogs.com/clover-toeic/p/3853132.html 引言 考虑下面的结构体定义: 1 typedef struct{ 2 char c1; 3 short s; 4 char c2; 5 int i; 6 }T_FOO; 假设这个结构体的成员在内存中是紧凑排列的,且c1的起始地址是0,则s的地址就是1,c2的地址是3,i的地址是4。 现在,我们编写一个简单的程序: 1 int main(void){ 2 T_FOO a; 3 printf("c1 -> %d, s -> %d, c2 -> %d, i -> %d\n", 4 (unsigned int)(void*)&a.c1 - (unsigned int)(void*)&a, 5 (unsigned int)(void*)&a.s - (unsigned int)(void*)&a, 6 (unsigned int)(void*)&a.c2 - (unsigned int)(void*)&a, 7 (unsigned int)(void*)&a.i - (unsigned int)(void*)&a); 8 return 0; 9 } 运行后输出: 1 c1 -> 0, s -> 2, c2 -> 4, i -> 8 为什么会这样?这就是字节对齐导致的问题。

详解C中volatile关键字

五迷三道 提交于 2019-12-03 23:43:13
在PHP官网上看到一个浮点数BUG, 测试代码 。在SVN里看了一下修复方法:在变量的声明前加了个volatile关键字。不知道这个是什么意思,特意去网上查了一下,找到了这篇文章,写得不错,转载一下。 volatile提醒编译器它后面所定义的变量随时都有可能改变,因此编译后的程序每次需要存储或读取这个变量的时候,都会直接从变量地址中读取数据。如 果没有volatile关键字,则编译器可能优化读取和存储,可能暂时使用寄存器中的值,如果这个变量由别的程序更新了的话,将出现不一致的现象。下面举 例说明。在DSP开发中,经常需要等待某个事件的触发,所以经常会写出这样的程序: short flag; void test() { do1(); while(flag==0); do2(); } 这段程序等待内存变量flag的值变为1(怀疑此处是0,有点疑问,)之后才运行do2()。变量flag的值由别的程序更改,这个程序可能是某个硬件中 断服务程序。例如:如果某个按钮按下的话,就会对DSP产生中断,在按键中断程序中修改flag为1,这样上面的程序就能够得以继续运行。但是,编译器并 不知道flag的值会被别的程序修改,因此在它进行优化的时候,可能会把flag的值先读入某个寄存器,然后等待那个寄存器变为1。如果不幸进行了这样的 优化,那么while循环就变成了死循环

vi 编辑器,gcc 编译器的使用

六月ゝ 毕业季﹏ 提交于 2019-12-03 21:53:56
vi 编辑器 是我们在linux下经常使用的文本编辑器,这个东西也是专门为程序员准备的,基本上每个Unix和Linux系统都为我们提供了这个软件,我们可以使用vi来编写我们的代码。 在vi编辑器下所有操作都必须通过键盘和特定的快捷键组合来完成,刚开始学习的新手会感觉不太容易,但是这个东西很重要,我们先来学习如何使用它,掌握了这个东西去面试的时候也算是一门技能。 而vim是vi的增强版,新增加了很多功能。 在shell下输入命令vi 文件名,例如:vi first.c,就开始编辑first.c文件了。vi编辑器有三种模式,它们分别为 命令模式,底行命令模式和编辑模式, 各个模式的转换如下。 命令模式( 按i进入编辑模式。按":"进入底行命令模式 )。当我们vi first.c的时候一进入就是命令模式,在这个模式下,我们可以执行如下的操作。 x:删除一个字符 dd:删除光标所在行 ndd:删除n行 p:粘贴光标下行,就是把复制的内容粘贴到光标所在行的下一行。 yy:复制光标所在行 nyy:复制n行 u:恢复上一次操作 编辑模式( 按Esc回到命令模式,然后再按下:就到了底行命令模式 )。在命令模式的时候我们按下i键就进入了这个模式,这个时候就可以编辑文字了。我们输入的每个字符都会写上去,不像命令模式那样字符代表的是一种命令。 底行命令模式 :支持如下的一些常用命令

GO中的逃逸分析

纵饮孤独 提交于 2019-12-03 17:41:44
1、什么是逃逸分析 以前写c/c++代码时,为了提高效率,常常将 pass-by-value (传值)“升级”成 pass-by-reference ,企图避免构造函数的运行,并且直接返回一个指针。 那么这里还隐藏了一个很大的坑:在函数内部定义了一个局部变量,然后返回这个局部变量的地址(指针)。这些局部变量是在栈上分配的(静态内存分配),一旦函数执行完毕,变量占据的内存被销毁,任何对这个返回值的动作(如解引用),都将扰乱程序的运行,甚至导致程序直接崩溃。比如下面的这段代码: int *foo ( void) { int t = 3; return &t; } 有些同学可能知道上面的这个坑,赢了更聪明的做法:在函数内部使用new函数构造一个变量(动态内存分配),然后返回此变量的地址。因为变量是在堆上创建的,所以函数退出时不会被销毁。但是,这样就行了吗?new出来的对象该在何时何处地delete呢?调用者可能忘记delete或者直接拿掉返回值传给其他函数,之后就再也不能delete它了,也就是发生可内存泄露。 C++是公认的语法最复杂的语言,据说没有人可以完全掌握C++的语法。而这一切在Go语言中就大不相同了。像上面示例的C++代码放到Go里,没有任何问题。 你表面的光鲜,一定是背后很多人为你支撑的!GO语言里就是编译器的逃逸分析。他是编译器执行静态代码分析后

C++总结(1)keywords to the class

为君一笑 提交于 2019-12-03 15:38:26
目录 Chapter 1.关于类的关键字 1. class,struct与union 2.private,public与protected 3.friend 4.virtual 5.const 6.inline 7.template 8.delete与default 9.final与override 10.explicit 11.extern与static 12.constexpr Chapter 1.关于类的关键字 1. class,struct与union 1.1简述: ​ class是我们最为熟悉的C++类声明的关键字,便不再多提了,而C++的struct相比C中struct而言很不一样了,已经扩充了很多东西,而union是一种一种特殊的类,相比前两者就比较少用了,但也不排除有派得上用场的时候。 1.2 详解: struct与class ​ 刚从C转自C++时,大多数人总是仍把struct当作原来熟悉的包含各种数据的结构体,其实不然。士别三日,即更刮目相看。struct已经可以包含成员函数,可以继承,甚至可以多态,class能做到的,它基本上都可以做到。 ​ 那为啥还要class呢?那是因为它们二者还是有区别的,而且关键是它们的定位是不同的。 ​ 首先,struct与class最本质的区别是默认访问控制----struct是public的,class是private的。

有关nginx中Strings模块中ngx_explicit_memzero()函数的问题

别说谁变了你拦得住时间么 提交于 2019-12-03 14:16:21
1.背景 在nginx的Development guide中介绍Strings模块时,提到ngx_explicit_memzero()函数可以消除编译器的dead store elimination optimization(死区消除优化策略),使得编译器不会消除这个函数的调用。 2.ngx_explicit_memzero()函数的实现 从源码中我们可以看到该函数使用了两部分: memset(buf,0,n); __asm__ volatile("" ::: "memory"); 想必memet我们已经很清除,__asm__我们可以看出是一个内嵌的汇编语句,那么这么一段语句是如何防止编译器的dead store elimination optimization呢? 3.dead store elimination optimization简介: 首先我们要了解死区消除优化策略是如何由编译器决策的? 引用 http://gcc.gnu.org/news/dse.html 中的一段内容: GCC now tracks loads and stores more accurately within each basic block. GCC also uses alias analysis to detect when the dead store candidates can not

JIT(just in time)即时编译器

不想你离开。 提交于 2019-12-03 12:08:17
JIT(just in time) 前端vs后端 在编译原理中,通常将编译分为前端和后端。其中前端会对程序进行词法分析、语法分析、语义分析,然后生成一个中间表达形式(称为IR:Intermediate Representation)。后端再讲这个中间表达形式进行优化,最终生成目标机器码。 在Java中,javac之后生成的就是中间表达形式(.class) JVM在执行时,首先会逐条读取IR的指令来执行,这个过程就是解释执行的过程。当某一方法调用次数达到即时编译定义的阈值时,就会触发即时编译,这时即时编译器会将IR进行优化,并生成这个方法的机器码,后面再调用这个方法,就会直接调用机器码执行,这个就是编译执行的过程。 字节码-->机器码: 用的时候直接翻译(解释器) 用的时候直接翻译,并把常用的方法等缓存起来,下次再用的时候直接取用(CodeCache) 可以通过java -XX:+PrintFlagsFinal打印出所有参数的默认值 参考 JVM系列之走进JIT 为什么 JVM 不用 JIT 全程编译? JVM杂谈之JIT 深入理解Java即时编译器(JIT)-上篇 深入理解Java即时编译器(JIT)-下篇 来源: https://www.cnblogs.com/shengulong/p/11795642.html

java历史简介

那年仲夏 提交于 2019-12-03 10:31:41
java历史简介 Java 是一种计算机编程语言,拥有跨平台、面向对象、泛型编程的特性,广泛应用于企业级Web应用开发和移动应用开发。 1991 年Sun公司的James Gosling等人开始开发名称为 Oak 的语言。希望用于控制嵌入在有线电视交换盒、PDA等的微处理器 1994年将Oak语言更名为Java 1998年JDK1.2时,更名为Java 2 Platform 分为标准版J2SE,企业版J2EE,微型版J2ME Java 既安全、可移植,又可跨平台,而且人们发现它能够解决Internet 上的大型应用问题 Internet使Java成为网上最流行的编程语言 Java对Internet的影响也意义深远 B/S的兴起 & C/S的没落 任职于太阳微系统的詹姆斯•高斯林等人于1990年代初开发Java语言的雏形,最初被命名为Oak,目标设置在家用电器等小型系统的程序语言,应用在电视机、电话、闹钟、烤面包机等家用电器的控制和通信。由于这些 智能 化家电的市场需求没有预期的高,Sun公司放弃了该项计划。随着1990年代互联网的发展,Sun公司看见Oak在互联网上应用的前景,于是改造了Oak,于1995年5月以Java的名称正式发布。Java伴随着互联网的迅猛发展而发展,逐渐成为重要的网络编程语言。 Java编程语言的风格十分接近C++语言。继承了C++语言面向对象技术的核心

对JavaScript 引擎基础:原型优化的研究 -----------------------引用

萝らか妹 提交于 2019-12-03 02:12:25
一、优化层级与执行效率的取舍 介绍了现代 JavaScript 引擎通用的工作流程: 我们也指出,尽管从高级抽象层面来看,引擎之间的处理流程都很相似,但他们在优化流程上通常都存在差异。为什么呢?为什么有些引擎的优化层级会比其他引擎多一些?事实证明,在快速获取可运行的代码与花费更多时间获得最优运行性能的代码之间存在一些取舍与平衡点。 解释器可以快速生成字节码,但字节码通常效率不高。 相比之下,优化编译器虽然需要更长的时间进行处理,但最终会产生更高效的机器码。 这正是 V8 在使用的模型。它的解释器叫 Ignition,(就原始字节码执行速度而言)是所有引擎中最快的解释器。V8 的优化编译器名为 TurboFan,最终由它生成高度优化的机器码。 我们往往需要在启动延迟与执行速度之间做出一些取舍,而这便是一些 JavaScript 引擎决定是否在流程中加入优化层的原因所在。例如,SpiderMonkey 在解释器和完整的 IonMonkey 优化编译器之间添加了一个 Baseline 层: 解释器可以快速生成字节码,但字节码执行起来相对较慢。Baseline 生成代码需要花费更长的时间,但能提供更好的运行时性能。最后,IonMonkey 优化编译器花费最长时间来生成机器码,但该代码运行起来非常高效。 让我们通过一个具体的例子,看看不同引擎中的优化流程都有哪些差异

iOS编译器

匿名 (未验证) 提交于 2019-12-03 00:40:02
Objective-C 和 Swift都是编译语言 编译语言在执行的时候,必须先通过编译器生成机器码 CPU执行机器码 LLVM 编译编译语言 LLVM 是一个模块化和可重用的编译器和工具链技术的集合 LLVM 核心库提供一个优化器,对流行的 CPU 做代码生成支持 Clang 是 LLVM 的子项目,是 C,C++ 和 Objective-C 编译器 clang static analyzer 主要是进行语法分析,语义分析和生成中间代码,对代码进行检查,出错的和需要警告的会标注出来。 lld 是 Clang / LLVM 的内置链接器,clang 必须调用链接器(lld)来产生可执行文件 1,编译源文件的时候,编译器首先做的是一些预处理工作。比如预处理器会处理源文件中的宏定义,将代码中的宏用其对应定义的具体内容进行替换。 2,.m 源文件里都有一堆的声明和定义,这些代码文本都会从string 转化成特殊的标记流,每一个标记流都包含了对应的 源码内容 和在源码中的位置. 注意这里的位置是宏展开之前的位置,这样一来,如果编译过程中遇到什么问 题,clang 能够在源码中指出出错的具体位置 3,标记流将会被解析成一棵抽象语法树 (abstract syntax tree -- AST),在抽象语法树中的每个节点都标注了其对应源码中的位置,同样的,如果产生了什么问题,clang