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

Dov*_*Dov 10 c++ debugging optimization performance 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 ...) {
      ...
    }
  }
};
Run Code Online (Sandbox Code Playgroud)

这些类完全没必要,单个函数可以工作.每个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
}
Run Code Online (Sandbox Code Playgroud)

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

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

zvr*_*rba 5

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