标签: compiler-optimization

哪些 GCC 优化标志对二进制大小影响最大?

我正在使用 GCC 为 ARM 开发 C++。我遇到了一个问题,我没有启用优化,我无法为我的代码创建二进制文件(ELF),因为它不适合可用空间。但是,如果我只是启用调试优化(-Og)(据我所知这是可用的最低优化),代码就很容易适应。

在这两种情况下,都会启用-ffunction-sections-fdata-sections-fno-exceptions-Wl,--gc-sections 。

  • 闪存大小:512 kB
  • 没有优化:.text 溢出约 200 kB
  • 使用-Og优化:.text 约为 290 kB

即使进行了最小的优化,二进制大小也存在巨大差异。

我查看了3.11 控制优化的选项,详细了解使用 -Og 标志执行哪些优化,看看这是否会给我任何见解。

哪些优化标志对二进制大小影响最大?我应该寻找什么来解释这种巨大的差异吗?

c++ gcc arm code-size compiler-optimization

29
推荐指数
2
解决办法
9484
查看次数

Visual Studio构建中"优化代码"选项的优点

我们的大部分C#发布代码都是在关闭"优化代码"选项的情况下构建的.我相信这是为了让发布模式内置的代码更容易调试.

鉴于我们正在创建相当简单的桌面软件,连接到后端Web服务(即,不是特别是处理器密集型应用程序),那么如果可能出现任何类型的性能影响呢?

是否有任何特定平台可能受到更严重的影响?例如.多处理器/ 64位.

c# compiler-construction build compiler-optimization visual-studio

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

是否有可能保证在C++中没有优化代码执行内存写入?

允许C++编译器优化写入内存:

 {
     //all this code can be eliminated
     char buffer[size];
     std::fill_n( buffer, size, 0);
 }
Run Code Online (Sandbox Code Playgroud)

处理敏感数据时,典型方法是使用volatile*指针来确保编译器发出内存写入.以下是SecureZeroMemory()Visual C++运行时库中的函数实现方式(WinNT.h):

FORCEINLINE PVOID RtlSecureZeroMemory(
     __in_bcount(cnt) PVOID ptr, __in SIZE_T cnt )
{
    volatile char *vptr = (volatile char *)ptr;
#if defined(_M_AMD64)
    __stosb((PBYTE )((DWORD64)vptr), 0, cnt);
#else
    while (cnt) {
        *vptr = 0;
        vptr++;
        cnt--;
    }
#endif
    return ptr;
}
Run Code Online (Sandbox Code Playgroud)

该函数将传递的指针强制转换为volatile*指针,然后通过后者写入.但是,如果我在局部变量上使用它:

char buffer[size];
SecureZeroMemory( buffer, size );
Run Code Online (Sandbox Code Playgroud)

变量本身不是volatile.因此根据C++标准定义的可观察行为写入buffer不计入可观察行为,看起来它可以被优化掉.

现在有很多关于页面文件,缓存等的评论都是有效的,但是我们在这个问题中忽略它们.这个问题唯一的问题是存储器写入的代码是否被优化掉了.

是否有可能确保在C++中没有优化写入内存的代码?该解决方案SecureZeroMemory()是否符合C++标准?

c++ compiler-construction optimization compiler-optimization

28
推荐指数
1
解决办法
1493
查看次数

强制GCC执行memcpy运行时大小检查的循环非开关?

有没有什么可靠的办法迫使海湾合作委员会(或编译器)中分解出运行时的大小检查memcpy()在循环外(如该尺寸不编译时间常数,但环内常数),专门为各相关尺寸范围内环路而不是反复检查其中的大小?

这是一个测试案例,从这里报告的性能回归中减少了一个开源库,该库设计用于大数据集的高效内存中分析.(回归恰好是因为我的一个提交...)

原始代码在Cython中,但我已将其简化为纯C代理,如下所示:

