【问题标题】:Break down C++ code size分解 C++ 代码大小
【发布时间】:2010-03-24 17:02:44
【问题描述】:

我正在为旧博文 C++ Code Size 中的第一个问题寻找一个不错的 Stack 溢出式答案,我将在下面重复:

我真的很想要一些工具(理想情况下,基于 g++),它可以告诉我编译/链接代码的哪些部分是从 C++ 源代码的哪些部分生成的。例如,查看是否为数百种不同类型实例化了特定模板(可通过模板特化修复)或者代码是否被过度内联,或者特定函数是否大于预期。

【问题讨论】:

  • 是你想要的链接器映射文件吗? g++ -Wl,map,prog.map 之类的东西?

标签: c++ static-analysis


【解决方案1】:

您可以查看 bloaty 来分析程序的二进制大小:

https://github.com/google/bloaty

./bloaty bloaty -d compileunits
    FILE SIZE        VM SIZE    
 --------------  -------------- 
  34.8%  10.2Mi  43.4%  2.91Mi    [163 Others]
  17.2%  5.08Mi   4.3%   295Ki    third_party/protobuf/src/google/protobuf/descriptor.cc
   7.3%  2.14Mi   2.6%   179Ki    third_party/protobuf/src/google/protobuf/descriptor.pb.cc
   4.6%  1.36Mi   1.1%  78.4Ki    third_party/protobuf/src/google/protobuf/text_format.cc
   3.7%  1.10Mi   4.5%   311Ki    third_party/capstone/arch/ARM/ARMDisassembler.c
   1.3%   399Ki  15.9%  1.07Mi    third_party/capstone/arch/M68K/M68KDisassembler.c
   3.2%   980Ki   1.1%  75.3Ki    third_party/protobuf/src/google/protobuf/generated_message_reflection.cc
   3.2%   965Ki   0.6%  40.7Ki    third_party/protobuf/src/google/protobuf/descriptor_database.cc
   2.8%   854Ki  12.0%   819Ki    third_party/capstone/arch/X86/X86Mapping.c
   2.8%   846Ki   1.0%  66.4Ki    third_party/protobuf/src/google/protobuf/extension_set.cc
   2.7%   800Ki   0.6%  41.2Ki    third_party/protobuf/src/google/protobuf/generated_message_util.cc
   2.3%   709Ki   0.7%  50.7Ki    third_party/protobuf/src/google/protobuf/wire_format.cc
   2.1%   637Ki   1.7%   117Ki    third_party/demumble/third_party/libcxxabi/cxa_demangle.cpp
   1.8%   549Ki   1.7%   114Ki    src/bloaty.cc
   1.7%   503Ki   0.7%  48.1Ki    third_party/protobuf/src/google/protobuf/repeated_field.cc
   1.6%   469Ki   6.2%   427Ki    third_party/capstone/arch/X86/X86DisassemblerDecoder.c
   1.4%   434Ki   0.2%  15.9Ki    third_party/protobuf/src/google/protobuf/message.cc
   1.4%   422Ki   0.3%  23.4Ki    third_party/re2/re2/dfa.cc
   1.3%   407Ki   0.4%  24.9Ki    third_party/re2/re2/regexp.cc
   1.3%   407Ki   0.4%  29.9Ki    third_party/protobuf/src/google/protobuf/map_field.cc
   1.3%   397Ki   0.4%  24.8Ki    third_party/re2/re2/re2.cc
 100.0%  29.5Mi 100.0%  6.69Mi    TOTAL

