标签: compiler-optimization

为什么C编译器不能重新排列struct成员以消除对齐填充?

可能重复:
为什么GCC不优化结构?
为什么C++不能使结构更紧凑?

请考虑以下32位x86计算机上的示例:

由于对齐约束,以下结构

struct s1 {
    char a;
    int b;
    char c;
    char d;
    char e;
}
Run Code Online (Sandbox Code Playgroud)

如果成员被重新排序,则可以更有效地表示内存(12对8字节)

struct s2 {
    int b;
    char a;
    char c;
    char d;
    char e;
}
Run Code Online (Sandbox Code Playgroud)

我知道C/C++编译器不允许这样做.我的问题是为什么语言是这样设计的.毕竟,我们最终可能会浪费大量的内存,而且struct_ref->b不会关心差异.

编辑:谢谢大家的非常有用的答案.您解释为什么由于语言的设计方式,重新排列不起作用.然而,它让我想到:如果重新排列是语言的一部分,这些论点是否仍然有效?让我们说有一些指定的重新排列规则,我们至少需要这个规则

  1. 我们应该只在实际需要时重新组织结构(如果结构已经"紧",不要做任何事情)
  2. 规则只查看结构的定义,而不是内部结构.这可确保结构类型具有相同的布局,无论它是否在另一个结构中是内部的
  3. 给定结构的编译内存布局是可预测的,因为它的定义(即规则是固定的)

我一个接一个地论证你的论点:

  • 低级别的数据映射,"最惊喜的元素":只写你的结构以紧身款式自己(像@佩里的回答),并没有发生任何改变(要求1).如果由于一些奇怪的原因,你想要内部填充,你可以使用虚拟变量手动插入,和/或可能有关键字/指令.

  • 编译器差异:要求3消除了这种担忧.实际上,从@David Heffernan的评论来看,我们今天似乎遇到了这个问题,因为不同的编译器填充不同?

  • 优化:重新排序的重点是(内存)优化.我在这看到很多潜力.我们可能无法一起删除填充,但我没有看到重新排序如何以任何方式限制优化.

  • 类型铸造:在我看来,这是最大的问题.不过,应该有办法解决这个问题.由于规则是用语言修复的,编译器能够弄清楚成员的重新排序方式,并做出相应的反应.如上所述,在您想要完全控制的情况下,始终可以防止重新排序.此外,要求2确保类型安全代码永远不会中断.

我认为这样的规则有意义的原因是因为我发现按结构内容而不是按类型对结构成员进行分组更为自然.当我有很多内部结构时,编译器也更容易选择最适合我的顺序.最佳布局甚至可能是我无法以类型安全的方式表达的布局.另一方面,它似乎使语言更复杂,这当然是一个缺点.

请注意,我不是在谈论改变语言 - 只有它可以(/应该)设计不同.

我知道我的问题是假设的,但我认为讨论提供了对机器和语言设计较低层次的更深入的了解.

我在这里很新,所以我不知道是否应该为此产生一个新问题.请告诉我是否是这种情况.

c struct memory-alignment compiler-optimization

89
推荐指数
7
解决办法
2万
查看次数

Clang优化级别

海合会,手动解释什么-O3,-Os等转化为在特定的优化参数方面(-funswitch-loops,-fcompare-elim,等)

我正在为clang寻找相同的信息.

我看过网上,man clang其中只提供一般信息(-O2优化更多-O1,-Os优化速度,...),并在此处查看Stack Overflow并发现了这一点,但我没有在引用的源文件中找到任何相关内容.

编辑:我找到了答案,但我仍然感兴趣,如果有人有链接到用户手册记录所有优化传递和选择的传球.目前我刚刚找到了这个通行证列表,但在优化级别上没有任何内容.-Ox

clang compiler-optimization user-manual

86
推荐指数
3
解决办法
7万
查看次数

为什么 GCC 不能为两个 int32s 的结构生成最佳 operator==?

