接口范例性能(动态绑定vs.通用编程) [英] interface paradigm performance (dynamic binding vs. generic programming)

查看:350
本文介绍了接口范例性能(动态绑定vs.通用编程)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

虽然在它们的核心动态绑定和模板是根本不同的东西,它们可以用于实现相同的功能。

While at their core dynamic binding and templates are fundamentally different things, they can be used to implement the same functionality.

namespace DB {
  // interface
  class CustomCode {
    public:
      virtual void operator()(char) const = 0;
  };
  class Lib {
    public:
      void feature(CustomCode const& c) {
        c('d');
      }
  };

  // user code
  class MyCode1 : public CustomCode {
    public:
      void operator()(char i) const {
        std::cout << "1: " << i << std::endl;
      }
  };
  class MyCode2 : public CustomCode {
    public:
      void operator()(char i) const {
        std::cout << "2: " << i << std::endl;
      }
  };

  void use() {
    Lib lib;
    lib.feature(MyCode1());
    lib.feature(MyCode2());
  }
}



B)通用编程



B) generic programming

namespace GP {
  //interface
  template <typename CustomCode> class Lib {
    public:
      void feature(CustomCode const& c) {
        c('g');
      }
  };

  // user code
  class MyCode1 {
    public:
      void operator()(char i) const {
        std::cout << "1: " << i << std::endl;
      }
  };
  class MyCode2 {
    public:
      void operator()(char i) const {
        std::cout << "2: " << i << std::endl;
      }
  };

  void use() {
    Lib<MyCode1> lib;
    lib.feature(MyCode1());
    //lib.feature(MyCode2());  <-- illegal
  }
}



问题



一些想法



虽然这些范例不完全相同,有其优缺点( A 有点更强大(见 MyCode2 )和 B 更灵活的用户)允许实现相同的功能(同时适用上面提到的限制)。

Question

Some thoughts

While these paradigms are not identical and have their advantages and disadvantages (A is a bit more powerful (see MyCode2) and B is more flexible for the user) they both allow the implementation of the same functionality (while the limitations hinted above apply).

无论如何,在理论上 > 在运行时因为虚函数的间接性而有点慢,而 B 提供了一些很好的优化机会,因为方法可以内联当然你没有间接)。

然而,我经常觉得 A 有点更自我记录,因为你有一个(通常由多个方法组成),而 B 有点更无政府主义(这意味着它的灵活性)。

