【问题标题】:C programming and error_code variable efficiencyC 编程和 error_code 变量效率
【发布时间】:2010-12-22 10:04:55
【问题描述】:

我读过的大多数代码都使用int 进行标准错误处理(从函数等返回值)。但我想知道使用uint_8 是否有任何好处将编译器 - 阅读:大多数架构上的大多数 C 编译器 - 使用立即地址模式生成指令 - 即嵌入 1 字节整数进入指令?我正在考虑的关键指令是函数后的比较,使用 uint_8 作为其返回类型,返回。

我可能想错了,因为引入 1 字节类型只会导致对齐问题——编译器喜欢将东西打包成 4 字节可能有一个完全理智的原因,这可能是每个人都只使用整数的原因-- 因为这是堆栈相关的问题而不是堆,所以没有真正的开销。

做正确的事是我的想法。但是为了争论,可以说这是一个流行的廉价微处理器,用于智能手表,它配置有 1k 的内存,但在其指令集中确实有不同的寻址模式:D

另一个稍微专门化讨论的问题(x86)是:文字在:

uint_32 x=func(); x==1;

uint_8 x=func(); x==1;

同一类型?或者编译器会在第二种情况下生成一个 8 字节的文字。如果是这样,它可以使用它来生成一个比较指令,该指令将文字作为立即值,并将返回的 int 作为寄存器引用。 See CMP instruction types..

Another Refference for the x86 Instruction Set.

【问题讨论】:

  • 我认为您正在担心通常所说的“微优化”。你有具体的上下文吗?

标签: c cpu-architecture micro-optimization machine-instruction low-level-code


【解决方案1】:

这是一个特定的编译器将对以下代码执行的操作:

extern int foo(void) ;
void bar(void)
{
        if(foo() == 31) { //error code 31
                do_something();
        } else {
                do_somehing_else();
        }
}

   0:   55                      push   %ebp
   1:   89 e5                   mov    %esp,%ebp
   3:   83 ec 08                sub    $0x8,%esp
   6:   e8 fc ff ff ff          call   7 <bar+0x7>
   b:   83 f8 1f                cmp    $0x1f,%eax
   e:   74 08                   je     18 <bar+0x18>
  10:   c9                      leave
  11:   e9 fc ff ff ff          jmp    12 <bar+0x12>
  16:   89 f6                   mov    %esi,%esi
  18:   c9                      leave
  19:   e9 fc ff ff ff          jmp    1a <bar+0x1a>

用于 cmp 的 3 字节指令。如果 foo() 返回一个 char ,我们得到 b: 3c 1f cmp $0x1f,%al

如果您正在寻找效率。不要假设比较 %a1 中的内容比比较 %eax 更快

【讨论】:

    【解决方案2】:

    特定架构上的不同积分类型之间可能存在非常小的速度差异。但是你不能依赖它,如果你移动到不同的硬件,它可能会改变,如果你升级到新的硬件,它甚至可能运行得更慢。

    如果您在给出的示例中谈论 x86,您会做出错误的假设:立即数需要是 uint8_t 类型。

    实际上嵌入指令的 8 位立即数是 int8_t 类型,可以与字节、字、双字和四字一起使用,用 C 表示法:charshortintlong long .

    所以在这种架构上根本没有任何好处,代码大小和执行速度都没有。

    【讨论】:

    • 所以如果到处都使用 4 字节的返回类型,我可以假设 CPU 架构师和编译器编写者同意这一点,让我不屑一顾:D?
    • 我认为这是某种共同进化:C 是围绕典型的冯诺依曼架构发展的第一个非常有效的抽象。很快,微处理器开始有效地实现典型的 C 寻址模式。所以,是的,编译器作者和处理器设计者同意这一点;-)
    • 太棒了 :D 很高兴我从来没有写过任何带有 8 字节返回类型的代码。非常感谢 drhirsch。
    【解决方案3】:

    您应该使用 int 或 unsigned int 类型进行计算。仅对化合物(结构/数组)使用较小的类型。原因是 int 通常被定义为处理器的“最自然”的整数类型,所有其他派生类型可能需要处理才能正常工作。我们在 Solaris for SPARC 上使用 gcc 编译的项目中,访问 8 位和 16 位变量的情况在代码中添加了一条指令。从内存中加载较小的类型时,必须确保正确设置了寄存器的上部(有符号类型的符号扩展或无符号的 0)。这使代码变长并增加了寄存器的压力,从而恶化了其他优化。

    我有一个具体的例子:

    我将结构的两个变量声明为 uint8_t 并在 Sparc Asm 中获取该代码:

        if(p->BQ > p->AQ)
    

    被翻译成

    ldub    [%l1+165], %o5  ! <variable>.BQ,
    ldub    [%l1+166], %g5  ! <variable>.AQ,
    and     %o5, 0xff, %g4  ! <variable>.BQ, <variable>.BQ
    and     %g5, 0xff, %l0  ! <variable>.AQ, <variable>.AQ
    cmp     %g4, %l0    ! <variable>.BQ, <variable>.AQ
    bleu,a,pt %icc, .LL586  !
    

    当我将这两个变量声明为 uint_t 时,我得到了什么

    lduw    [%l1+168], %g1  ! <variable>.BQ,
    lduw    [%l1+172], %g4  ! <variable>.AQ,
    cmp     %g1, %g4    ! <variable>.BQ, <variable>.AQ
    bleu,a,pt %icc, .LL587  !
    

    减少两个算术运算,增加两个寄存器用于其他东西

    【讨论】:

      【解决方案4】:

      处理器通常喜欢使用其自然寄存器大小,在 C 中是“int”。

      虽然有例外,但你在一个不存在的问题上想的太多了。

      【讨论】:

      • 确实。我是,但如果它可以使用立即寻址,它就不会占用寄存器,不是吗?
      • @Vainstah:这完全没有目的:寄存器更快,调用/cmp/jz 序列无法进一步优化,如果其他人需要该寄存器,您将有一个上下文切换..
      • 指令中有两个元素,硬编码比较字面量和返回值。返回值不能硬编码,但前者可以。例如,在 x86 中,CMP 指令可以将其两个操作数作为 REG 和 IMMED。如果两个元素都有 4 个字节,它们都必须在寄存器中。但是硬编码的文字是 8 字节的,它可以放入指令中。
      • 如果您查看流行架构的指令集,您会发现它们各自的 CMP 指令实现支持高达 32/64 位立即数常量。
      • (假设是 8 位拱门)相关指令将使用指向 32 位值的指针,而不是直接的 8 位值。对于 x86,您可以看到情况并非如此:siyobik.info/index.php?module=x86
      猜你喜欢
      • 2011-08-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-02-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多