需要在gcc,g ++中生成ASM代码

时间:2015-12-01 10:14:28

标签: c++ gcc assembly g++

为了缩小我的问题,让我描述一下我的假设和我做过的实验......

我的假设: 用汇编语言编写的代码运行速度比C / C ++代码快得多,而且可执行文件的大小要小得多从C / C ++代码生成。

实验:我将以下程序写入bin2dec.c

#include <stdio.h>

int main()
{
    long int binary, decimal, reminder, exp;
    int i, j;

    for(i=0; i<10000; i++)
    {
        for(j=0; j<1000; j++)
        {
            binary = 11000101;

            exp = 1;
            decimal = 0;

            while(binary != 0)
            {
                reminder = binary % 10;
                binary = binary / 10;
                decimal = decimal + reminder * exp;
                exp *= 2;
            }   
        }
    }
    return 0;
}

然后为其生成了ASM代码gcc -S bin2dec.c -o bin2dec.s

之后我编译了两个文件

gcc bin2dec.c -o bin2dec_c
gcc bin2dec.s -o bin2dec_s

测试1:找到两个文件的一些内部细节

[guest@localhost ASM]$ size bin2dec_c bin2dec_s
   text    data     bss     dec     hex filename
    951     252       4    1207     4b7 bin2dec_c
    951     252       4    1207     4b7 bin2dec_s

结果:两者完全相同......

测试2:执行文件并计算所花费的时间

[guest@localhost ASM]$ time ./bin2dec_c
real    0m1.724s
user    0m1.675s
sys     0m0.002s

[guest@localhost ASM]$ time ./bin2dec_s
real    0m1.721s
user    0m1.676s
sys     0m0.001s

结果:两者都相同。有些时候从ASM生成的可执行文件运行得慢: - (

所以问题是,我的假设是否错误? 如果没有,我做了什么错误,以便可执行文件bin2dec_c和bin2dec_s以相同的速度运行? 有没有更好的方法从C / C ++程序中获取ASM代码,或者我应该从ASM中从头开始重写所有逻辑以获得速度和程序大小的优势?

2 个答案:

答案 0 :(得分:3)

这是一个古老的传统(在20世纪70年代早期的Unix系统中,机器非常小,生成一些汇编程序文件更简单),而且有些编译器可以直接生成目标文件或机器代码。可能是最新版本的Clang/LLVMTinyCC(仅适用于C:快速编译时,但执行速度非常慢!)也许来自IBM的一些专有XLC编译器,以及GCC社区中的一些人正在考虑那(特别是GCCJIT)。

但是,生成汇编程序文件对于编译器开发人员来说通常更容易。并且由于大多数编译器工作都发生在optimization次传递(在编译器中转换某些内部表示),因此丢失几毫秒来启动汇编程序并不是很重要。

使用GCC,使用gcc -timegcc -ftime-report进行编译(当然还有常用的优化标记,例如-O2),以了解编译器花费时间的位置。它永远不会出现在汇编程序中......

有时您可能会发现查看生成的汇编程序文件很有用。使用foo.cc编译g++ -O2 -Wall -S -fverbose-asm -std=c++11 foo.cc C ++ 11文件,然后(使用某个编辑器或寻呼机)查看生成的foo.s汇编程序文件。

您甚至可以使用g++ -fdump-tree-all -O2进行编译,并从GCC获取数百个编译器转储文件,解释编译器对您的代码所做的转换。

今天BTW(超标量,流水线)处理器(桌面,笔记本电脑,平板电脑,服务器)处理器非常复杂,实际上编译器可以比人类程序员更好地进行优化。实际上,优化编译器生成的汇编代码来自一些实际大小的C代码(例如几百行的C源文件) 通常比实验汇编程序人类程序员在几周内编码更快(不到一千个汇编程序行)。换句话说,你的假设(用汇编语言编写的代码比用C语言编写的代码更好/更好,并由优秀的优化编译器编译)错误 在实践中

(顺便说一句,允许优化编译器转换你的bin2dec.c程序,它没有可观察到的副作用,例如没有输入和输出,进入空程序,GCC 5.2gcc -O2 !!)

另请阅读halting problemRice's theorem。优化编译器或static program analyzers可以实现的内在限制。

答案 1 :(得分:2)

  

假设:用汇编语言编写的代码运行速度比它快得多   C / C ++对应物和可执行文件的大小要小得多   从C / C ++代码生成的。

汇编语言只是机器代码的文本表示。

有一些注意事项,您可以反汇编二进制文件,并将该源重新组合回同一个二进制文件中。显然这对于​​ARM来说确实是可能的,但x86 asm方言没有语法来表示同一指令的不同编码。例如强制在PLT(过程链接表)中的jmp指令中使用4字节偏移量,跳转目标将在稍后进行修补。

您的实验制作了两个相同的二进制文件。 gcc直接从C转到可执行文件内部生成一个asm源文件并组装它。你只需要分解这个过程,这样就可以了解编译器生成的asm。

手写汇编代码始终至少与编译器输出一样好。您始终可以从编译器输出开始并寻找改进。在极少数情况下,不会有任何改进 但是,编译过程中编译器生成的asm只是观察并没有做任何事情来改进它!将代码插入http://gcc.godbolt.org/以查看各种不同编译器的输出(甚至是ARM或PPC,这对于std:atomic代码很有用,看看在弱有序的arch上会发生什么)

由于您编译时没有进行优化,因此肯定会有很大的改进。我将从gcc -O3 -march=native -fverbose-asm -masm=intel -S

的输出开始

编译器输出真的是最佳的非常,即使对于短序列也是如此。编译器对人类具有优势的地方在于一次跟踪大量源代码,并根据他们可以跨功能证明的内容进行优化。 (这样的整个程序优化对于人类在源代码中维护来说太脆弱了。)因此编译器可以利用在这个构建中恰好是真实的东西,但不是正在编译的函数的设计的一部分。 / p>

编译器几乎总是执行良好的作业,但极少需要伟大的作业。重要的是,它是足够好的作业,并且代码运行速度很快,即使它使用的指令多于所需的指令。通常诸如分支错误预测,缓存未命中和依赖链之类的东西都是瓶颈,并且CPU足够宽以处理编译器倾向于使用的额外指令而没有显着的减速。使用超线程,使用更少的指令执行相同的工作是一个更大的优势。

有关具体示例,请参阅https://codereview.stackexchange.com/questions/6502/fastest-way-to-clamp-an-integer-to-the-range-0-255上的编译器输出,并将其与我的probably-optimal hand-written asm进行比较。我试图让gcc生成类似的最佳输出,但没有成功。它要么使用多个分支,要么使用两个cmov指令(这将使得无钳位快速路径变慢),而不是用于钳位的分支,然后是钳位为零或钳位到最大值的cmov。