因为调试符号的巨大可执行文件,为什么?

时间:2011-05-28 12:12:57

标签: c++ performance debugging optimization symbols

我们一直在银行开发大型金融应用程序。它开始是15万行非常糟糕的代码。到1个月前,它下降到一半多一点,但可执行文件的大小仍然很大。我预计,因为我们只是让代码更具可读性,但模板化代码仍然生成了大量的目标代码,我们只是在努力提高效率。

应用程序分为大约5个共享对象和一个主要对象。其中一个较大的共享对象是40Mb,甚至在代码缩小时也增长到50个。

我对代码开始增长并不感到惊讶,因为毕竟我们正在添加一些功能。但我很惊讶它增长了20%。当然没有人接近编写20%的代码,所以我很难想象它是如何增长的那么多。这个模块对我来说很难分析,但是在星期五,我有一个新的数据点可以解释一下。

SOAP服务器可能有10个提要。代码是自动生成的,非常糟糕。每个服务都有一个具有完全相同代码的解析器类,如:

#include <boost/shared_ptr.hpp>
#include <xercesstuff...>
class ParserService1 {
public:
  void parse() {
    try {
      Service1ContentHandler*p = new Service1ContentHandler( ... );
      parser->setContentHandler(p);
      parser->parser();
    } catch (SAX ...) {
      ...
    }
  }
};

这些类完全没必要,单个函数可以工作。每个ContentHandler类都使用相同的7或8个变量自动生成,我可以与继承共享。

所以当我从代码中删除解析器类时,我希望代码的大小会下降。但是只有10项服务,我没想到它会从38Mb降到36Mb。这是一个令人发指的符号数量。

我唯一能想到的是每个解析器都包含boost :: shared_ptr,一些Xerces解析器的东西,不知何故,编译器和链接器为每个文件重复存储所有这些符号。无论如何我很想知道。

那么,任何人都可以建议我如何追踪为什么像这样的简单修改会产生如此大的影响?我可以在模块上使用nm来查看里面的符号,但是这会产生一些痛苦的,大量的半可读内容。

此外,当同事用我的新库运行她的代码时,用户时间从1分55秒到1分25秒。实时是高度可变的,因为我们正在等待慢速SOAP服务器(恕我直言,SOAP是一个非常糟糕的CORBA替代品......)但CPU时间非常稳定。我本来期望通过减少代码大小来获得一点点提升,但最重要的是,在具有大容量内存的服务器上,我真的很惊讶速度受到如此大的影响,考虑到我没有改变架构的XML处理本身。

周二我会更进一步,希望能获得更多信息,但如果有人知道如何能得到这么大的改进,我很想知道。

更新: 事实证明,在任务中使用调试符号似乎根本不会改变运行时间。我这样做是通过创建一个包含许多东西的头文件来实现的,包括在这里产生效果的两个:boost共享指针和一些xerces XML解析器。似乎没有运行时性能损失(我检查过,因为两个答案之间存在意见分歧)。但是,我还验证了包含头文件为每个实例创建调试符号,即使剥离的二进制文件大小不变。因此,如果您包含一个给定的文件,即使您甚至不使用它,也会有一定数量的符号反对该对象,这些符号在链接时没有折叠在一起,即使它们大概相同。

我的代码如下:

#include "includetorture.h"
void f1()
{
    f2(); // call the function in the next file
}

我的特定包含文件的大小约为每个源文件100k。据推测,如果我包括更多,它会更高。包含的总可执行文件大约是600k,没有大约9k。我确认增长与包含文件的数量呈线性关系,但剥离的代码大小相同,不管它应该如何。

显然,我错误地认为这是性能提升的原因。我想我现在已经解决了这个问题。尽管我没有删除太多代码,但我确实简化了大量的大型xml字符串处理,并大大减少了通过代码的路径,这可能就是原因。

3 个答案:

答案 0 :(得分:5)

您可以在linux上使用readelf实用程序,或在windows上使用dumpbin,以查找exe文件中各种数据所使用的确切空间量。虽然,我不明白为什么可执行文件大小令你担心:调试符号在运行时使用绝对没有内存!

答案 1 :(得分:2)

您似乎正在使用许多带有内联方法的c ++类。如果这些类具有高可见性,则此内联代码将使整个应用程序膨胀。我打赌你的链接时间也增加了。尝试减少内联方法的数量并将代码移动到.cpp文件。这将减少目标文件的大小,exe文件并减少链接时间。

在这种情况下的权衡当然是减少了编译单元的大小,而不是执行时间。

答案 2 :(得分:2)

我没有你期待你的问题的答案,但让我分享我的经验。

可执行文件的大小差异非常大。我无法详细解释原因,但只考虑现代调试器允许您对代码执行的所有疯狂事情。你知道,这要归功于调试符号。

大小的差异是如此之大,以至于如果您动态加载某些共享库,那么文件的绝对加载时间可以解释您找到的性能差异。

实际上,这是编译器的一个非常“内部”的方面,而且只是为了给你一个例子,多年前我对GCC-4与GCC-3相比产生的巨大可执行文件非常不满意,然后我只是已经习惯了(我的HD尺寸也在增长)。

总而言之,我不介意,因为你应该只在开发过程中使用带有调试符号的构建,这应该不是问题。在部署中,没有调试符号,您将看到文件将缩小多少。