C++ C++;异常处理添加

C++ C++;异常处理添加,c++,exception,exception-handling,embedded,footprint,C++,Exception,Exception Handling,Embedded,Footprint,这个问题对于嵌入式开发尤其重要。异常处理为生成的二进制输出添加了一些内存占用。另一方面,毫无例外,错误需要以其他方式处理,这需要额外的代码,最终也会增加二进制大小 我对你的经历很感兴趣,尤其是: 编译器为异常处理添加的平均占用空间是多少(如果您有这样的度量) 就二进制输出大小而言,异常处理真的比其他错误处理策略更昂贵吗(很多人这么说) 对于嵌入式开发,您建议采用什么错误处理策略 请把我的问题作为指导。欢迎任何意见 附录:是否有一个具体的方法/脚本/工具,对于一个特定的C++对象/可执行文件,将显

这个问题对于嵌入式开发尤其重要。异常处理为生成的二进制输出添加了一些内存占用。另一方面,毫无例外,错误需要以其他方式处理,这需要额外的代码,最终也会增加二进制大小

我对你的经历很感兴趣,尤其是:

  • 编译器为异常处理添加的平均占用空间是多少(如果您有这样的度量)
  • 就二进制输出大小而言,异常处理真的比其他错误处理策略更昂贵吗(很多人这么说)
  • 对于嵌入式开发,您建议采用什么错误处理策略
  • 请把我的问题作为指导。欢迎任何意见


    附录:是否有一个具体的方法/脚本/工具,对于一个特定的C++对象/可执行文件,将显示编译器生成的代码和专用于异常处理的数据结构占用的加载内存占用的百分比?

    我想这取决于特定平台的硬件和工具链端口

    我没有这些数字。然而,对于大多数嵌入式开发,我看到人们放弃了两件事(对于VxWorks/GCC工具链):

    • 模板
    • RTTI

    在大多数情况下,异常处理确实利用了这两种方法,因此也有将其丢弃的趋势

    在那些我们确实想接近金属的情况下,使用了
    setjmp
    /
    longjmp
    。请注意,这可能不是最好的解决方案(或功能非常强大),但这正是我们所使用的

    您可以使用两个版本的基准测试套件在桌面上运行简单的测试,并进行/不进行异常处理,从而获得您最依赖的数据

    关于嵌入式开发的另一件事:模板就像瘟疫一样被避免——它们会导致太多的膨胀。异常标记模板和RTTI,正如Johann Gerell在评论中解释的那样(我认为这是很好理解的)


    同样,这正是我们所做的。在我看来,异常处理对于嵌入式开发来说是不可接受的

    GCC和Microsoft都没有“零开销”异常处理。两个编译器都将序言和尾声语句插入跟踪执行范围的每个函数中。这将导致性能和内存占用的显著增加

    根据我的经验,性能差异大约为10%,这对于我的工作领域(实时图形)来说是一个巨大的数字。内存开销要小得多,但仍然很重要——我现在记不起这个数字,但使用GCC/MSVC,可以很容易地编译程序并测量差异

    我见过一些人将异常处理作为“仅在使用时”成本来讨论。根据我观察到的情况,这根本不是真的。当启用异常处理时,它会影响所有代码,代码路径是否可以抛出异常(当您考虑编译器如何工作时,这是完全意义的)。
    对于嵌入式开发,我也会远离RTTI,尽管我们确实在调试构建中使用它来检查向下广播结果的合理性。

    我在低延迟环境中工作。(对于我在生产“链”中的应用程序,不到300微秒)根据我的经验,异常处理会根据您的工作量增加5-25%的执行时间

    我们通常不关心二进制膨胀,但如果膨胀过多,就会疯狂地挥舞,所以需要小心

    只要保持二进制文件的合理性(取决于您的设置)

    我对我的系统进行了相当广泛的分析。
    其他令人讨厌的领域:

    伐木

    持久化(我们只是不这样做,或者如果我们并行地这样做)

    当异常发生时,会有时间开销,这取决于您如何实现异常处理。但是,作为一个轶事,应该引起异常的事件的严重性将需要与使用任何其他方法处理相同的时间。为什么不使用高度支持的基于语言的方法来处理这些问题呢

    GNU C++编译器默认使用零成本模型,即在不发生异常时没有时间开销。 由于有关异常处理代码和本地对象偏移量的信息可以在编译时计算一次,因此这些信息可以保存在与每个函数关联的单个位置,但不能保存在每个ARI中。实际上,您可以从每个ARI中删除异常开销,从而避免将它们推送到堆栈上的额外时间。这种方法称为异常处理的零成本模型,前面提到的优化存储称为影子堆栈。-Bruce Eckel,C++思想第2卷< /P> 大小复杂度开销不容易量化,但Eckel表示平均为5%和15%。这将取决于异常处理代码的大小与应用程序代码的大小的比例。如果您的程序很小,那么异常将是二进制文件的很大一部分。如果您使用的是零成本模型,那么异常将占用更多的空间来消除时间开销,因此如果您关心的是空间而不是时间,那么就不要使用零成本编译


    <强>我的意见是大多数嵌入式系统内存充足,如果系统有C++编译器,你就有足够的空间来包含异常。在我的项目中使用的PC/104计算机有几个GB的二次存储器,512兆字节的主存储器,因此没有例外的空间问题。然而,我们的微控制器是用C编程的。我的启发式是“如果有一个主流的C++编译器,使用异常,否则使用C”。< /P> < P>测量事物,第2部分。我现在有两个程序。第一个是C语言,使用gcc-O2编译:

    #include <stdio.h>
    #include <time.h>
    
    #define BIG 1000000
    
    int f( int n ) {
        int r = 0, i = 0;
        for ( i = 0; i < 1000; i++ ) {
            r += i;
            if ( n == BIG - 1 ) {
                return -1;
            }
        }
        return r;
    }
    
    int main() { 
        clock_t start = clock();
        int i = 0, z = 0;
        for ( i = 0; i < BIG; i++ ) {
            if ( (z = f(i)) == -1 ) { 
                break;
            }
        }
        double t  = (double)(clock() - start) / CLOCKS_PER_SEC;
        printf( "%f\n", t );
        printf( "%d\n", z );
    }
    
    #包括
    #包括
    
    #include <stdio.h>
    #include <time.h>
    
    #define BIG 1000000
    
    int f( int n ) {
        int r = 0, i = 0;
        for ( i = 0; i < 1000; i++ ) {
            r += i;
            if ( n == BIG - 1 ) {
                throw -1;
            }
        }
        return r;
    }
    
    int main() { 
        clock_t start = clock();
        int i = 0, z = 0;
        for ( i = 0; i < BIG; i++ ) {
            try {
             z += f(i); 
            }
            catch( ... ) {
                break;
            }
    
        }
        double t  = (double)(clock() - start) / CLOCKS_PER_SEC;
        printf( "%f\n", t );
        printf( "%d\n", z );
    }