一位同事向我展示了我认为没有必要的代码,但果然,确实如此。我希望大多数编译器会将所有这三种相等性测试的尝试视为等效的:

#include <cstdint>
#include <cstring>

struct Point {
    std::int32_t x, y;
};

[[nodiscard]]
bool naiveEqual(const Point &a, const Point &b) {
    return a.x == b.x && a.y == b.y;
}

[[nodiscard]]
bool optimizedEqual(const Point &a, const Point &b) {
    // Why can't the compiler produce the same assembly in naiveEqual as it does here?
    std::uint64_t ai, bi;
    static_assert(sizeof(Point) == sizeof(ai));
    std::memcpy(&ai, &a, sizeof(Point));
    std::memcpy(&bi, &b, sizeof(Point));
    return ai == bi;
}

[[nodiscard]]
bool optimizedEqual2(const Point &a, const Point &b) { …
Run Code Online (Sandbox Code Playgroud)

c++ gcc x86-64 micro-optimization compiler-optimization

86
推荐指数
3
解决办法
4658
查看次数

为什么当d == 0时'd / = d'不抛出零除异常?

我不太明白为什么我不能除以零例外:

int d = 0;
d /= d;
Run Code Online (Sandbox Code Playgroud)

我本来希望得到除以零的除法运算,但是反而d == 1

为什么在什么时候不d /= d将被零除d == 0

c++ division divide-by-zero compiler-optimization undefined-behavior

80
推荐指数
3
解决办法
5485
查看次数

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

这个问题的标题可能有点奇怪,但事实是,据我所知,根本没有什么可以反对尾部调用优化.但是,在浏览开源项目时,我已经遇到了一些主动尝试阻止编译器进行尾调用优化的函数,例如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
}
Run Code Online (Sandbox Code Playgroud)

我很想知道为什么这看起来如此重要,有没有我作为一个普通的开发人员应该保持这种想法呢?例如.尾调用优化有常见的陷阱吗?

c c++ optimization compiler-optimization tail-call-optimization

79
推荐指数
3
解决办法
3076
查看次数

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

显然在我的Windows 8笔记本电脑上使用HotSpot JDK 1.7.0_45(所有编译器/ VM选项都设置为默认值),下面的循环

final int n = Integer.MAX_VALUE;
int i = 0;
while (++i < n) {
}
Run Code Online (Sandbox Code Playgroud)

比以下情况快至少2个数量级(~10 ms vs.~5000 ms):

final int n = Integer.MAX_VALUE;
int i = 0;
while (i++ < n) {
}
Run Code Online (Sandbox Code Playgroud)

在编写循环以评估另一个不相关的性能问题时,我碰巧注意到了这个问题.之间的差异++i < ni++ < n是巨大的,足以显著影响结果.

如果我们查看字节码,更快版本的循环体是:

iinc
iload
ldc
if_icmplt
Run Code Online (Sandbox Code Playgroud)

而对于较慢的版本:

iload
iinc
ldc
if_icmplt
Run Code Online (Sandbox Code Playgroud)

因此++i < n,它首先将局部变量递增i1,然后将其推入操作数堆栈,同时i++ < n以相反的顺序执行这两个步骤.但这似乎并不能解释为什么前者要快得多.后一种情况是否涉及临时副本?或者是否应该负责性能差异的字节码(VM实现,硬件等)以外的东西?

我已经阅读了一些关于++ii++(但并非详尽无遗)的其他讨论,但未找到任何特定于Java的答案,并且与价值比较中涉及++ii++涉及的情况直接相关.

java performance compiler-optimization pre-increment post-increment

74
推荐指数
3
解决办法
9556
查看次数

是否有可能告诉分支预测器跟随分支的可能性有多大?

为了说清楚,我不打算在这里使用任何类型的便携性,所以任何将我绑定到某个盒子的解决方案都可以.

