C代码分析

C代码分析,c,cpu-registers,type-punning,C,Cpu Registers,Type Punning,下面是我在64位linux机器上编写的函数 void myfunc(unsigned char* arr) //array of 8 bytes is passed by reference { unsigned long a = 0; //8 bytes unsigned char* LL = (unsigned char*) &a; LL[0] = arr[6]; LL[1] = arr[3]; LL[2] = arr[1]; LL[3] = a

下面是我在64位linux机器上编写的函数

void myfunc(unsigned char* arr) //array of 8 bytes is passed by reference
{
   unsigned long a = 0; //8 bytes
   unsigned char* LL = (unsigned char*) &a;

   LL[0] = arr[6];
   LL[1] = arr[3];
   LL[2] = arr[1];
   LL[3] = arr[7];
   LL[4] = arr[5];
   LL[5] = arr[4];
   LL[6] = arr[0];
   LL[7] = arr[2];
}
现在我的问题是:

  • 变量“a”是否会被存储在寄存器中,这样就不会从RAM或chache中一次又一次地访问它
  • 在64位体系结构上工作时,我是否应该假设“arr”数组将存储在寄存器中,因为函数参数存储在64位arch中的寄存器中
  • 指针类型转换的效率如何?我的猜测是,它应该是没有效率的
  • 任何帮助都将受到感谢


    关于

    您最好使用显式移位和掩码指令来实现这一点,而不是使用数组索引

    数组操作将使编译器更难为此使用寄存器,因为通常没有执行“从寄存器A的第三个字节加载8位”之类操作的指令。(一个优化编译器可能会发现用移位/掩码可以做到这一点,但我不确定这种可能性有多大)

  • a
    不能存储在寄存器中,因为您已经获取了它的地址。(valdo正确地指出,一个真正智能的编译器可以优化对位操作的数组访问,并在寄存器中保留
    a
    ,但我从未见过编译器这样做,我不确定它是否会更快)
  • arr
    (指针本身)存储在寄存器中(
    %edi
    ,在amd64上)。数组的内容在内存中
  • 指针类型转换本身通常不会生成任何代码。然而,使用类型转换做一些愚蠢的事情可能会导致非常低效的代码,甚至导致行为未定义的代码
  • 看起来您正试图将字节排列在一个数组中,然后将它们放入一个数字中,而您的示例生成的机器代码对此并不坏。David建议改为使用移位和掩码操作是很好的(如果您的代码需要在big-endian机器上运行,这也可以避免问题),还有SSE vector permute指令,但我听说它们使用起来有点麻烦

    顺便说一句,您应该将示例函数的返回类型设置为
    无符号long
    ,并将
    返回a在最后;然后,您可以使用
    gcc-O2-S
    ,准确地查看编译结果。如果不更改return
    a
    ,GCC将愉快地优化整个函数体,因为它没有外部可见的副作用

  • 关于变量
    a
    是否将存储在寄存器中的问题是一个优化问题。由于没有
  • volatile修饰符IMHO,智能编译器将执行此操作

  • 这是一个电话会议的问题。如果按照惯例,在寄存器中传输单指针参数,则
    arr
    也将如此

  • 指针类型转换不是CPU解释的操作。没有为它生成代码。它只是为编译器提供关于你的意思的信息


  • (实际上有时强制转换确实会产生额外的代码,但这与多重继承和多态性有关)

    取决于您的优化级别。您可以检查程序集以回答问题。对于gcc,使用“-S”标志

    生成的程序集完全不同。(确保
    返回一个;
    建议的更改。)


    有关如何生成混合c/assembly列表的提示,请参见(这很快会对优化毫无用处)。

    Ok,因此“a”或“arr”存储在寄存器中的可能性很小。这段代码中的缓存命中率如何?我能假设读写变量“ARR”和“A”产生了100%个缓存命中吗?是的,这可能是一个安全的假设——我唯一能想到的是,如果你运气不好,让上下文切换到函数的中间,当控件返回到您的进程时,它们可能不再在缓存中。GCC不会将
    a
    优化到寄存器中,我不确定在这种情况下它会更快。我立案调查他们的想法,扎克!我在gcc网站上看到了你的帖子。您在C文件中编写的第二个按位操作函数对char数组执行大量类型转换。你能告诉我这些铸件的膨胀程度吗?对不起,我对汇编代码不太了解,所以看不懂。你为什么这么担心类型转换的成本?一般来说,C中的强制转换需要零或一条指令。它们与高级语言中的转换操作完全不同。(在这种情况下,它们自己不做任何事情,但它们强制编译器发出移位指令,在寄存器的整个宽度上进行操作——这是必要的,否则所有移位都会产生零,而不是您想要的。)
    gcc -S -O0 -o /tmp/xx-O0.s /tmp/xx.c
    gcc -S -O3 -o /tmp/xx-O3.s /tmp/xx.c