Tag: 编译器优化

这是一个JVM的错误或“预期的行为”?

我注意到一些意想不到的行为(与我个人的期望相关),我想知道如果JVM中有一个错误,或者这是一个附带案例,我不明白什么是什么细节应该会发生。 假设我们在主要方法中有以下代码: int i; int count = 0; for(i=0; i < Integer.MAX_VALUE; i+=2){ count++; } System.out.println(i++); 一个天真的期望是,这将打印Integer.MAX_VALUE-1 ,最大的偶数可表示的int 。 不过,我相信整数算术应该在Java中“滚动”,所以将1加到Integer.MAX_VALUE应该导致Integer.MIN_VALUE 。 由于Integer.MIN_VALUE仍然小于Integer.MAX_VALUE ,所以循环会持续迭代负整数。 最终它会回到0,这个过程应该重复为一个无限循环。 当我真的运行这个代码时,我得到了非确定性的结果。 打印的结果往往是五十万的数量级,但确切的数值是变化的。 所以当我相信它应该是一个无限循环时,不仅循环终止,而且似乎随机终止。 这是怎么回事? 我的猜测是,这是JVM中的一个错误,或者有很多时髦的优化,使得这种预期的行为。 这是哪个?

为什么“while(i ++ <n){}”明显慢于“while(++ i <n){}”

显然,在我的Windows 8笔记本电脑上安装了HotSpot JDK 1.7.0_45(所有编译器/虚拟机选项设置为默认),下面的循环 final int n = Integer.MAX_VALUE; int i = 0; while (++i < n) { } 至less快两个数量级(〜10ms对比~5000ms): final int n = Integer.MAX_VALUE; int i = 0; while (i++ < n) { } 在写一个循环来评估另一个不相关的性能问题时,我碰巧注意到了这个问题。 ++i < n和i++ < n之间的差异足够大,可以显着影响结果。 如果我们看字节码,更快版本的循环体是: iinc iload ldc if_icmplt 而对于较慢的版本: iload iinc ldc if_icmplt 因此,对于++i < n ,它首先将局部variablesi递增1,然后将其推到操作数栈上,而i++ […]

为什么C编译器不能重新排列结构成员来消除alignment填充?

可能重复: 为什么GCC不优化结构? 为什么C ++不使结构更紧密? 考虑在32位x86机器上的以下示例: 由于alignment约束,下面的结构 struct s1 { char a; int b; char c; char d; char e; } 可以更有效地代表内存(12比8字节),如果成员被重新sorting的话 struct s2 { int b; char a; char c; char d; char e; } 我知道C / C ++编译器不允许这样做。 我的问题是为什么语言是这样devise的。 毕竟,我们最终可能会浪费大量的内存, struct_ref->b引用不会关心这个区别。 编辑 :谢谢大家的非常有用的答案。 你很好地解释了为什么重新安排因为devise语言的方式而不起作用。 但是,这让我想:如果重新安排是语言的一部分,这些论据是否仍然会成立? 假设有一些特定的重排规则,我们至less要求这样做 我们应该只在实际需要时重新组织结构(如果结构已经“紧”,不要做任何事情) 该规则仅查看结构的定义,而不是内部结构。 这确保了一个结构types具有相同的布局,而不pipe它是否在另一个结构中是内部的 给定结构的编译内存布局是可预测的,因为它的定义(即规则是固定的) 我一个接一个地说我的理由: 低级数据映射,“最不可思议的元素” :只要你自己写一个简洁的结构(比如@Perry的答案),没有什么变化(需求1)。 如果出于某种奇怪的原因,您希望内部填充在那里,您可以使用虚拟variables手动插入它,和/或可能有关键字/指令。 […]

为什么代码会主动尝试阻止尾部呼叫优化?

