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

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

本文关键字:文件 算法 为什么      更新时间:2023-10-16

我不是一个受过训练的计算机科学家,所以我不知道编译等方面的全部或大部分细节,但我一直认为我的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文件是否用于任何用途?我的玩具测试是假的吗?

感谢您的任何见解

曾经有一段时间,CPU非常简单,像上面尝试的比特移位技巧实际上可以比CPU的内置乘法指令产生更好的性能。(以牺牲程序长度为代价:一系列移位指令可能比一条乘法指令快,也可能不快,但肯定会更长。)我相信这一点一直持续到80286年。

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

但如今,这种说法已不再成立。你的(可能是现代的)CPU当然有一个内置的乘法指令,它名义上在非常少的时钟周期内执行(小,如3),所以使用它一定会比将乘法分解为多个移位运算运行得更快(更小),每个移位运算名义上在一个时钟周期内运行。

我之所以说"名义上",是因为有了预取、流水线、缓存等,即使你可以提前知道任何给定指令需要多少时钟周期的想法也不再成立。

所以,长话短说:"学会停止担忧,爱上炸弹"。

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

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