为什么显式位移算术会导致更大的 .s 文件?

why does explicit bit shifting arithmetic result in a bigger .s file?

我不是受过训练的计算机科学家,所以我不知道关于编译等的全部或大部分细节,但我一直认为我的 c 程序被编译成机器代码,我可以使用 -使用 gcc 的 S 标志。

我还认为我的代码越接近机器代码,计算机执行它的速度就越快。所以我决定测试一下。

我写了两个测试文件来计算一个简单的算术问题。

// test1.c
int main(int argc, char* argv[]){
    int x = 4243;
    int y = 3235;
    int z = 613*x + 725*y;
    return 0;
}

// test2.c
int main(int argc, char* argv[]){
    int x = 4243;
    int y = 3235;
    int z = ( ( ( ( ( ( ( x << 3 ) + x ) << 1 ) + x ) << 3 ) + x ) << 2 ) + x + 
      ( ( ( ( ( ( ( ( ( y << 2 ) + y ) << 1 ) + y ) << 2 ) + y ) << 2 ) + y ) << 2 ) + y;
    return 0;
}

我知道我把这个例子做得比必要的复杂得多,但是当我用一个更简单的例子来尝试时,区别并不那么明显。

现在,如果我用 gcc -S 标志编译,test1.s 的机器代码是 31 行,test2.s

的机器代码是 47 行

可能的解释是什么?更少的机器代码行意味着更快的执行的假设是否有缺陷?在创建二进制文件之前,.s 文件是否被用于任何用途?我的玩具测试是假的吗?

感谢您的任何见解

曾经有一段时间 CPUs 非常简单和不复杂,像你在上面尝试做的那样的移位技巧实际上可以产生比内置乘法指令更好的性能CPU。 (以程序长度为代价:一系列移位指令可能比单个乘法指令快,也可能不快,但它肯定会更长。)我相信这在 80286 之前一直适用。

甚至曾经有一段时间(还记得 Z80 吗?)CPU 非常简单,甚至没有内置的乘法指令,所以我们不得不调用例程来乘法数字,这些例程当然会包含循环,这些循环会迭代与要相乘的数字的位数一样多的次数,因此这些位移技巧在当时会产生非常非常好的性能。 (同样,这将以程序长度为代价:调用乘法例程比执行两次或更多次移位操作需要更少的字节。)

但现在这种说法已经不适用了。您的(大概是现代的)CPU 肯定有一个内置的乘法指令,名义上在非常小的时钟周期内执行,(small, as in, 3)所以使用它必然会 运行 更快(并且更小)而不是将乘法分解为多个移位操作,每个移位操作名义上在一个时钟周期内执行。

我说 "nominally" 因为有了预取、流水线、缓存等,即使您可以提前知道任何给定指令需要多少个时钟周期的概念也不再适用.

所以,长话短说:"learn to Stop Worrying and Love the Bomb"。

如果您编写的 CPU 没有乘法指令(这样的 CPU 确实存在),并且如果您必须多次计算 613*x + 725*y,那么它可能值得编写自定义位移代码。

但是您可能必须用汇编语言编写它才能使其比编译器的内置乘法函数更快。