void take(double * out, double * in,
          int stride_out_0, int stride_out_1,
          int stride_in_0, int stride_in_1,
          int * indexer, int n, int k)
{
    int i, idx, j, k_local;
    k_local = k; /* prevent aliasing */
    for(i = 0; i < n; ++i) {
        idx = indexer[i];
        for(j = 0; j < k_local; ++j)
            out[i * stride_out_0 + j * stride_out_1] =
            in[idx * stride_in_0 + j * stride_in_1];
    }
}
Run Code Online (Sandbox Code Playgroud)

步伐是可变的; 一般来说,数组甚至不能保证是连续的(因为它们可能是较大数组的非连续切片.)但是,对于c连续数组的特殊情况,我已将上述内容优化为以下内容: …

c c++ performance memcpy compiler-optimization

28
推荐指数
1
解决办法
1719
查看次数

为什么编译器会产生4字节负载而不是1字节负载,而较宽的负载可能会访问未映射的数据?

我有一个填充了可变长度记录的字节缓冲区,其长度由记录的第一个字节决定.用于读取单个记录的缩减版C函数

void mach_parse_compressed(unsigned char* ptr, unsigned long int* val)
{
    if (ptr[0] < 0xC0U) {
        *val = ptr[0] + ptr[1];
        return;
    }

  *val = ((unsigned long int)(ptr[0]) << 24)
      | ((unsigned long int)(ptr[1]) << 16)
      | ((unsigned long int)(ptr[2]) << 8)
      | ptr[3];
}
Run Code Online (Sandbox Code Playgroud)

生成汇编(x86_64上的GCC 5.4 -O2 -fPIC),首先在ptr加载4个字节,将第一个字节与0xC0进行比较,然后处理两个,即四个字节.未定义的字节被正确丢弃,但为什么编译器认为首先加载四个字节是安全的?由于ptr没有例如对齐要求,因此它可能指向存储页面的最后两个字节,这是我们所知道的未映射的存储页面的最后两个字节,从而导致崩溃.

再生都需要-fPIC和-O2或更高.

我在这里错过了什么吗?编译器是否正确执行此操作以及如何解决此问题?

我可以通过mmap/mprotect获得上面显示的Valgrind/AddressSanitiser错误或崩溃:

//#define HEAP
#define MMAP
#ifdef MMAP
#include <unistd.h>
#include <sys/mman.h>
#include <stdio.h>
#elif HEAP
#include <stdlib.h>
#endif

