【问题标题】:may a whole array reside in some cpu register?整个数组可以驻留在某个 cpu 寄存器中吗?
【发布时间】:2010-11-08 10:19:08
【问题描述】:

由于我对 cpu 寄存器不太熟悉,一般来说,特别是在 x86 的任何架构中,如果使用 VC++ 与编译器相关,我很好奇数组的所有元素是否都可能具有少量元素就像一个包含 4 个元素的 1 字节字符数组驻留在某个 cpu 寄存器中,因为我知道这对于双精度、整数等单个原语可能是正确的?

当我们有如下参数时:

void someFunc(char charArray[4]){
//whatever
}

这个参数传递是否肯定会通过传递一个指向函数的指针来完成,或者该数组将驻留在某个 cpu 寄存器中,从而无需传递指向主存储器的指针?

【问题讨论】:

  • 正如您所提到的,这是非常特定于平台和编译器的。请提供一些规格。
  • 这取决于 CPU 寄存器的大小,它是 32 位 int 并且是 32 位 CPU,您的数组中只有 1 个变量可以容纳,我怀疑整个数组会不会放在一个 CPU 寄存器中,我认为最多是一个指向数组中某个元素的指针。

标签: c++ arrays parameters cpu


【解决方案1】:

依赖于编译器,也不可能。数组不能像其他类型一样按值传递,即它们在传递给函数时不能被复制。 C++ 标准很清楚,在声明中处理函数签名时,以下是完全等价的:

void foo( char *a );
void foo( char a[] );
void foo( char a[4] );
void foo( char a[ 100000 ] );

兼容的编译器会将函数签名中的数组转换为指针。现在,在调用处,发生了类似的操作:如果参数是一个数组,编译器必须将其衰减为指向第一个元素的指针。同样,数组的大小在衰减中丢失了。

特定寄存器可用于保存多个值并对它们执行操作(google for vectorized operations, MME and variables)。但是,虽然这意味着编译器实际上可以将一个小数组的内容插入到单个寄存器中,但它不能用于更改您引用的函数调用。

【讨论】:

  • 这是一日之二 (stackoverflow.com/questions/4120658)。有人可能应该为所有这些东西找到一个非常好的答案并将其标记为 c++-faq。
  • 如果由于内联而消除了函数调用,这是否会阻止相同的优化?
  • @Steve Jessop:虽然答案是一样的,但我可以理解用户没有意识到这两个问题是完全相同的:“按值传递”语法的具体语义是什么用于数组。
  • @Sharptooth,如果函数被内联,编译器将直接在数组中操作,因为它能够做到这一点,同时仍然满足 as-if 保证。但同样,这并不意味着数组可以在寄存器中传递,它根本不传递。在寄存器中传递数组会产生副作用,即在函数内部可能修改数组的操作只会修改寄存器,而不是实际内存,这是语义上的重大变化。
  • @dribeas:是的,对不起,我不是说这个问题是一样的。其实这个是专门针对函数参数的,另外一个专门针对对象的。我的意思是它还在答案中调用了“数组是指针”的错误,没有人仅仅通过产生“这就是你错的原因”常见问题解答来反驳这种错误。
【解决方案2】:

在单个函数中,一个数组可以保存在一个或多个寄存器中,只要编译器能够生成 CPU 指令来按照代码指示对其进行操作。该标准并没有真正定义在寄存器中“存在”的含义。这是编译器和调试器之间的私事,在寄存器中的某些内容与完全“优化掉”之间可能存在细微差别。

在您的示例中,参数是指针,而不是数组(请参阅 dribeas 的回答)。因此,它指向的数组可能被保存为寄存器是不寻常的。您可能处理的“主要”架构不允许指向寄存器的指针,因此即使数组保存在调用代码中的寄存器中,也必须将其写入内存才能获取指向它,传递给被调用者。

如果函数调用是内联的,那么可能会有更好的优化,就像根本没有调用一样。

如果将数组包装在结构中,则可以将其转换为可以按值传递的东西:

struct Foo {
    char a[4];
};

void FooFunc(Foo f) {
    // whatever
}

现在,该函数将实际数组数据作为其参数,因此将其保存在寄存器中的障碍减少了。不过,实现的调用约定是否确实在寄存器中传递了小结构是另一个问题。我不知道有什么调用约定可以做到这一点,如果有的话。

【讨论】:

  • +1 表示可以在寄存器中传递数组,并且比问题中建议的方法更进一步。
  • +1 用于解决所有问题的神话般的额外间接层 :)
【解决方案3】:

在我相当熟悉的 5 个左右的编译器中,(Borland/Turbo C/C++ 从 1.0,Watcom C/C++ 从 v8.0,MSC 从 5.0,IBM Visual Age C/C++,各种 gcc DOS、Linux 和 Windows 上的版本)我还没有看到这种优化是自然发生的。

有一个字符串库,我不记得它的名字了,它在 x86 ASM 中进行了类似的优化。它可能是“Spontaneous Assembly”库的一部分,但不能保证。

【讨论】:

    【解决方案4】:

    接受数组的函数可能会索引到该数组。我知道没有支持有效索引到寄存器的架构,因此在寄存器中传递数组可能毫无意义。

    (在 x86 架构上,您可以通过访问 alaheax 寄存器来访问 a[0]a[1],但这是一种特殊情况,只有在索引已知的情况下才有效编译时间。)

    【讨论】:

    • 例如8051在内存映射中具有与寄存器相对应的地址。对这些地址的索引访问与对任何其他内存的索引访问相同或更好,但是当然你必须为一个大于 1 的数组使用多个寄存器,所以如果你这样做了您的数组不会保存在“某个 CPU 寄存器”中。而是“一些 CPU 寄存器”。
    • 只要编译器可以在编译时确定所有索引,就可以用寄存器引用替换它们。
    【解决方案5】:

    您询问是否可以在 x86 上使用 VC++。

    我怀疑这种配置是否可行。诚然,您可以生成汇编代码,其中该数组保存在寄存器中,但由于数组的性质,它绝不是编译器的自然优化,所以我怀疑他们是否将其放入。

    您可以尝试一下并生成一些代码,编译器会“激励”将其放入寄存器中,但它看起来很奇怪

    char x[4];
    *((int*)x) = 36587467;
    

    使用优化和 /FA 开关编译它并查看生成的汇编代码(然后告诉我们结果:-))

    如果您以更“自然”的方式使用它,例如访问单个字符或使用字符串对其进行初始化,编译器根本没有理由将该数组放入寄存器中。

    即使将它传递给函数 - 编译器可能会将数组的 地址 放入寄存器,而不是数组本身

    【讨论】:

      【解决方案6】:

      只有变量可以存储在寄存器中。您可以尝试使用 register 关键字强制寄存器存储:register int i;

      数组默认是指针。

      您可以像这样获取位于 4 位置的值(使用指针语法):

      char c = *(charArray + 4);
      

      【讨论】:

      • 嗯,现代 C++ 编译器会忽略 register 关键字。如果优化器发现变量将从寄存器存储中受益,它将被相应地使用。 不管是否声明为register
      • 不是...数组是不是指针,它们很容易衰减为指针(无论何时用作右值),但它们不是指针.
      • dribeas 是对的。数组不能按值传递,所以看起来数组的参数实际上是指针参数。
      猜你喜欢
      • 2013-06-24
      • 1970-01-01
      • 2014-01-11
      • 2020-07-24
      • 2017-08-01
      • 1970-01-01
      • 1970-01-01
      • 2016-10-28
      • 1970-01-01
      相关资源
      最近更新 更多