gcc 如何分析异常处理的性能开销

zpjtge22  于 2023-10-19  发布在  其他
关注(0)|答案(8)|浏览(131)

在C中测量异常处理开销/性能的最佳方法是什么?
请给予独立的代码样本。
我的目标是Microsoft Visual C
2008和GCC。
我需要从以下案例中获得结果:
1.没有try/catch块时的开销
1.当有try/catch块但没有抛出异常时的开销
1.引发异常时的开销

px9o7tmv

px9o7tmv2#

作为一个建议:当抛出异常时,不要太在意开销。异常处理的实现通常会使不抛出变快,捕获变慢。这是确定的,因为这些情况下,嗯,例外。
卡尔

yyhrrdl8

yyhrrdl83#

这是我想出来的测量代码。你觉得有什么问题吗?
到目前为止可以在Linux和Windows上运行,编译时使用:

g++ exception_handling.cpp -o exception_handling [ -O2 ]

或者例如Visual C++ Express
要获取基本情况(“从语言中完全删除异常支持”),请使用:用途:

g++ exception_handling.cpp -o exception_handling [ -O2 ] -fno-exceptions -DNO_EXCEPTIONS

或类似的MSVC设置。
一些初步结果here。由于机器负载的变化,它们可能都是虚假的,但它们确实给予了一些关于相对异常处理开销的概念。(执行摘要:当没有抛出异常时,没有或很少,当它们实际上被抛出时,巨大。

#include <stdio.h>

// Timer code

#if defined(__linux__)
#include <sys/time.h>
#include <time.h>

double time()
{
    timeval tv;
    gettimeofday(&tv, 0);
    return 1.0 * tv.tv_sec + 0.000001 * tv.tv_usec;
}
#elif defined(_WIN32)
#include <windows.h>

double get_performance_frequency()
{
    unsigned _int64 frequency;
    QueryPerformanceFrequency((LARGE_INTEGER*) &frequency); // just assume it works
    return double(frequency);
}

double performance_frequency = get_performance_frequency();

double time()
{
    unsigned _int64 counter;
    QueryPerformanceCounter((LARGE_INTEGER*) &counter);
    return double(counter) / performance_frequency;
}
#else
# error time() not implemented for your platform
#endif

// How many times to repeat the whole test
const int repeats = 10;

// How many times to iterate one case
const int times = 1000000;

// Trick optimizer to not remove code
int result = 0;


// Case 1. No exception thrown nor handled.

void do_something()
{
    ++result;
}

void case1()
{
    do_something();
}


// Case 2. No exception thrown, but handler installed

#ifndef NO_EXCEPTIONS
void do_something_else()
{
    --result;
}

void case2()
{
    try
    {
        do_something();
    }
    catch (int exception)
    {
        do_something_else();
    }
}


// Case 3. Exception thrown and caught

void do_something_and_throw()
{
    throw ++result;
}

void case3()
{
    try
    {
        do_something_and_throw();
    }
    catch (int exception)
    {
        result = exception;
    }
}
#endif // !NO_EXCEPTIONS

void (*tests[])() =
{
    case1,
#ifndef NO_EXCEPTIONS
    case2,
    case3
#endif // !NO_EXCEPTIONS
};

int main()
{
#ifdef NO_EXCEPTIONS
    printf("case0\n");
#else
    printf("case1\tcase2\tcase3\n");
#endif
    for (int repeat = 0; repeat < repeats; ++repeat)
    {
        for (int test = 0; test < sizeof(tests)/sizeof(tests[0]); ++test)
        {
            double start = time();

            for (int i = 0; i < times; ++i)
                tests[test]();

            double end = time();

            printf("%f\t", (end - start) * 1000000.0 / times);
        }
        printf("\n");
    }

    return result; // optimizer is happy - we produce a result
}
rbpvctlc

rbpvctlc4#

Kevin Frei在他的演讲“The Cost of C++ Exception Handling on Windows“中谈到了异常处理性能成本。(在“总结和结论”下有一个列表项,上面写着“[异常处理性能成本]并不总是可衡量的”。

izkcnapc

izkcnapc5#

在代码中没有好的方法来保证这一点。你需要使用一个侧写器。
这不会直接显示异常处理花费了多少时间,但通过一点点研究,您将发现哪些运行时方法处理异常(例如,对于VC++.NET,它是__cxx_exc[...])。
把他们的时间加起来,你就有了开销。在我们的项目中,我们使用了Intel的vcss,它可以与Visual C++和gcc一起工作。
编辑:好吧,如果你只是需要一个通用的数字,可能会工作。我以为你有一个实际的应用程序来分析你不能关闭异常的地方。

aij0ehis

aij0ehis6#

关于异常处理性能的另一个注意事项:简单的测试不考虑缓存。try代码和catch代码都很小,可以容纳指令和数据缓存。但是编译器可能会尝试将catch-code移到远离try-code的地方,这减少了通常需要保存在缓存中的代码量,从而提高了性能。
如果将异常处理与传统的C风格的返回值检查进行比较,那么也应该考虑这种缓存效应(在讨论中通常忽略这个问题)。
卡尔

7xzttuei

7xzttuei7#

答案难道不取决于投掷的结果是什么?如果抛出一个异常,导致整个对象负载超出堆栈的作用域,那么这将增加开销。
换句话说,我不确定第三个问题的答案是否独立于代码的细节。

dojqjjoe

dojqjjoe8#

关于g++如何处理异常的完整细节显示在here中。它将其描述为用于Itanium体系结构,但是使用的一般技术是相同的。它不会告诉你确切的时间开销,但是你可以收集粗略的代码开销。

相关问题