void
mach_parse_compressed(unsigned char* ptr, unsigned long int* val)
{
    if (ptr[0] < …
Run Code Online (Sandbox Code Playgroud)

c compiler-optimization

28
推荐指数
1
解决办法
430
查看次数

为什么编译器不会在集合的元素上优化空的ranged-for循环?

在测试我的代码时,我注意到当for loop删除空的范围时,执行时间显着增加.通常我会认为编译器会注意到for循环没有用处,因此会被忽略.作为编译器标志我正在使用-O3(gcc 5.4).我还使用向量而不是集合来测试它,这似乎可以工作并在两种情况下都给出相同的执行时间.似乎迭代器的增量花费了所有额外的时间.

第一种情况下,ranged for循环仍然存在(慢):

#include <iostream>
#include <set>
int main () {
    long result;
    std::set<double> results;
    for (int i = 2; i <= 10000; ++i) {
        results.insert(i);
        for (auto element : results) {
            // no operation
        }
    }
    std::cout << "Result: " << result << "\n";
}
Run Code Online (Sandbox Code Playgroud)

第二种情况,删除了范围for循环(快速):

#include <iostream>
#include <set>
int main () {
    long result;
    std::set<double> results;
    for (int i = 2; i <= 10000; ++i) {
        results.insert(i);
    }
    std::cout …
Run Code Online (Sandbox Code Playgroud)

c++ compiler-optimization

28
推荐指数
4
解决办法
3458
查看次数

如何防止gcc优化器产生错误的位操作?

考虑以下程序.

#include <stdio.h>

int negative(int A) {
    return (A & 0x80000000) != 0;
}
int divide(int A, int B) {
    printf("A = %d\n", A);
    printf("negative(A) = %d\n", negative(A));
    if (negative(A)) {
        A = ~A + 1;
        printf("A = %d\n", A);
        printf("negative(A) = %d\n", negative(A));
    }
    if (A < B) return 0;
    return 1;
}
int main(){
    divide(-2147483648, -1);
}
Run Code Online (Sandbox Code Playgroud)

在没有编译器优化的情况下编译它时,它会产生预期的结果.

gcc  -Wall -Werror -g -o TestNegative TestNegative.c
./TestNegative
A = -2147483648
negative(A) = 1
A = -2147483648
negative(A) = 1 …
Run Code Online (Sandbox Code Playgroud)

c gcc compiler-optimization

28
推荐指数
4
解决办法
4151
查看次数

如何在Haskell中使CAF不是CAF?

如何将常量适用表格制作成一个不变的适用表格,以阻止它在程序的整个生命周期中保留?

我尝试过这种方法:

-- | Dummy parameter to avoid creating a CAF
twoTrues :: () -> [[[Bool]]]
twoTrues _ = map (++ (True : repeat False)) . trueBlock <$> [1..]
Run Code Online (Sandbox Code Playgroud)

但它似乎不起作用 - 配置文件显示它仍然保留,仍然将其标记为CAF.

我发现了一个相关的Google结果,Simon Peyton-Jones对Neil Mitchell 的回复,他正好问了这个问题 - 但不幸的是,答案指的是一个死链接.

haskell ghc compiler-optimization

27
推荐指数
4
解决办法
2287
查看次数

GCC(/ Clang):使用相同的指令合并函数(COMDAT折叠)

好奇的是,GCC或Clang工具集目前是否实现了相同的MSVC 相同的COMDAT折叠(ICF)?如果没有,有什么计划吗?除了旧的GCC邮件列表消息之外,我似乎无法找到关于该主题的任何最新权威链接.

如果不是,这是否意味着不同类型的模板实例化在结果二进制文件中始终是不同的函数(在它们没有完全内联的情况下),即使它们是二进制兼容的,或者是否存在其他机制来处理它在其他一些水平?

此外,有没有人发现ICF在最大限度地减少实际可执行文件的大小方面有很大的不同?我没有任何大型MSVC项目可以方便地进行测试.(我猜它只是真的有帮助,如果你碰巧在许多不同的vtable-layout兼容类型上实例化模板.)

最后,是否符合C++ 11标准的两个函数指针指向不同的函数,以便在运行时进行相等比较?这个链接似乎意味着它不是,但它适用于C99.编辑:发现此主题的上一个问题

linker gcc templates clang compiler-optimization

27
推荐指数
1
解决办法
5343
查看次数

为什么GCC和Clang不进行混叠优化?

我有一个案例,朋友将类型为"Base"的非基类对象强制转换为类类型对象"Derived",其中"Derived"是"Base"的派生类,只添加函数,但没有数据.在下面的代码中,我确实x向派生类添加了一个数据成员

struct A {
  int a;
};

struct B : A {
  // int x;
  int x;
};

A a;

int g(B *b) {
   a.a = 10;
   b->a++;
   return a.a;
}
Run Code Online (Sandbox Code Playgroud)

通过严格的别名分析,GCC(也是Clang)总是返回10,而不是11,因为b永远不会指向a明确定义的代码.但是,如果我删除B::x(因为实际上是在我朋友的代码的情况下),GCC的输出汇编代码并没有优化的回归访问a.a并重新加载从内存中值.因此我的朋友的代码g在GCC(按照他的意图)调用"有效",即使我认为它仍然有未定义的行为

g((B*)&a);
Run Code Online (Sandbox Code Playgroud)

因此,在基本相同的两种情况下,GCC优化了一种情况并且没有优化另一种情况.是因为b可以合法地指出a?或者是因为GCC只是想破坏现实世界的代码?


我测试了答案

如果删除B :: x,那么B符合9p7中对标准布局类的要求,并且访问变得非常明确,因为这两种类型是布局兼容的,9.2p17.

有两个布局兼容的枚举

enum A : int { X, Y };
enum B : int { Z …
Run Code Online (Sandbox Code Playgroud)

c++ optimization gcc strict-aliasing compiler-optimization

27
推荐指数
2
解决办法
1406
查看次数