【问题标题】:Will the encapsulation of a native type impact on efficiency?原生类型的封装会影响效率吗?
【发布时间】:2012-07-29 09:10:04
【问题描述】:

我想将float 封装在两个结构中,例如:

struct AngleDeg {
    explicit AngleDeg(float angle):value(angle) {}
    float value;
};

struct AngleRad {
    explicit AngleRad(float angle):value(angle) {}
    float value;
};

然后在一个类中使用这些结构重载一个函数,例如:

...
void DoStuff(AngleRad angle);
inline void DoStuff(AngleDeg angle) { DoStuff(Deg2Rad(angle.value)); }
...

这会和使用以下两个函数一样高效吗?

void DoStuffRad(float angle);
inline void DoStuffDeg(float angle) { DoStuffRad(Deg2Rad(angle)); }

【问题讨论】:

  • 为什么不编写一个包含上述所有代码的程序来衡量每个代码的性能?
  • 我做了一个基准测试并在答案中发布了我的结论。

标签: c++ performance inline encapsulation abstraction


【解决方案1】:

这可能不会影响程序的运行速度,但会增加使程序正确的几率。效率很高!

【讨论】:

    【解决方案2】:

    不可能肯定地说。如果您想 100% 确定,请检查生成的程序集(或仅计时代码的执行时间)。

    但是,有两个观察结果可能对您有所帮助:

    • 首先,您的structfloat 的大小相同,因此不会浪费空间。并且没有不必要的间接:可以像以前一样高效地访问包含的float。所以没有根本原因让它变慢
    • 但是,这可能会导致某些编译器优化失效。在某些情况下,它可能只是为structs 生成与内置类型不同的代码。或者,它对floats 使用的调用约定与“大小为float 的对象”不同。

    所以也许,也许不是。但速度差异不太可能产生可衡量的差异。

    【讨论】:

    【解决方案3】:

    我认为空间和访问与@jalf 相同,但不确定。于是我听了@quamrana 和@jalf,做了一个运行时测试。

    在调试模式下,使用结构的方法要慢约 30%,但在发布模式下它们是等效的。它可能取决于编译器,但我将使用结构的方法。

    【讨论】:

    • 这并不奇怪:编译器不会在“调试模式”(无论这意味着什么)下内联函数,以便在出现问题时可以进行堆栈跟踪。
    • 通过调试模式,我的意思是没有优化。应该强调该方法在调试时的影响,因为它可能成为调试实时应用程序时的瓶颈。
    • 这听起来很对。包装在结构中意味着优化器必须更加努力地工作。它可以做到这一点(因此发布版本不受影响),但在禁用优化器的调试版本中,性能会受到影响。
    • 如果您发现它弄乱了时间配置文件或 w/e,您可以只优化一个文件(并忽略其调试信息)并查看它是否有任何改变。
    • @trinithis:这通常不是调试信息的问题(它们位于库的单独部分中,并且仅按需加载)。只是,正如 Alexandre C 所指出的那样,为了获得正确的堆栈跟踪,内联函数实际上在大多数调试模式下都不是内联的。
    【解决方案4】:

    Technical Report on C++ Performance 对此问题进行了调查。有关 Stepanove 抽象惩罚基准,请参见附录 D.3。引用

    基准测试的结构非常简单。它增加了2000 在数组中加倍 25000 次。它以 13 种不同的方式实现 介绍越来越抽象的方法:
    0 - 使用简单 类似于 Fortran 的 for 循环。
    1 - 12 使用 STL 风格的累积模板 带有加号函数对象的函数。
    1, 3, 5, 7 ,9, 11 使用双打。 2, 4, 6, 8, 10, 12 使用 Double - 双包裹在一个类中。
    1, 2 - 使用 常规指针。
    3、4 - 使用封装在类中的指针。
    5、6 - 使用 封装在反向迭代器适配器中的指针。
    7, 8 - 使用包裹 封装在反向迭代器适配器中的指针。
    9、10 - 使用指针 包裹在反向迭代器中的适配器 包裹在反向迭代器中 适配器。
    11, 12 - 使用包装在反向迭代器中的包装指针 适配器包裹在反向迭代器适配器中。

    Ideone 上的输出:现代编译器 (gcc 4.5.1) 的抽象惩罚(所有 13 个测试的几何平均值)小于 1%。

    【讨论】:

      【解决方案5】:

      对于任何值得您花钱的编译器(即使它是免费的),它不会比使用普通浮点数产生任何开销。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-07-01
        • 2012-07-01
        • 1970-01-01
        • 2011-03-02
        • 1970-01-01
        • 2014-11-03
        相关资源
        最近更新 更多