当前位置:首页 » 编程软件 » 编译器折叠方法

编译器折叠方法

发布时间: 2023-08-11 15:41:22

❶ 现代C/C++编译器有多智能

最近在搞C/C++代码的性能优化,发现很多时候自以为的优化其实编译器早就优化过了,得结合反汇编才能看出到底要做什么样的优化。
请熟悉编译器的同学结合操作系统和硬件谈一谈现代c/c++编译器到底有多智能吧。哪些书本上的优化方法其实早就过时了?
以及程序员做什么会让编译器能更好的自动优化代码?
举个栗子:
1,循环展开,大部分编译器设置flag后会自动展开;
2,顺序SIMD优化,大部分编译器设置flag后也会自动优化成SIMD指令;
3,减少中间变量,大部分编译器会自动优化掉中间变量;
etc.
查看代码对应的汇编:
Compiler Explorer
【以下解答】
举个之前看过的例子:
int calc_hash(signed char *s){ static const int N = 100003; int ret = 1; while (*s) { ret = ret * 131 + *s; ++ s; } ret %= N; if (ret < 0) ret += N; //注意这句 return ret;}
【以下解答】
举个简单例子,一到一百求和
#include int sum() { int ret= 0; int i; for(i = 1; i <= 100; i++) ret+=i; return ret;}int main() { printf("%d\n", sum()); return 0;}
【以下解答】
话题太大,码字花时间…
先放传送门好了。
请看Google的C++编译器组老大Chandler Carruth的演讲。这个演讲是从编译器研发工程师的角度出发,以Clang/LLVM编译C++为例,向一般C++程序员介绍理解编译器优化的思维模型。它讲解了C++编译器会做的一些常见优化,而不会深入到LLVM具体是如何实现这些优化的,所以即使不懂编译原理的C++程序员看这个演讲也不会有压力。
Understanding Compiler Optimization - Chandler Carruth - Opening Keynote Meeting C++ 2015
演示稿:https://meetingcpp.com/tl_files/mcpp/2015/talks/meetingcxx_2015-understanding_compiler_optimization_themed_.pdf
录像:https://www.youtube.com/watch?v=FnGCDLhaxKU(打不开请自备工具…)
Agner Fog写的优化手册也永远是值得参考的文档。其中的C++优化手册:
Optimizing software in C++ - An optimization guide for Windows, Linux and Mac platforms - Agner Fog
要稍微深入一点的话,GCC和LLVM的文档其实都对各自的内部实现有不错的介绍。
GCC:GNU Compiler Collection (GCC) Internals
LLVM:LLVM’s Analysis and Transform Passes
========================================
反模式(anti-patterns)
1. 为了“优化”而减少源码中局部变量的个数
这可能是最没用的手工“优化”了。特别是遇到在高级语言中“不用临时变量来交换两个变量”这种场景的时候。
看另一个问题有感:有什么像a=a+b;b=a-b;a=a-b;这样的算法或者知识? - 编程
2. 为了“优化”而把应该传值的参数改为传引用
(待续…)
【以下解答】
推荐读一读这里的几个文档:
Software optimization resources. C++ and assembly. Windows, Linux, BSD, Mac OS X
其中第一篇:http://www.agner.org/optimize/optimizing_cpp.pdf
讲解了C++不同领域的优化思路和问题,还有编译器做了哪些优化,以及如何代码配合编译器优化。还有优化多线程、使用向量指令等的介绍,推荐看看。
感觉比较符合你的部分需求。
【以下解答】
一份比较老的slides:
http://www.fefe.de/source-code-optimization.pdf
【以下解答】
利用C++11的range-based for loop语法可以实现类似python里的range生成器,也就是实现一个range对象,使得
for(auto i : range(start, stop, step))
【以下解答】
我觉得都不用现代。。。。寄存器分配和指令调度最智能了
【以下解答】
每次编译poco库的时候我都觉得很为难GCC
【以下解答】
有些智能并不能保证代码变换前后语义是等价的
【以下解答】
诶诶,我错了各位,GCC是可以借助 SSE 的 xmm 寄存器进行优化的,经 @RednaxelaFX 才知道应该添加 -march=native 选项。我以前不了解 -march 选项,去研究下再来补充为什么加和不加区别这么大。
十分抱歉黑错了。。。以后再找别的点来黑。
误导大家了,实在抱歉。(??ˇ?ˇ??)
/*********以下是并不正确的原答案*********/
我是来黑 GCC的。
最近在搞编译器相关的活,编译OpenSSL的时候有一段这样的代码:
BN_ULONG a0,a1,a2,a3; // EmmetZC 注:BN_ULONG 其实就是 unsigned longa0=B[0]; a1=B[1]; a2=B[2]; a3=B[3];A[0]=a0; A[1]=a1; A[2]=a2; A[3]=a3;
【以下解答】
提示:找不到对象
【以下解答】
忍不住抖个机灵。
私以为正常写代码情况下编译器就能优化,才叫智能编译器。要程序员绞尽脑汁去考虑怎么写代码能让编译器更好优化,甚至降低了可读性,那就没有起到透明屏蔽的作用。
智能编译器应该是程序猿要较劲脑汁才能让编译器不优化。
理论上是这样的。折叠我吧。
【以下解答】
编译器智能到每次我都觉得自己很智障。
【以下解答】
虽然题主内容里是想问编译器代码性能优化方面的内容,但题目里既然说到编译器的的智能,我就偏一下方向来说吧。
有什么更能展示编译器的强大和智能?
自然是c++的模版元编程
template meta programming
简单解释的话就是写代码的代码,写的还是c++,但能让编译器在编译期间生成正常的c++代码。
没接触过的话,是不是听上去感觉就是宏替换的加强版?感觉不到它的强大呢?
只是简单用的话,效果上这样理解也没什么
但是一旦深入下去,尤其翻看大神写的东西,这明明看着就是c++的代码,但TM怎么完全看不懂他在干什么?后来才知道这其实完全是另外一个世界,可是明明是另外一个世界的东西但它又可以用来做很多正常c++能做的事....
什么?你说它好像不能做这个,不能做那个,好像做不了太多东西,错了,大错特错。就像你和高手考试都考了100分的故事一样,虽然分数一样,但你是努力努力再努力才得了满分,而高手只是因为卷面分只有100分.....在元编程面前,只有想不到,没有做不到。
再回头看看其他答案,编译器顺手帮你求个和,丢弃下无用代码,就已经被惊呼强大了,那模板元编程这种几乎能在编译期直接帮你“生成”包含复杂逻辑的c++代码,甚至还能间接“执行”一些复杂逻辑,这样的编译器是不是算怪兽级的强大?
一个编译器同时支持编译语法相似但结果不同却又关联的两种依赖语言,这个编译器有多强大多智能?
写的人思维都要转换几次,编译器转着圈嵌着套翻着番儿地编译代码的代码也肯定是无比蛋疼的,你说它有多强大多智能?
一个代码创造另外一个代码,自己能按照相似的规则生成自己,是不是听上去已经有人工智能的发展趋势了?
上帝说,要有光,于是有了光。
老子曰,一生二,二生三,三生万物。
信c++,得永生!
===
FBI WARNING:模板元编程虽然很强大,但也有不少缺点,尤其对于大型项目,为了你以及身边同事的身心健康,请务必适度且谨慎的使用。勿乱入坑,回头是岸。
【以下解答】
c++11的auto自动类型推断算么....
【以下解答】
智能到开不同级别的优化,程序行为会不同 2333
【以下解答】
这个取决于你的水平

❷ C++中常量赋值是如何规定的

1、值替代
c语言中预处理器用值代替名字:

#define BUFSIZE 100

这种方式在C++中依然适用,同时

C++用const把值代替带进编译器领域:
const bufsize = 100 ;或 const int bufsize = 100 ;
同时还可以执行常量折叠:
char buf[bufsize] ;
1.1 头文件里的const
与使用#define一样,使用const必须把const定义放在头文件里。这样,通过包含头文件,可把const定义单独放在一个地方,并把她分配给一个编译单元。const默认为内部连接,即仅在const被定义过的文件里才是可见的,而在连接时不能被其他编译单元看到。
比如说在Test.h中定义了,

const int nTestConst = 1000;

在A.cpp中include"Test.h"
同时在B.cpp中include"Test.h"
不会出现,当没有定义为const时,因变量定义出现两次而出错的情形.
当定义一个const常量时,必须赋一个值给它,除非用extern作了清楚的说明:

extern const bufsize ;

虽然上面的extern强制进行了存储空间分配,但C++编译器并不为const分配存储空间,相反它把这个定义保存在它的符号表里。当const被使用时,它在编译时会进行常量折叠。

1.2 const的安全性
如果想要一个值保持不变,就应该使它成为一个常量(const)。
1.3 集合
const可以用于集合,但必须分配内存,意味着“不能改变的一块存储”。但其值在编译时不能被使用。


const int i [] = {1,2,3,4};
// float f [ i [3] ]; // Illegal

2,指针
当使用带有指针的const时,有两种选择:1、或者const修饰指针正指向的对象,2、const修饰存储在指针本身的地址。
2.1 指向const的指针
使用指针定义的技巧,正如任何复杂的定义一样,是在标识符的开始处读它并从里到外读。如果我们要使正指向的元素不发生改变,得写:

const int * x;

从标识符开始:“x 是一个指针,指向一个const int。”
把const从一边移到另一边,结果一样。
int const * x;
2.2 cosnt指针
是指针本身成为一个const指针,必须把const标明的部分放在*的右边,如:

int d = 1;
int * const x = &d;

指针本身是const指针,编译器要求给它一个初始化值,这个值在指针寿命期间不变。然而要改变它所指向的值是可以的,可以写*x = 2。
这些可以总结成这样一句话:
以*为分界点,
当const在*的左边时,实际物体值不变
当const在*的右边时,指针不变,
即,左物右指
这样来记比较方便!!

❸ keil编译器前面的这些 加减符号如何修改

选中你想要能够收缩的代码,右键-->outlining --> collapse Selection ;就可以将自己想要收缩的代码行收缩咯,collapse 为折叠的意思,outlining里面还有几个选项,可以自己试试功能。 我也是网上查找无果,还是花了点时间才发现的哦,望采纳!

javashort怎么-1

注:如未特别说明,Java语言规范 jls 均基于JDK8,使用环境是 eclipse4.5 + win10 + JDK 8
本篇的知识点,主要是涉及到 Java 中一些比较答橡雹常见的默认窄化处理(Java编译器自动添加的),这里将从一个问题开始,据说这也是一道常见的笔试题/面试题:

为什么 short i = 1; i += 1; 可以正确编译运行而 short i = 1; i = i + 1; 会出现编译错误?

其他说法:都放在一起编译会出现有什么结果,哪一行报错?为什么?

笔者注:其实这其中会涉及到一些编译优化和底层的知识,限于知识面,本篇不涉及,如有需要,可自行搜索。

本文的目录结构如下:

1、结论

关于开篇提出的问题,这里先直接给出结论:

Java语言规范规定基础数据类型运算默认使用32位精度的int类型

只要是对基本类型做窄化处理的,例如 long -> int -> short -> char,都需要做强制转换,有些是Java编译器默认添加的,有的则是代码中显式做强制转换的。

short i = 1; i += 1;可以正确编译运行是因为Java编译器自己添加了强制窄化处理,即对于任何的T a; X b; a += b;等价于T a; X b; a = (T) (a + b);Java编译器会默认做这个显式强制转换(尽管有时候会出现精度问题,例如 b 是 float 、 double 类型,强烈建议不要有这样的操作)。前面的i += 1其实就等价于i = (int) (i + 1),即便将数字1换成是double类型的1.0D也是如此。

short i = 1; i = i + 1;编译不通过的原因就很明显了:无论是代码中,还是Java编译器,都没有做强制转换,int 类型直接赋给 short ,因此编译出错。

对于常量(数字常量、常量表达式、final常量等),Java编译器同样也可以做默认的强制类型转换,只要常量在对应清帆的数据范围内即可。

2、详如歼解

接下来讲详细分析为什么 short i = 1; i += 1; 可以正确编译而 short i = 1; i = i + 1; 则会编译失败。先列一下搜出来的一些令人眼前一亮(or 困惑)的代码

public static voidmain(String[] args) {//注:short ∈ [-32768, 32767]

{/** 1、对于 +=, -=, *=, /=, Java编译器默认会添加强制类型转换,

* 即 T a; X b; a += b; 等价于 T a; X b; a = (T) (a + b);*/

//0是int类型的常量,且在short范围内,被Java编译器默认强制转换的

short i = 0;

i+= 1; //等价于 i = (short) (i + 1);

System.out.println("[xin01] i=" + i); //输出结果: 1

i = (short) (i + 1);

System.out.println("[xin02] i=" + i); //输出结果: 2

/** 下面这2行都会有编译报错提示:

* Exception in thread "main" java.lang.Error: Unresolved compilation problem:

* Type mismatch: cannot convert from int to short

* [注]错误: 不兼容的类型: 从int转换到short可能会有损失

* Eclipse 也会有提示: Type mismatch: cannot convert from int to short*/

//i = i + 1;//i = 32768;

i= 0;

i+= 32768; //等价于 i = (short) (i + 32768); 下同

System.out.println("[xin03] i=" + i); //输出结果: -32768

i += -32768;

System.out.println("[xin04] i=" + i); //输出结果: 0

i= 0;long j = 32768;

i+=j;

System.out.println("[xin05] i=" + i); //输出结果: -32768

i= 0;float f = 1.23F;

i+=f;

System.out.println("[xin06] i=" + i); //(小数位截断)输出结果: 1

i= 0;double d = 4.56D;

i+=d;

System.out.println("[xin07] i=" + i); //(小数位截断)输出结果: 4

i= 10;

i*= 3.14D;

System.out.println("[xin08] i=" + i); //输出结果: 31

i= 100;

i/= 2.5D;

System.out.println("[xin09] i=" + i); //输出结果: 40

}

{/** 2、常量表达式和编译器优化: 常量折叠*/

//2 * 16383 = 32766//(-2) * 16384 = -32768//都在 short 范围内,常量表达式在编译优化后直接用对应的常量结果,然后编译器做强制转换

short i = 2 * 16383; //等价于 short i = (short) (2 * 16383);

short j = (-2) * 16384;//2 * 16384 = 32768,超过 short 范围,编译器不会做转换//Type mismatch: cannot convert from int to short//short k = 2 * 16384;//常量表达式在编译优化后直接用对应的常量结果,然后编译器做强制转换

short cThirty = 3 * 10;short three = 3;short ten = 10;//Type mismatch: cannot convert from int to short//short thirty = three * ten;

final short fTthree = 3;final short fTen = 10;//常量表达式在编译优化后直接用对应的常量结果,然后编译器做强制转换

short fThirty = fTthree *fTen;final short a = 16384;final short b = 16383;//常量表达式在编译优化后直接用对应的常量结果,然后编译器做强制转换

short c = a +b;

}

}

接下来根据代码罗列的两部分分别进行说明:

2.1、对于 +=, -=, *=, /=, Java编译器默认会添加强制类型转换,即 T a; X b; a += b; 等价于 T a; X b; a = (T) (a + b);

A compound assignment expression of the formE1 op= E2is equivalent toE1 = (T) ((E1) op (E2)), whereTis the type ofE1, except thatE1is evaluated only once.

For example, the following code is correct:

short x = 3;

x+= 4.6;

and results in x having the value 7 because it is equivalent to:

short x = 3;

x= (short)(x + 4.6);

笔者注:

实际上,直接加上强制类型转换的写法,也是大家都熟悉且理解起来最清晰的方式,可以避免可能潜在的类型不匹配时出现的精度损失问题,使用的时候需要注意。当然,笔者认为这些方式都没有好坏之分,正确地使用即可。

Java从语言规范层面对此做了限制。有兴趣的还可以通过 class文件和 javap -c 反汇编对所使用的字节码作进一步的研究。

知道了Java语言相关的规范约定,我们就可以看出,与之对应的是以下这种出现编译错误的写法(报错提示:Type mismatch: cannot convert from int to short):

short i = 1;//i + 1 是 int 类型,需要强制向下类型转换

i = i + 1;

2.2、常量表达式和编译器优化: 常量折叠

需要注意的是,前面的示例short x = 3;中的3其实默认是 int 类型,但是却可以赋值给short类型的x。这里涉及到到的其实是 常量表达式。

In addition, if the expression is a constant expression (

A narrowing primitive conversion may be used if the type of the variable is byte, short, or char, and the value of the constant expression is representable in the type of the variable.

A narrowing primitive conversion followed by a boxing conversion may be used if the type of the variable is:

Byte and the value of the constant expression is representable in the type byte.

Short and the value of the constant expression is representable in the type short.

Character and the value of the constant expression is representable in the type char.

对于常量表达式,其结果是可以自动做窄化处理的,只要是在对应的数据类型范围内,Java编译器就进行做默认强制类型转换。

Some expressions have a value that can be determined at compile time. These are constant expressions (

true(short)(1*2*3*4*5*6)

Integer.MAX_VALUE/ 2

2.0 *Math.PI"The integer " + Long.MAX_VALUE + " is mighty big."

A constant variable is a final variable of primitive type or type String that is initialized with a constant expression (

通过常量表达式赋值的final变量都是常量,这种也是编译期可以确认最终值,会通过编译优化直接赋予最终值,而且可以直接依靠编译器做窄化处理。

对于这一块,其实对应的是一个比较基础的编译器优化:常量折叠(Constant Folding),有兴趣的可以自行搜索。stackoverflow 上由相关的讨论,参考[9]、[10]、[11]

笔者注:

尽管常量表达式最终都被编译器优化为直接值,但是为了清晰,提高可读性、可维护性,代码中没必要对常量直接换算,例如一天 24 * 60 * 60 秒,其实可以分别用可读性更强的final常量来表示。

Bloch大神的 Java Puzzlers 中也有相关的一些说明,有兴趣的可以去看看

❺ C语言编写好代码后,怎么编译,最后生成可执行文件

材料/工具:vc6.0

1、打开c语言编辑工具,在工具中写入程序的源代码。

热点内容
最快脚本语言 发布:2025-02-04 22:27:23 浏览:525
安卓的人脸识别在哪里 发布:2025-02-04 22:16:45 浏览:673
悠然服务器的ip是什么 发布:2025-02-04 22:10:17 浏览:65
3des源码 发布:2025-02-04 22:09:16 浏览:808
如何备份数据库表 发布:2025-02-04 22:09:07 浏览:294
如何删除下载的闹钟铃声安卓 发布:2025-02-04 22:03:35 浏览:658
死神脚本 发布:2025-02-04 21:57:03 浏览:167
phpposthtml 发布:2025-02-04 21:37:46 浏览:89
最新asp源码 发布:2025-02-04 21:17:33 浏览:571
让linux死机 发布:2025-02-04 20:48:08 浏览:141