【讨论】:

    【解决方案2】:

    在大多数 C 编译器中,都有一种生成 .map 文件的方法。该文件列出了所有已编译库的地址和大小。您可以使用该映射文件来帮助您确定应该首先优化哪些文件。

    【讨论】:

      【解决方案3】:

      如果您想在 C++ 代码中查找代码膨胀的来源,我使用了“nm”。以下命令将列出您的应用程序中的所有符号,其中最大的代码和数据块位于顶部:

      nm --demangle --print-size --size-sort --reverse-sort <executable_or_lib_name> | less
      

      【讨论】:

        【解决方案4】:

        看起来应该存在这样的东西,但我没有使用过类似的东西。不过,我可以告诉你我将如何一起编写脚本。可能有更快和/或更性感的方式来做到这一点。

        首先一些你可能已经知道的东西:

        addr2line 命令接收一个地址,并可以告诉您机器代码在哪里实现的源代码。可执行文件需要使用调试符号构建,并且您可能不想对其进行太多优化(-O0、-O1 或 -Os 可能与您最初想要的一样高)。 addr2line 有几个标志,您需要阅读它的手册页,但如果您想查看输出中有意义的 C++ 函数名称,您肯定需要使用 -C 或 --demangle。

        objdump 命令可以打印出关于许多类型的目标文件中的东西的各种有趣的东西。它可以做的事情之一是打印出一个表,该表表示对象文件(包括可执行文件)中或引用的符号。

        现在,你想用它做什么:

        您需要让 objdump 告诉您 .text 部分的地址和大小。这是实际的可执行机器代码所在的位置。有几种方法可以做到这一点,但最简单的(无论如何)可能是你做的:

        objdump -h my_exe | grep text
        

        这应该是这样的:

         12  .text       0000049  000000f000  0000000f000 00000400  2**4
        

        如果你没有 grep 它会给你一个像这样的标题:

        Idx  Name        Size     VMA         LMA         File off  Algn
        

        我认为对于可执行文件,VMA 和 LMA 应该是相同的,所以使用哪个并不重要,但我认为 LMA 是最好的。您还需要尺寸。

        使用 LMA 和大小,您可以反复调用 addr2line 询问机器代码的源代码来源。如果你传递一个指令中的地址,我不确定这将如何工作,但我认为它应该可以工作。

        addr2line -e my_exe <address>
        

        它的输出将是一个路径/文件名、一个冒号和一个行号。 如果您要计算每个唯一路径/文件:num 的出现次数,您应该能够查看计数最高的那些。 Perl 使用 path/file:num 作为键和计数器作为值的散列将是实现这一点的简单方法,但如果您发现运行速度太慢,还有更快的方法。 您还可以过滤掉您可以确定不需要及早包含的内容。 为了显示您的输出,您可能希望从同一个函数中过滤掉不同的行,但您可能会注意到一个函数中的不同行具有不同的计数,这可能很有趣。无论如何,这可以通过让 addr2line 告诉您函数名称或在第一步中使用 objdump -t 并一次处理一个函数来完成。

        如果您发现某些模板代码或其他代码行在可执行文件中出现的频率超出了您的预期,那么您可以轻松找到它们并仔细查看。宏和内联函数的最终表现可能与您的预期不同。

        如果您不知道,objdump 和 addr2line 来自 GNU binutils 包,其中包括其他几个有用的工具。

        【讨论】:

          【解决方案5】:

          我最近写了一个工具,bloat-blame,它的功能类似于nategoose proposed

          【讨论】:

            【解决方案6】:

            我不知道它是否会有所帮助,但有一个 gcc 标志可以将它生成的汇编代码写入文本文件以供您检查。

            "-S 用于代替 -c 以生成汇编程序源文件,使用 .s 作为扩展名,而不是目标文件。如果您需要检查生成的汇编代码,这可能很有用。 "

            【讨论】:

            • 谢谢,这很有用,但我希望有更适合我的问题的东西。
            【解决方案7】:

            我不知道如何映射代码->一般来说生成的程序集。

            对于模板实例化,您可以使用“strings -a |grep |sort -u|gc++filt”之类的东西来大致了解正在创建的内容。

            您提到的另外两个项目实际上似乎很主观。什么是“太多”内联?您是否担心您的二进制文件会膨胀?唯一要做的实际上是进入 gdb 并反汇编调用者以查看它生成的内容,一般无需检查“过度”内联。

            对于函数大小,我再次好奇为什么它很重要?您是否试图找到编译时意外扩展的代码?您甚至如何定义工具要检查的预期大小?同样,您始终可以将您怀疑编译的任何函数编译成比您想要的多得多的代码,并准确查看编译器在做什么。

            【讨论】:

            • 关于“为什么重要?”我们正在一个对代码大小有固定限制的平台上开发。一些见解将帮助我们找到首先要攻击的问题区域。
            【解决方案8】:

            在 Visual C++ 中,这基本上就是 .PDB 文件的用途。

            【讨论】:

            • 您能提供详细信息吗?如何确定与符号关联的代码大小?
            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2015-02-24
            • 1970-01-01
            • 2014-05-21
            • 1970-01-01
            • 1970-01-01
            • 2012-04-04
            相关资源
            最近更新 更多