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

时间:2015-04-04 09:13:44

标签: c++ c gcc micro-optimization machine-code

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

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

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

// 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文件是否用于任何内容?我的玩具测试是假的吗?

感谢您的任何见解

2 个答案:

答案 0 :(得分:2)

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

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

但是现在这种类型都不再适用。你的(可能是现代的)CPU当然有一个内置的乘法指令,名义上只需要很少的时钟周期(small, as in, 3)执行,因此使用它必然会比打破你的乘法运行得更快(并且更小)进入多个移位操作,每个移位操作名义上在一个时钟周期内执行。

我说“名义上”因为预取,流水线,缓存等等,即使你事先知道任何给定指令需要多少个时钟周期的概念也不再适用。

所以,简而言之:“学会停止担心并爱炸弹”。

答案 1 :(得分:0)

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

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