我正在使用一种int类型来存储一个值.根据程序的语义,值总是在很小的范围内变化(0 - 36),并且仅使用int(不是a char)因为CPU效率.
似乎可以在如此小的整数范围内执行许多特殊的算术优化.可以将对这些整数的许多函数调用优化为一小组"神奇"操作,并且甚至可以将某些函数优化为表查找.
那么,是否有可能告诉编译器这int总是在那么小的范围内,并且编译器是否可以进行这些优化?
The C++ Standard Library (Second Edition)Nicolai Josuttis 在其着作中指出,编译器可以比普通函数更好地优化lambdas.
此外,C++编译器比普通函数更好地优化lambdas.(第213页)
这是为什么?
我认为在内联时不应该有任何差别.我能想到的唯一原因是编译器可能有一个更好的本地上下文与lambdas,这样可以做出更多假设并执行更多优化.
我正在使用GCC 4.3编译我的C++应用程序.而不是手动选择我正在使用的优化标志-march=native,理论上应该添加适用于我正在编译的硬件的所有优化标志.但是我如何检查它实际使用的是哪些标志?
在无形状中,Nat类型表示在类型级别编码自然数的方法.这用于例如固定大小的列表.您甚至可以在类型级别上进行计算,例如,将N元素列表附加到元素列表中,K并返回在编译时已知的具有N+K元素的列表.
这种表示是否能够表示大数,例如1000000或2 53,或者这会导致Scala编译器放弃吗?
GCC 6具有一个新的优化器功能:它假定this始终不为null并基于此进行优化.
值范围传播现在假定C++成员函数的this指针是非null.这消除了常见的空指针检查,但也打破了一些不符合规范的代码库(例如Qt-5,Chromium,KDevelop).作为临时解决方法,可以使用-fno-delete-null-pointer-checks.使用-fsanitize = undefined可以识别错误的代码.
更改文档清楚地将其称为危险,因为它打破了大量频繁使用的代码.
为什么这个新假设会破坏实用的C++代码?是否存在粗心或不知情的程序员依赖于这种特定的未定义行为的特定模式?我无法想象有人写作,if (this == NULL)因为那是不自然的.
我用 C 语言实现了一个冒泡排序,并在测试其性能时发现该-O3标志使其运行速度甚至比没有标志时还要慢!与此同时-O2,它的运行速度比预期的要快得多。
没有优化:
time ./sort 30000
./sort 30000 1.82s user 0.00s system 99% cpu 1.816 total
Run Code Online (Sandbox Code Playgroud)
-O2:
time ./sort 30000
./sort 30000 1.00s user 0.00s system 99% cpu 1.005 total
Run Code Online (Sandbox Code Playgroud)
-O3:
time ./sort 30000
./sort 30000 2.01s user 0.00s system 99% cpu 2.007 total
Run Code Online (Sandbox Code Playgroud)
代码:
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <time.h>
int n;
void bubblesort(int *buf)
{
bool changed = true;
for (int i = n; changed == true; …Run Code Online (Sandbox Code Playgroud) C11 标准似乎暗示不应优化带有常量控制表达式的迭代语句。我从这个答案中得到了我的建议,它特别引用了标准草案中的第 6.8.5 节:
其控制表达式不是常量表达式的迭代语句......可能会被实现假定为终止。
在该答案中,它提到while(1) ;不应进行优化之类的循环。
那么……为什么 Clang/LLVM 优化了下面的循环(用 编译cc -O2 -std=c11 test.c -o test)?
#include <stdio.h>
static void die() {
while(1)
;
}
int main() {
printf("begin\n");
die();
printf("unreachable\n");
}
Run Code Online (Sandbox Code Playgroud)
在我的机器上,这会打印出begin,然后在非法指令(ud2放置在 之后的陷阱die())上崩溃。在 Godbolt 上,我们可以看到调用puts.
让 Clang 输出无限循环是一项非常困难的任务-O2- 虽然我可以反复测试一个volatile变量,但这涉及到我不想要的内存读取。如果我做这样的事情:
#include <stdio.h>
static void die() {
while(1)
;
}
int main() {
printf("begin\n");
volatile int x …Run Code Online (Sandbox Code Playgroud) 在阅读Mysticial对问题的精彩回答时,这个问题浮现在脑海中:为什么处理排序数组比处理未排序数组更快?
涉及类型的上下文:
const unsigned arraySize = 32768;
int data[arraySize];
long long sum = 0;
Run Code Online (Sandbox Code Playgroud)
在他的回答中,他解释说英特尔编译器(ICC)对此进行了优化:
for (int i = 0; i < 100000; ++i)
for (int c = 0; c < arraySize; ++c)
if (data[c] >= 128)
sum += data[c];
Run Code Online (Sandbox Code Playgroud)
......变成与此相当的东西:
for (int c = 0; c < arraySize; ++c)
if (data[c] >= 128)
for (int i = 0; i < 100000; ++i)
sum += data[c];
Run Code Online (Sandbox Code Playgroud)
优化器认识到这些是等效的,因此交换循环,将分支移动到内循环之外.非常聪明!
但为什么不这样做呢?
for (int …Run Code Online (Sandbox Code Playgroud) 我最近遇到了一个奇怪的去优化(或者错过了优化机会).
考虑此函数可以有效地将3位整数数组解包为8位整数.它在每次循环迭代中解包16个int:
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 >> 15) & 0x7;
target[6] = (t >> 18) & 0x7;
target[7] = (t >> 21) & 0x7;
target[8] = (t >> 24) & 0x7;
target[9] = …Run Code Online (Sandbox Code Playgroud) c++ optimization strict-aliasing compiler-optimization c++11
这是我作为一个经验不足的程序员经常遇到的情况,我特别想知道我正在努力优化的一个雄心勃勃,速度密集的项目.对于主要的类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
}
}
Run Code Online (Sandbox Code Playgroud)
基本上,是否有提前break或return早期的直接效率奖金/罚款?堆栈框架是如何涉及的?是否有优化的特殊情况?是否有任何因素(如内联或"Do stuff"的大小)可能会对此产生重大影响?
我总是支持改进可读性而非次要优化(我通过参数验证看到foo1很多),但这种情况经常发生,我想一劳永逸地抛开所有的担忧.
而且我知道过早优化的陷阱......呃,这些都是一些痛苦的回忆.
编辑:我接受了答案,但EJP的答案非常简洁地解释了为什么使用a return几乎可以忽略不计(在汇编中,return在函数末尾创建一个'分支',这非常快.分支改变了PC寄存器和也可能会影响缓存和管道,这是非常小的.)特别是对于这种情况,它实际上没有区别,因为它们if/else和return函数末尾都创建了相同的分支.