2

这是我在 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];
}

现在我的问题是:

  1. 变量“a”是否会存储在寄存器中,这样就不会从 RAM 或 chache 一次又一次地访问它?
  2. 在 64 位架构上工作,我是否应该假设“arr”数组将存储在寄存器中,因为函数参数存储在 64 位架构的寄存器中?
  3. 指针类型转换的效率如何?我的猜测是它应该是低效的吗?

任何帮助都将不胜感激。

问候

4

4 回答 4

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

看起来您正在尝试排列数组中的字节,然后将它们推入一个数字,而您的示例生成的机器代码对此并不是非常糟糕。David 建议改用 shift 和 mask 操作很好(如果您的代码需要在 big-endian 机器上运行,这也可以避免问题),还有 SSE 向量置换指令,但我听说它们很友好使用起来很痛苦。

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

于 2010-10-01T18:08:51.733 回答
2

您最好使用显式移位和掩码指令来完成此操作,而不是使用数组索引。

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

于 2010-10-01T18:08:16.493 回答
0
  1. 关于变量a是否将存储在寄存器中的问题是优化问题。由于没有volatile修饰符恕我直言,智能编译器会这样做。

  2. 这是调用约定的问题。如果按照惯例,在寄存器中传输单个指针参数 - 也会如此arr

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

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

于 2010-10-01T18:13:34.113 回答
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

生成的程序集完全不同。(请务必按照Zackreturn a;的建议进行更改。)

另请参阅此消息以获取有关如何生成混合 c/程序集列表的提示(优化后很快变得无用)。

于 2010-10-01T18:22:24.413 回答