【问题标题】:Understanding the Optimization of C code on ARM platform using GCC Compiler使用 GCC 编译器了解 ARM 平台上 C 代码的优化
【发布时间】:2023-03-18 17:55:01
【问题描述】:

我在 ARM 上进行 c 编程,其中内存占用和速度都有非常严格的限制。我正在使用 GSL-2.1 库,它几乎所有的功能都是双倍的,但我的硬件在硬件上没有浮点,所以它都是在软件中完成的。因此,它会产生额外的代码大小并降低执行速度。我的处理器有 180KB SRAM 和 1MB 闪存。现在我想提高速度和内存占用,所以我查看了 IDE 编译器设置并获得了以下设置。

我已阅读 some thread 关于 GCC 优化级别的内容,但这里还有一些我不太了解的设置。您能否详细说明针对 ARM Cortex-M 处理器的 GCC 的每个设置。

更新: 我随机选中/取消选中了一些框,但代码大小没有任何差异。

【问题讨论】:

  • 问题是什么?您应该决定准备在速度和大小之间进行权衡。你不能同时拥有两者。
  • 当您显然只是在谈论 C 时,为什么要标记为 C++?
  • 还有一些用 c++ 编写的附加文件。
  • 研究“数据驱动设计”和“数据缓存优化”。一个简单的技巧是将内容声明为“静态常量”,以便编译器可以直接访问数据,而不是制作本地副本。
  • 在我的一个嵌入式系统上,我们使用了 4096 的定点分母(由于 ADC 计数)。这就是“内部代表”。固定点将在输出到用户或从用户输入期间转换。我们的 ARM7 喜欢完整的数据指令。我还将数据移动到一个结构中以获得更好的数据缓存行并加快速度。

标签: c gcc optimization


【解决方案1】:

当使用 gcc 为嵌入式系统编写代码时,重要的是要注意,与许多允许存储的嵌入式系统编译器不同,可以将存储作为一种类型编写并作为另一种类型读取,并且将以至少某种可预测的方式处理整数溢出,除非使用-fno-strict-aliasing-fwrapv 标志编译,否则在使用gcc 编译时,依赖于此类行为的代码很容易中断。例如,虽然 C 标准的作者会期望函数

// Assume usmall is an unsigned value half the size of unsigned int
unsigned multiply(usmall x, usmall y) { return x*y; }

在二进制补码硬件平台上应该是安全的,溢出时无声回绕,他们不要求编译器以这种方式实现它(我想他们可能没想到任何正在写作的人除非模拟其他平台,否则这种平台的编译器会很迟钝,否则会这样做)。但是,当使用 gcc 编译时,该函数可能会产生意想不到的副作用。

同样,在许多编译器上,例如

struct widget_header {uint16_t length; uint8_t type_id;};
struct acme_widget {uint16_t length; uint8_t type_id; uint8_t dat[5];};
struct beta_widget {uint16_t length; uint8_t type_id; uint32_t foo;};

指向任何这些类型的指针可以转换为widget_header;代码 然后可以查看 type_id 字段并转换为更具体的类型。 然而,这样的技术并不总是适用于 gcc;即使是工会 包含所有三种类型的声明都在范围内,gcc 将假定 对其中一种类型的字段的访问不可能修改 任何其他的相应字段。

一个更具体的例子来展示 gcc 如何处理别名:

    struct s1 { int x; };
    struct s2 { int x; };
    union { struct s1 v1; struct s2 v2; } u;

    static int peek(void *p)
    {
      struct s1 *p1 = (struct s1*)p;
      return *(int*)&p1->x;
    }

    static void setToFive(void *p)
    {
      struct s2 *p2 = (struct s2*)p;
      *(int*)(&p2->x) = 5;
    }

    static int test1a(void *p, void *q)
    {
      struct s1 *p1 = (struct s1*)p;
      if (peek(p)!=23) return 0;
      setToFive(q);
      return peek(p);
    }

    int test1(void)
    {
      struct s2 v2 = {23};
      u.v2 = v2;
      return test1a(&u.v1, &u.v2);
    }

ARM gcc 4.8.2 生成

test1:
        movw    r3, #:lower16:u
        movt    r3, #:upper16:u
        movs    r2, #5
        movs    r0, #23
        str     r2, [r3]
        bx      lr

将 5 存储到“u”中,然后返回 23(假设第二次调用 peek 将返回与第一次相同的值,尽管所有指针类型转换都应该为编译器,某些东西可能在某处有别名)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-06-08
    • 2014-08-29
    • 1970-01-01
    • 1970-01-01
    • 2015-12-27
    • 1970-01-01
    相关资源
    最近更新 更多