基本上,我有一个if语句将99%的时间评估为true,并且我试图剔除每个性能的最后一个时钟,我可以发出某种编译器命令(使用GCC 4.1.2和x86 ISA,如果告诉分支预测器它应该缓存该分支吗?

c x86 gcc micro-optimization compiler-optimization

73
推荐指数
4
解决办法
2万
查看次数

为什么使用三元运算符返回字符串会生成与在等效的 if/else 块中返回的代码截然不同的代码?

我正在使用编译器资源管理器,在使用类似这样的东西时,我偶然发现了三元运算符的一个有趣行为:

std::string get_string(bool b)
{
    return b ? "Hello" : "Stack-overflow";
}
Run Code Online (Sandbox Code Playgroud)

编译器为此生成的代码(clang trunk,带 -O3)是这样的:

get_string[abi:cxx11](bool):                 # @get_string[abi:cxx11](bool)
        push    r15
        push    r14
        push    rbx
        mov     rbx, rdi
        mov     ecx, offset .L.str
        mov     eax, offset .L.str.1
        test    esi, esi
        cmovne  rax, rcx
        add     rdi, 16 #< Why is the compiler storing the length of the string
        mov     qword ptr [rbx], rdi
        xor     sil, 1
        movzx   ecx, sil
        lea     r15, [rcx + 8*rcx]
        lea     r14, [rcx + 8*rcx]
        add     r14, 5 #< …
Run Code Online (Sandbox Code Playgroud)

c++ optimization assembly clang compiler-optimization

71
推荐指数
2
解决办法
3723
查看次数

这是JVM错误还是"预期行为"?

我注意到了一些意想不到的行为(意外相对于我的个人期望),我想知道是否有什么东西,如果JVM中有一个错误,或者这可能是一个边缘情况,我不明白究竟是什么细节应该发生.假设我们在main方法中有以下代码:

int i;
int count = 0;
for(i=0; i < Integer.MAX_VALUE; i+=2){
  count++;
}
System.out.println(i++);
Run Code Online (Sandbox Code Playgroud)

一个天真的期望是,这将打印Integer.MAX_VALUE-1,最大甚至可表示int.但是,我认为整数算术应该在Java中"翻转",因此Integer.MAX_VALUE应该导致添加1 Integer.MIN_VALUE.由于Integer.MIN_VALUE仍然小于Integer.MAX_VALUE,循环将继续迭代负甚至整数.最终它会回到0,这个过程应该重复为无限循环.

当我实际运行此代码时,我得到非确定性结果.打印的结果往往大约为50万,但确切的值会有所不同.因此,当我认为它应该是一个无限循环时,不仅循环终止,而且它似乎随机终止.这是怎么回事?

我的猜测是,这可能是JVM中的一个错误,或者正在进行大量的时髦优化,这会产生这种预期的行为.这是什么?

java jvm integer-overflow compiler-optimization

70
推荐指数
2
解决办法
3719
查看次数

编译器优化未使用字符串的行为不一致

我很好奇为什么下面的代码:

#include <string>
int main()
{
    std::string a = "ABCDEFGHIJKLMNO";
}
Run Code Online (Sandbox Code Playgroud)

当使用编译时,将-O3产生以下代码:

main:                                   # @main
    xor     eax, eax
    ret
Run Code Online (Sandbox Code Playgroud)

(我完全理解不需要多余的,a因此编译器可以从生成的代码中完全忽略它)

但是以下程序:

#include <string>
int main()
{
    std::string a = "ABCDEFGHIJKLMNOP"; // <-- !!! One Extra P 
}
Run Code Online (Sandbox Code Playgroud)

产量:

main:                                   # @main
        push    rbx
        sub     rsp, 48
        lea     rbx, [rsp + 32]
        mov     qword ptr [rsp + 16], rbx
        mov     qword ptr [rsp + 8], 16
        lea     rdi, [rsp + 16]
        lea     rsi, [rsp + 8]
        xor …
Run Code Online (Sandbox Code Playgroud)

c++ gcc compilation clang compiler-optimization

69
推荐指数
2
解决办法
2894
查看次数