2010-10-01 110 views
2

这里是我写在64位linux机器上的函数。C代码分析

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]; 
} 

现在我的问题是:

  1. 威尔变量“a”被存储在一个寄存器,使得它不会被一次又一次地从RAM或chache访问?
  2. 在64位体系结构上工作时,我应该假设'arr'数组将被存储在一个寄存器中,因为函数参数存储在一个64位体系结构的寄存器中?
  3. 指针类型转换的效率如何?我的猜测是它应该效率低下?

任何帮助将appriciated。

问候

回答

3
  1. a无法存储在寄存器中,因为您已经使用了它的地址。 (valdo正确地指出,一个非常聪明的编译器可能优化数组访问到位操作,并将a留在一个寄存器中,但我从来没有见过编译器这样做,我不确定它会变得更快)。
  2. arr(指针本身)存储在一个寄存器(%edi,在amd64上)。内存中的的内容。
  3. 指针类型铸造本身通常根本不产生任何代码。然而,用类型转换来做愚蠢的事情会导致非常低效的代码,甚至导致代码的行为未定义。

它看起来像你试图排列一个数组中的字节,然后将它们推到一个数字,并且你的例子生成的机器代码不是非常糟糕的。 David建议使用shift和mask操作,而不是这样(如果你的代码需要在一个big-endian机器上运行,这也可以避免问题),并且还有SSE向量排列指令,但是我听说他们很友善的痛苦使用。顺便说一句,你应该使你的示例函数的返回类型为unsigned long,并将return a;放在最后;那么你可以使用gcc -O2 -S,看看你从编译中得到了什么。没有改变返回a,GCC将愉快地优化功能的整个身体,因为它没有外部可见的副作用。

+0

好,所以'a'或'arr'都不会存储在寄存器中。这个代码中的缓存命中呢?我能否假设读写变量'arr'和'a'产生100%缓存命中? – 2010-10-01 18:31:11

+0

是的,这可能是一个安全的假设 - 我能想到的唯一的事情就是如果你在函数中间切换上下文非常不幸,那么当控制回到你的过程时,它们可能不会再被缓存了。 – zwol 2010-10-01 18:34:24

2

你可能会做的更好使用显式移位和掩码指令来实现这一点,而不是使用数组索引。

数组操作将使编译器难以使用寄存器,因为通常没有指令执行诸如“从寄存器A的第3个字节装入8位”的指令。 (一个优化编译器可能找出可以用shift/mask来做到这一点,但我不确定它有多可能)。

0
  1. 关于变量a将被存储在寄存器中的问题是一个优化问题。由于没有volatile修饰符恕我直言智能编译器会做到这一点。

  2. 这是一个调用约定的问题。如果按照惯例,单个指针参数被传送到一个寄存器中 - 那么将是arr

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

(其实有时铸造确实会产生额外的代码,但是这涉及到多重继承和多态)

+0

GCC不会将'a'优化成一个寄存器,而且我不确定在这种情况下它会更快。我提交了http://gcc.gnu.org/bugzilla/show_bug.cgi?id=45861看看他们的想法。 – zwol 2010-10-01 18:35:43

+0

扎克!我在海湾合作委员会网站看到过你的帖子。您在C文件中编写的第二个按位操作函数在char数组上进行了大量类型转换。你能告诉我这些类型的铸件应该是多么膨胀吗?对不起,我不太了解汇编代码,所以无法读取它。 – 2010-10-01 18:49:25

+0

你为什么如此担心类型转换的成本?一般来说,使用C来投射零或一条指令。它们完全不像高级语言中的转换操作。 (在这种情况下,它们本身并不做任何事情,但是它们强制编译器发出在寄存器的整个宽度上运行的移位指令 - 这是必要的,或者所有的移位都会产生零,而不是所有你想要的。) – zwol 2010-10-02 01:41:20

0

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

gcc -S -O0 -o /tmp/xx-O0.s /tmp/xx.c 
gcc -S -O3 -o /tmp/xx-O3.s /tmp/xx.c 

生成的组件是完全不同。 (请务必将return a;更改为Zack建议的更改。)

另请参阅this message关于如何生成混合c /汇编列表(通过优化快速变得无用)的提示。