这个问题的标题可能有点奇怪,但事实是,就我所知,根本就没有什么能说明跟尾部优化有关。 然而,在浏览开源项目时,我已经遇到了一些主动尝试阻止编译器进行尾部调用优化的函数,例如CFRunLoopRef的实现,它充满了这样的黑客攻击 。 例如: static void __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__() __attribute__((noinline)); static void __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__(CFRunLoopObserverCallBack func, CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info) { if (func) { func(observer, activity, info); } getpid(); // thwart tail-call optimization } 我很想知道为什么这看起来如此重要,是否有任何情况下我作为​​一个正常的开发人员应该保持这种心态呢? 例如。 尾部呼叫优化有常见的缺陷吗?

为什么不能(或不)编译器将一个可预测的加法循环优化成一个乘法?

在读到Mysticial关于如下问题的精彩答案时, 想到了这个问题: 为什么处理sorting后的数组比处理未sorting的数组更快 ? 涉及types的上下文: const unsigned arraySize = 32768; int data[arraySize]; long long sum = 0; 在他的回答中,他解释说,英特尔编译器(ICC)优化了这一点: for (int i = 0; i < 100000; ++i) for (int c = 0; c < arraySize; ++c) if (data[c] >= 128) sum += data[c]; …相当于这样的东西: for (int c = 0; c < arraySize; ++c) if (data[c] […]

function过早返回的效率

这是我经常遇到的一个缺乏经验的程序员的情况,我特别想知道我正在试图优化的一个雄心勃勃,高速度的项目。 对于主要的类C语言(C,objC,C ++,Java,C#等)及其通常的编译器,这两个函数的运行效率如何? 编译代码有没有区别? void foo1(bool flag) { if (flag) { //Do stuff return; } //Do different stuff } void foo2(bool flag) { if (flag) { //Do stuff } else { //Do different stuff } } 从根本上来说,是否有一个直接的效率奖金/罚款提前break或return ? 如何涉及到堆栈? 有没有优化特殊情况? 是否有任何因素(如内联或“做什么”的大小)可能会显着影响这一点? 我总是支持提高可读性,而不是轻微的优化(我在参数validation中看到foo1),但是这样频繁出现,我想一劳永逸地放下所有的担心。 而且我意识到过早优化的陷阱…呃,那些是一些痛苦的回忆。 编辑:我接受了一个答案,但EJP的答案很简洁地解释了为什么使用return几乎可以忽略不计(在汇编中, return创build了一个'分支'到函数结束,这是非常快速的分支改变PC注册,也可能会影响caching和pipe道,这是非常微不足道的。)对于这种情况下,特别是,它没有区别,因为无论是if/else和return创build相同的分支到函数的结尾。

使用这个指针会导致热循环中出现奇怪的去最佳化

我最近遇到了一个奇怪的去优化(或者说错过了优化的机会)。 考虑将3位整数数组有效解包为8位整数的函数。 它在每个循环迭代中解包16个整数: void unpack3bit(uint8_t* target, char* source, int size) { while(size > 0){ uint64_t t = *reinterpret_cast<uint64_t*>(source); target[0] = t & 0x7; target[1] = (t >> 3) & 0x7; target[2] = (t >> 6) & 0x7; target[3] = (t >> 9) & 0x7; target[4] = (t >> 12) & 0x7; target[5] = (t >> […]

如何查看哪些标志-march = native将激活?

我使用GCC 4.3编译我的C ++应用程序。 我没有手动select优化标志,而是使用-march=native ,理论上应该添加所有适用于我正在编译的硬件的优化标志。 但是,我怎样才能检查它实际使用哪个标志?

为什么增强的GCC 6优化器打破实用的C ++代码?

GCC 6有一个新的优化器function :它假定this总是不为空,并基于this优化。 值范围传播现在假定C ++成员函数的这个指针是非空的。 这消除了常见的空指针检查, 但也打破了一些不合格的代码库(如Qt-5,Chromium,KDevelop) 。 作为一个临时的work-around -fno-delete-null-pointer-checks可以被使用。 错误的代码可以通过使用-fsanitize = undefined来识别。 更改文件清楚地表明这是危险的,因为它打破了令人吃惊的常用代码量。 为什么这个新的假设会打破实用的C ++代码呢? 有没有特定的模式,不小心或不知情的程序员依赖这种特定的未定义的行为? 我无法想象任何人if (this == NULL)因为这是不自然的写作。

纳特types的限制在无形中

在无形中,Nattypes表示一种在types级别编码自然数的方法。 这用于例如固定大小的列表。 你甚至可以在types层次上进行计算,例如在K元素列表中追加N元素的列表,并获得在编译时已知的具有N+K元素的列表。 这种表示能够表示大数字,例如1000000或2 53 ,还是这会导致Scala编译器放弃?