C 代码大小的公平比较

C 代码大小的公平比较,c,embedded,size,C,Embedded,Size,我已经编写了两种不同的算法C实现,运行在嵌入式处理器(ARM)上。我想用一种公平的方式来比较这两种实现的性能 代码大小,因此在下载可执行文件时,我得到以下数字: Implementation One .text size 55098 bytes .data size 2048 bytes Implementation Two .text size 54598 bytes .data size 2048 bytes .text段中的差异是500字节,但相对而言并不是很多。问题是,该

我已经编写了两种不同的算法C实现,运行在嵌入式处理器(ARM)上。我想用一种公平的方式来比较这两种实现的性能 代码大小,因此在下载可执行文件时,我得到以下数字:

Implementation One

 .text size 55098 bytes
 .data size 2048 bytes

Implementation Two

 .text size 54598 bytes
 .data size 2048 bytes
.text段中的差异是500字节,但相对而言并不是很多。问题是,该图还包含围绕可执行文件的引导代码,以便可以在独立模式下调用它,即嵌入式处理器上没有操作系统

我想知道是否有人知道我如何在没有所有臃肿的额外代码的情况下获得可执行文件的实际代码大小

非常感谢
Andrew

您应该定义额外代码的一部分。希望您已经在另一个编译单元中编写了这段额外的代码。您的工具链应该具有检测该编译单元的代码大小的工具

最重要的一点是,您应该将代码大小与可用的ALE内存进行比较。如果您不需要将代码放入指定的内存区域,那么减少代码大小是不值得的

  • 您的C编译器通常可以生成可供检查的汇编源代码
  • 另一种可能是从链接器查看映射文件,它应该为您提供各个函数的大小
  • 您可以使用调试器查看二进制代码
  • 要获取asm输出或映射文件,需要为编译器和/或链接器指定适当的选项。这些选项取决于您使用的工具链(编译器、链接器)

    要从gcc获取asm输出,请执行以下操作:
    gcc-S-o hello_asm.S hello.c

    您的链接器几乎肯定会生成一个映射文件(可能已经在这样做),该文件将显示所有单个数据和代码对象的内存使用详细信息(以分钟为单位),当然会一直到对象模块级别,通常是单个函数和数据对象级别。

    最后,所有代码都将下载,包括引导加载程序和操作系统。那你为什么要把他们排除在这项措施之外呢


    了解要排除的代码大小的简单方法是使用尽可能简单的应用程序进行编译(例如,
    main(){}
    )。然后,您只需从下一个度量中减去获得的值。

    一种可能的方法是让构建工具转储粒度更细的映射

    例如,如果您正在使用gnu工具集,则可以对.elf文件使用run
    size
    ,并根据其中的对象文件对其进行细分

    您还可以在.elf文件上使用
    objdump
    ,以获得相当详细的地图


    假设示例中的引导代码在两个总体实现之间保持不变,使用这些工具可以更好地了解哪一个具有更好的代码空间效率。

    如果您使用的是GNU工具,则可以使用
    size
    工具。它将告诉您任何对象文件在每个部分中使用的内存量。在您的情况下,您需要确保要度量的代码位于它自己的文件中

    下面是一个输出示例:

    text    data     bss     dec     hex filename
    9767       4    5184   14955    3a6b cfi_flash.o
    1138       0     192    1330     532 cfi_mtd.o
     556       0     128     684     2ac mtdcore.o
    3897       0       8    3905     f41 mtdpart.o
    

    有时很难单独计算实现的大小,因为有些实现需要链接库例程,而其他实现则不需要。如果例程#1比#2小300字节,但需要500字节的库例程foo(),而例程#3比#4小300字节,但也需要相同的例程,那么#1在隔离状态下实际上比#2大200字节(即500-300字节),而#3在隔离状态下比#4大200字节,但是#1加#3比#2加#4小100字节(因为库例程只需要一次)

    如果一个库例程产生了一个功能降低(成本降低)的替代方案,那么这种情况就会经常发生。如果可以避免使用昂贵的库例程,那么编写和使用更简单的版本是值得的。另一方面,如果代码最终还是使用了昂贵的例程,那么更简单的版本可能会变得多余。

    您可以编写一个“实现Null”,它只有一个存根函数,不做任何事情。这将为您提供一个基线值,用于比较

    (实现大小1-实现大小为空) vs。
    (实现大小2-实现大小Null)

    将引导加载程序放在其自己的专用内存段中应该可以解决这个问题,是吗?你为什么不这么做?同意——尽管“你为什么要把他们排除在措施之外”不一定是一个反问句;有几个可能的原因,你可能想这样做(或者为什么你可能会错误地认为你这样做!);你的问题的正确答案很可能取决于它是什么原因。完成的系统很可能还需要做很多其他事情,但这个实验只是为了找出问题某一部分的两种方法的代码大小。