代码之家  ›  专栏  ›  技术社区  ›  Steve314

在C++中有没有工具来追踪臃肿?

  •  7
  • Steve314  · 技术社区  · 14 年前

    一个粗心的模板,这里有一些过度的内联,在C++中编写臃肿的代码太容易了。原则上,重构以减少膨胀并不难。问题是跟踪最糟糕的模板和内联——跟踪那些在实际程序中导致真正膨胀的项。

    考虑到这一点,并且因为我确信我的库比它们应该的更容易膨胀,所以我想知道是否有任何工具可以自动跟踪那些最严重的违规者——也就是说,找出那些对特定目标规模贡献最大的项目(包括所有重复的实例化和调用)。

    我现在对性能不太感兴趣——这都是关于可执行文件的大小。

    是否有任何工具可用于此工作,在Windows上可用,并与mingw gcc或Visual Studio兼容?

    编辑 一些上下文

    我有一组多路树模板,它们可以替代红黑树标准容器。它们是围绕非类型安全非模板代码编写的包装器,但它们也是很久以前编写的,并且是一个“将更好地缓存友好性提高实际性能”的实验。关键是,它们并不是真正为长期使用而写的。

    因为它们支持一些方便的技巧(基于自定义比较/部分键的搜索、高效的订阅访问、搜索最小的未使用的键),所以它们最终在我的代码中几乎无处不在。最近,我几乎从未使用过std::map。

    除此之外,我还有一些更复杂的容器,比如双向地图。除此之外,我还有树类和有向图类。最重要的是…

    使用映射文件,我可以跟踪非内联模板方法是否导致了膨胀。这只是找到特定方法的所有实例化并添加大小的问题。但是不明智的内联方法呢?毕竟,这些模板原本是围绕非模板代码的薄包装纸,但从历史上看,我判断某些东西是否应该内联的能力并不十分可靠。这些模板内联的膨胀影响并不容易测量。

    我知道哪些方法被大量使用,但这是众所周知的没有仿形错误的阿片化。

    5 回复  |  直到 11 年前
        1
  •  7
  •   Ben Straub    14 年前

    退房 Symbol Sort . 我用了一段时间来弄明白为什么我们的安装程序在六个月内增长了4倍(结果发现答案是C运行时和libxml2的静态链接)。

        2
  •  5
  •   Community Egal    7 年前

    地图文件分析

    我在一段时间前就遇到过这样的问题,最后我编写了一个自定义工具来分析地图文件(可以指示Visual Studio链接器生成一个)。工具输出为:

    • 按代码大小降序排序的函数列表,仅列出前n个
    • 按代码大小降序排序的源文件列表,仅列出前n个

    解析映射文件相对容易(函数代码大小可以计算为当前行和后续行之间的差异),最困难的部分可能是以合理的方式处理损坏的名称。您可能会发现一些可以同时使用的库,几年前我做过,我不知道当前的情况。

    下面是一个地图文件的简短摘录,这样您就知道了预期结果:

    Address         Publics by Value              Rva+Base       Lib:Object
    
    0001:0023cbb4       ?ApplyScheme@Input@@QAEXPBVParamEntry@@@Z 0063dbb4 f   mainInput.obj
    0001:0023cea1       ?InitKeys@Input@@QAEXXZ    0063dea1 f   mainInput.obj
    0001:0023cf47       ?LoadKeys@Input@@QAEXABVParamEntry@@@Z 0063df47 f   mainInput.obj
    

    符号排序

    如张贴在 Ben Staub's answer , Symbol Sort 是一个随时可用的命令行实用程序(附带完整的C源),它可以完成所有这些工作,唯一的区别是不分析映射文件,而是分析pdb/exe文件。

        3
  •  2
  •   jalf    14 年前

    所以根据你的问题和评论,我读到的是图书馆 实际上太大了。

    唯一需要确定的工具是命令shell或Windows文件资源管理器。查看文件大小。它是如此之大以致于造成真正的实际问题吗?(不可接受的下载时间,不适合目标平台的内存吗,诸如此类的事情)?

    如果不是,那么您应该担心代码的可读性和可维护性,而不必担心其他任何问题。以及用于 那个 是你的眼睛。阅读代码,并采取必要的措施使其更具可读性。

    如果可以指出可执行文件大小是 问题 ,请将其编辑到您的问题中,因为这是重要的上下文。

    但是,假设文件大小 实际上是个问题:

    内联函数通常不是问题,因为编译器和其他任何人都不会选择要内联的函数。简单地做标记 inline 不内联实际生成的代码。如果编译器决定在较大的代码和较小的间接寻址之间进行权衡,那么它就可以进行内联,这样做是值得的。如果一个函数经常被调用,它将不会被内联,因为这将极大地影响代码大小,这将损害性能。

    如果担心内联函数会导致代码膨胀,只需使用“优化大小”标志进行编译。然后编译器会将内联限制在不会显著影响可执行文件大小的情况下。

    为了找出最大的符号,请按照@suma建议的方式解析映射文件。

    但事实上,当你提到“众所周知的没有仿形错误的阿片化”时,你自己也说过。

    你需要做的第一步就是询问 可执行文件的大小是否确实有问题? ?在评论中你说你“有一种感觉”,这在分析上下文中是无用的,可以翻译成“不,可执行文件的大小不是问题”。

    轮廓。收集数据并找出故障点。在担心如何降低可执行文件的大小之前,先了解可执行文件的大小,并确定这是否真的是一个问题。你还没做到。你在一本书中读到“代码膨胀是C++中的一个问题”,因此你认为代码膨胀是程序中的一个问题。是吗?为什么?你如何确定它是什么?

        4
  •  1
  •   Mike Dunlavey    14 年前

    基本上,你在寻找你不需要的昂贵的东西。假设有一类函数不需要占用很大的空间,比如20%。然后,如果从图像大小中随机选取20个字节,那么平均有4个(20*20%)将属于该类别,并且您将能够看到它们。所以,基本上,你拿着这些样本,看看它们,如果你看到一个明显的你并不真正需要的函数模式,那么删除它们。然后 再做一遍 因为使用较少空间的其他类型的例程现在所占的百分比更高。

    所以我同意Suma的观点,解析映射文件是一个好的开始。然后我会写一个例行程序来完成它,并且每5%的方式(空间上的)打印我所处的例行程序。这样我可以得到20个样品。通常,我会发现大量的对象空间是由非常少的(比如1)行源代码产生的,我可以很容易地用另一种方法来实现。

    您还担心过多的内联使函数变大。为了弄清楚这一点,我将取这些示例中的每一个,并且由于它代表特定函数中的特定地址,所以我将把它追溯到它所在的代码行。这样,我就能知道它是否在一个扩展函数中。这有点工作,但可行。

    一个类似的问题是如何在磁盘满了的时候找到肿瘤。同样的想法是,浏览目录树,添加文件大小,然后再浏览一次,当您通过每5%的点时,打印出您所在文件的路径。这不仅告诉你有大文件,也告诉你有大量小文件,不管它们埋得有多深或分散的有多广。当您清除一个不需要的文件类别时,可以再次执行该操作以获取下一个类别,依此类推。

    祝你好运。

        5
  •  1
  •   j0k spurrchalifax    11 年前

    http://www.sikorskiy.net/prj/amap/index.html

    这是由Visual Studio编译器映射文件生成的lib/library大小分析GUI工具中的出色对象文件。该工具分析并从地图文件生成报告。您也可以进行过滤,它会动态显示大小。只需将映射文件输入此工具,此工具将列出由dll/exe生成的给定映射域所占用的函数大小检查上述文件中的屏幕截图/您也可以按大小排序。