Anyway, in theory (TM) A is a bit slower at runtime because of the indirection of the virtual function, while B offers some great optimisation opportunities as methods can be inlined (and of course you don't have the indirection).
However, I often feel that A is a bit more self-documenting because you have a clear interface you have to implement (which usually consists of more than one method) while B is a bit more anarchistic (which implies its flexibility).


  1. 这些范式有一般结果/比较研究吗?

  2. 加速是否有意义?

  3. 编译时间如何?

  4. (我主要使用 A 为我的模块间接口,我到目前为止还没有做过真的很大的项目)

  1. Are there any general results / comparative studies of these paradigms?
  2. Is the speed-up significant?
  3. What about compilation time?
  4. What are the design implications of either for interfaces in larger systems (I mainly used A for my inter-module interfaces and I haven't done really really big projects so far)?



编辑



注意:说动态绑定更好,因为它更强大根本不是一个答案,因为前提条件是你有两种方法都适用的情况(否则没有选择的自由 - 至少不合理)。

Edit

Note: Saying "dynamic binding is better because it is more powerful" is not at all an answer, because the precondition is that you have a case where both approaches are applicable (otherwise there is no freedom to choose -- at least not reasonably).

推荐答案


有这些范例的一般结果/比较研究吗?

Are there any general results / comparative studies of these paradigms?

,许多证明的例子可以在文章和出版物中找到。你最喜欢的c ++书应该提供几个示范;如果你没有这样的资源,你可能想阅读现代C + +设计:应用的通用编程和设计模式 - A. Alexandrescu 。虽然,不是一个特定的资源,记住,直接回答你的问题。以及,实现和编译器的结果会有所不同 - 即使编译器设置可以大大影响这种测试的结果。 (回答你的每一个问题,虽然这不能作为这个具体问题的答案)。

from what i have seen, many examples of proofs can be found in articles and publications. your favorite c++ books should provide several demonstrations; if you have no such resource, you may want to read Modern C++ Design: Generic Programming and Design Patterns Applied - A. Alexandrescu. although, there is not a specific resource that comes to mind that directly answers your question. as well, the result will vary by implementation and compiler - even compiler settings can greatly affect the outcome of such a test. (responding to each of your questions, although this does not qualify as an answer to this specific question).


加速显着?

Is the speed-up significant?

简短的答案:

编译器实际上可以使用静态分派或甚至内联虚函数调用(足够的信息对编译器可见)。我现在要将响应从一个简单的例子(具体来说,OP)移动到更大,更复杂的程序。

in your example, the compiler could in fact use static dispatch or even inline the virtual function calls (enough information is visible to the compiler). i am now going to move the responses away from a trivial example (specifically, the OP) to larger, more complex programs.

加速可以从不可测量到巨大。你必须(可能已经)意识到编译器可以在通过泛型编译时提供大量的信息。它可以使用这些信息来更准确地优化您的程序。一个很好的例子是使用 std :: array vs std :: vector 。向量在运行时增加了灵活性,但是成本可能相当大。向量需要实现更多的调整大小,对动态分配的需要可能是昂贵的。还有其他的区别:数组的后台分配不会改变(++优化),元素计数是固定的(++优化),并且在很多情况下不需要通过新的调用。

expanding on 'it depends': yes, the speed up can range from unmeasurable to huge. you have to (and likely already) realize that the compiler can be provided an incredible amount of information at compilation via generics. it can then use this information to optimize your program much more accurately. one good example of this is the use of std::array vs std::vector. the vector adds flexibility at runtime, but the cost can be quite significant. the vector needs to implement more for resizing, the need for dynamic allocations can be costly. there are other differences: the backing allocation of the array will not change (++optimization), the element count is fixed (++optimization), and again - there's no need to call through new in many cases.

你现在可能认为这个例子已经明显偏离了原来的问题。在许多方面,它真的没有那么不同:编译器越来越多地了解你的程序,因为它的复杂性扩展。这个信息可以删除你的程序的几个部分(死代码)并使用 std :: array 作为例子,类型提供的信息就足够了,编译器可以轻松地说哦,我看到这个数组的大小是七个元素,我会展开循环相应,你将有更少的指令,将消除误预测。但是在数组/向量的情况下,我看到可执行大小的优化程序从向量转换为类似于 array 。以及,代码可以执行几倍更快。实际上,一些表达式可以在编译时完全计算。

you may now be thinking this example has significantly deviated from the original question. in many ways, it's really not so different: the compiler knows more and more about your program as its complexity expands. this information can remove several portions of your program (dead code) and using std::array as an example, the information the type provides is enough such that a compiler can easily say "oh, i see that this array's size is seven elements, i will unroll the loop accordingly" and you will have fewer instructions and will have eliminated mispredictions. there's a lot more to it, but in the array/vector case, i have seen executable size of optimized programs reduce to 20% when converting from vector to an interface similar to array. as well, the code can perform several times faster. in fact, some expressions can be calculated entirely at compilation.

动态调度仍然有其优点,使用动态调度也可以提高程序的速度,如果使用正确 - 将真正需要学习来决定什么时候偏爱一个对另一个。类似于具有许多变量的巨大函数不能被非常有效地优化(在真实程序中所有模板扩展的结果),虚拟函数调用在许多情况下实际上可以是更快,更清洁的方法。因此,他们是两个独立的功能,你需要一些实践来确定什么是正确的(许多程序员不花时间来学习这个够好)。

dynamic dispatch still has its virtues, and using dynamic dispatch can also improve your program's speed if used correctly - what you will really need to learn comes down to deciding when to favor one over the other. similar to how a huge function with many variables cannot be optimized very effectively (the result of all that template expansion in a real program), a virtual function call can in fact be a faster, cleaner approach in many situations. as such, they are two separate features, you will need some practice to determine what is right (and many programmers don't take the time to learn this well enough).

结论,它们应被视为单独的特征,适用于不同的场景。这些应该(imho)的实际重叠要比他们在现实世界中的实际重叠小得多。

in conclusion, they should be regarded as separate features, applicable to different scenarios. these should (imho) have have much less practical overlap than they actually do in the real world.


编译时间? >

What about compilation time?

使用模板,开发期间的编译和链接时间可能相当高。每次头/模板更改时,您将需要对所有依赖关系进行编译 - 这通常是有利于动态分派的重要优势。你当然可以减少这一点,如果你计划和建设适当 - 理解如何是一个更难的主题与模板。与模板,你不仅增加了大型构建的频率,你经常增加大型构建的时间和复杂性。 (更多注释跟随)

with templates, the compilation and link times during development can be quite high. each time a header/template changes, you will require a compilation on all dependencies -- this can often be a significant boon for favoring dynamic dispatch. you can of course reduce this if you plan ahead and build out appropriately - understanding how is a much more difficult subject to master with templates. with templates, you not only increrase the frequency of large builds, you often increase the time and complexity of large builds. (more notes follow)


对于大型系统中的接口,我们的设计含义是什么(我主要使用A作为我的模块间接口到目前为止,我还没有做过真正的大项目)

What are the design implications of either for interfaces in larger systems (I mainly used A for my inter-module interfaces and I haven't done really really big projects so far)?

这真的取决于你的程序的期望。我每年写更少的虚拟(以及许多其他)。在其他方法中,模板正变得越来越常见。老实说,我不明白 B 是如何是无政府主义的。对我来说, A 有点过时,因为有很多合适的替代品。它最终是一个设计选择,可以采取很多考虑建设大型系统好。一个好的系统将使用语言的功能的健康组合。历史证明没有特征在这个讨论是必要的写一个非平凡的程序,但所有的功能都添加了,因为有人看到更好的替代品在一些特定的用途。您也应该期望lambdas在其中一些(不是全部)小组/代码库中替换其当前使用的50%以上的虚拟。

it really depends on your program's expectations. i write virtual less every year (and many others as well). among other approaches, templates are becoming more and more common. honestly, i don't understand how B is 'anarchistic'. to me, A is a bit anachronistic as there are plenty of suitable alternatives. it's ultimately a design choice which can take a lot of consideration to architect large systems well. a good system will use a healthy combination of the language's features. history proves no feature in this discussion is necessary to write a nontrivial program, but all features were added because somebody saw better alternatives in some specific uses. you should also expect lambdas to replace virtuals in more than 50% of their current uses in some (not all) teams/codebases.


  • 如果使用正确,模板可以更快地执行。

  • 可执行文件。如果正确使用并且可执行大小很重要,那么写者将使用多种方法来减少可执行文件大小,同时提供很好的可用接口。

  • 模板可以变得非常复杂。学习爬行和解释错误消息需要时间。

  • 模板将几个错误推送到编译域。

  • 通过虚拟机减少编译时间通常是简单的(虚拟机属于.cpp)。如果你的程序是巨大的,那么巨大的模板系统,经常更改可以快速发送您的重建时间和计数通过屋顶,因为将有很多模块的可见性和依赖。

  • 延迟和/或者使用较少编译文件的选择性实例化可用于减少编译时间。

  • 在更大的系统中,您必须更加注意为您的团队/客户端强制执行重要的编译。使用虚拟是一种方法来最小化这一点。同时,更高百分比的方法将在cpp中定义。替代方案当然是您可以隐藏更多的实现或提供给客户更具表现力的方式来使用您的界面。

  • 在更大的系统,模板,lambdas / functors等

  • 虚拟增加依赖性,通常变得难以维护,膨胀接口,并成为结构上笨拙的野兽。以模板为中心的库倾向于反转该优先级。

  • 所有方法可能由于错误的原因使用。

  • templates can be significantly faster to execute, if used correctly.
  • either can produce larger executables. if used correctly and executable size is important, then the writer will use multiple approaches to reduce executable size while providing nice usable interfaces.
  • templates can grow very very complex. learning to crawl through and interpret error messages takes time.
  • templates push several errors into the compilation domain. personally, i favor a compilation error over a runtime error.
  • reducing compilation times via virtuals is often straightforward (virtuals belong in the .cpp). if your program is huge, then huge template systems that change often can quickly send your rebuild times and counts through the roof because there will be a lot of intermodule visibility and dependency.
  • deferred and/or selective instantiation with fewer compiled files can be used to reduce compile times.
  • in larger systems, you'll have to be much more considerate about forcing a nontrivial recompiles for your team/client. using virtuals are one approach to minimize this. as well, a higher percentage of your methods will be defined in the cpp. the alternative is of course that you can hide more of the implementation or provide to the client more expressive ways to use your interfaces.
  • in larger systems, templates, lambdas/functors, etc. can actually be used to significantly reduce coupling and dependencies.
  • virtuals increase dependency, often become difficult to maintain, bloat interfaces, and become structurally awkward beasts. template-centric libraries tend to invert that precedence.
  • all approaches can be used for the wrong reasons.

底线
a大型,精心设计的现代系统将有效地同时使用很多范例。如果你目前使用虚拟大部分时间,你是(imo)做错了 - 特别是如果这仍然是一种方法,一旦你有时间吸收c ++ 11。如果速度,性能和/或并行性也是重要的问题,那么模板和lambdas应该是你更亲密的朋友。

Bottom Line a large, well designed modern system will use many paradigms effectively and simultaneously. if you use virtuals most of the time at present, you are (imo) doing it wrong -- especially if that is still the approach once you've had time to absorb c++11. if speed, performance, and/or parallelism are also significant concerns, then templates and lambdas deserve to be your closer friends.

这篇关于接口范例性能(动态绑定vs.通用编程)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