前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >C++核心准则Per.7:设计要为优化做准备

C++核心准则Per.7:设计要为优化做准备

作者头像
面向对象思考
发布2020-06-24 10:43:29
4150
发布2020-06-24 10:43:29
举报

Per.7: Design to enable optimization

Per.7:设计要为优化做准备

Reason(原因)

Because we often need to optimize the initial design. Because a design that ignores the possibility of later improvement is hard to change.

因为我们经常需要优化最初的设计。忽略将来的发展可能性的设计很难变更。

Example(示例)

From the C (and C++) standard:

来自C(和C++)标准:

代码语言:javascript
复制
void qsort (void* base, size_t num, size_t size, int (*compar)(const void*, const void*));

When did you even want to sort memory? Really, we sort sequences of elements, typically stored in containers. A call to qsort throws away much useful information (e.g., the element type), forces the user to repeat information already known (e.g., the element size), and forces the user to write extra code (e.g., a function to compare doubles). This implies added work for the programmer, is error-prone, and deprives the compiler of information needed for optimization.

你什么时候有过内存排序的需求?事实上,我们对通常保存在容器中的元素排序。快速排序调用丢掉了特别有用信息(例如元素类型),要求用户重复提供已知的信息(例如元素大小),要求用户书写额外的代码(例如双精度数比较函数)。这个实现会增加程序员的工作,易错,使编译器无法获得可用于优化的信息。

代码语言:javascript
复制
double data[100];
// ... fill a ...

// 100 chunks of memory of sizeof(double) starting at
// address data using the order defined by compare_doubles
qsort(data, 100, sizeof(double), compare_doubles);

From the point of view of interface design is that qsort throws away useful information.

从接口设计的观点来看,qsort丢掉了有用的信息。

We can do better (in C++98)

我们可以做得好一些(C++98)

代码语言:javascript
复制
template<typename Iter>
    void sort(Iter b, Iter e);  // sort [b:e)

sort(data, data + 100);

Here, we use the compiler's knowledge about the size of the array, the type of elements, and how to compare doubles.

这里我们使用的编译器已经知道了数组大小信息,元素的类型,比较双精度数的方法等。

With C++11 plus concepts, we can do better still

使用C++11加概念,我们可以做得更好

代码语言:javascript
复制
// Sortable specifies that c must be a
// random-access sequence of elements comparable with <
void sort(Sortable& c);

sort(c);

The key is to pass sufficient information for a good implementation to be chosen. In this, the sort interfaces shown here still have a weakness: They implicitly rely on the element type having less-than (<) defined. To complete the interface, we need a second version that accepts a comparison criteria:

重要的是为选择的良好实现传递足够的信息。这段代码中,sort结构仍然有弱点:它隐含要求元素类型定义了小于(<)运算符。

代码语言:javascript
复制
// compare elements of c using p
void sort(Sortable& c, Predicate<Value_type<Sortable>> p);

The standard-library specification of sort offers those two versions, but the semantics is expressed in English rather than code using concepts.

标准库中定义的sort提供了上面的两个版本,但是语义通过英语表达,而不是使用了concept的代码。

Note(注意)

Premature optimization is said to be the root of all evil, but that's not a reason to despise performance. It is never premature to consider what makes a design amenable to improvement, and improved performance is a commonly desired improvement. Aim to build a set of habits that by default results in efficient, maintainable, and optimizable code. In particular, when you write a function that is not a one-off implementation detail, consider

过早的优化被认为是万恶之源,但是这不应该成为轻视性能的理由。考虑一个设计如何适应变化永远不嫌早,改善性能也是最普通的提高。努力建立一套可以自然产生有效,可维护,可优化代码的习惯。通常情况下,当你编写一个可以复用的函数时:

  • Information passing: Prefer clean interfaces carrying sufficient information for later improvement of implementation. Note that information flows into and out of an implementation through the interfaces we provide.
  • 信息传递:提供可以为进一步开发提供足够信息的清晰的接口。注意透过我们提供的接口进出的信息流。
  • Compact data: By default, use compact data, such as std::vector and access it in a systematic fashion. If you think you need a linked structure, try to craft the interface so that this structure isn't seen by users.
  • 紧凑数据:默认情况下,使用紧凑数据,例如std::vector并使用系统化的方式进行访问。如果你觉得你需要链式结构,努力调整接口以保证该结构对用户不可见。
  • Function argument passing and return: Distinguish between mutable and non-mutable data. Don't impose a resource management burden on your users. Don't impose spurious run-time indirections on your users. Use conventional ways of passing information through an interface; unconventional and/or "optimized" ways of passing data can seriously complicate later reimplementation.
  • 函数参数传递和返回:区别可修改和不能修改的数据。不要将资源管理责任强加给用户。不要将虚假的运行时间接处理强加给用户。使用惯用方式通过接口传递信息;以非惯用的,“优化过的”方式传递数据会为以后的重新实现造成严重的困难。
  • Abstraction: Don't overgeneralize; a design that tries to cater for every possible use (and misuse) and defers every design decision for later (using compile-time or run-time indirections) is usually a complicated, bloated, hard-to-understand mess. Generalize from concrete examples, preserving performance as we generalize. Do not generalize based on mere speculation about future needs. The ideal is zero-overhead generalization.
  • 抽象:不要过度泛化;试图让设计适用于(包含错误用法)所有可能用法,或者(使用编译时指令或执行时指令)为将来(的优化)推迟所有的设计判断通常都会导致复杂,臃肿的,难以理解的混乱结果。从具体的示例进行泛化,泛化的同时维持性能。不要仅仅基于对将来需求的推测就进行泛化。理想的状态是0开销泛化。
  • Libraries: Use libraries with good interfaces. If no library is available build one yourself and imitate the interface style from a good library. The standard library is a good first place to look for inspiration.
  • 库:使用具有良好接口的库。如果没有可用的库,可以自己构建一个并模仿优秀库的接口。标准库可以作为寻找灵感的好起点。
  • Isolation: Isolate your code from messy and/or old-style code by providing an interface of your choosing to it. This is sometimes called "providing a wrapper" for the useful/necessary but messy code. Don't let bad designs "bleed into" your code.
  • 隔离:通过选择一个接口将你的代码和混乱代码或旧风格代码进行隔离。这种方式有时被叫做为有用且必要的混乱代码“提供一个封装”。不要让不好的设计流入你的代码中。
Example(示例)

Consider(考虑):

代码语言:javascript
复制
template<class ForwardIterator, class T>
bool binary_search(ForwardIterator first, ForwardIterator last, const T& val);

binary_search(begin(c), end(c), 7) will tell you whether 7 is in c or not. However, it will not tell you where that 7 is or whether there are more than one 7.

binary_search(begin(c), end(c), 7)会告诉你7是否包含在c中。然而它不会告诉你7在哪里,也不会告诉你是否存在更多的7。

Sometimes, just passing the minimal amount of information back (here, true or false) is sufficient, but a good interface passes needed information back to the caller. Therefore, the standard library also offers

有时,只返回最小限度的信息(这里是true或false)就足够了,但是好的接口会向调用者提供需要的信息。因此,标准库也提供了

代码语言:javascript
复制
template<class ForwardIterator, class T>
ForwardIterator lower_bound(ForwardIterator first, ForwardIterator last, const T& val);

lower_bound returns an iterator to the first match if any, otherwise to the first element greater than val, or last if no such element is found.

如果存在,lower_bound返回指向第一个匹配结果的迭代器,否则返回第一个大于val的元素,如果不存在匹配元素返回最后一个元素。

However, lower_bound still doesn't return enough information for all uses, so the standard library also offers

然而,lower_bound依然没有返回可以满足所有使用者需求的足够的信息。因此标准库也提供了

代码语言:javascript
复制
template<class ForwardIterator, class T>
pair<ForwardIterator, ForwardIterator>
equal_range(ForwardIterator first, ForwardIterator last, const T& val);

equal_range returns a pair of iterators specifying the first and one beyond last match.

equal_range返回了定义第一个匹配元素和最后一个匹配元素的下一个位置的迭代器对。

代码语言:javascript
复制
auto r = equal_range(begin(c), end(c), 7);
for (auto p = r.first; p != r.second; ++p)
    cout << *p << '\n';

Obviously, these three interfaces are implemented by the same basic code. They are simply three ways of presenting the basic binary search algorithm to users, ranging from the simplest ("make simple things simple!") to returning complete, but not always needed, information ("don't hide useful information"). Naturally, crafting such a set of interfaces requires experience and domain knowledge.

显然,这三个接口是通过相同的基础代码实现的。这只是存在3种向使用者提供基本二进制检索算法的方式的情况,范围从最简单的(让简单的事情保持简单)到不会经常用到的返回全部信息(不要隐藏有用信息)。很显然,提供这样的接口集合需要经验和领域知识。

Note(注意)

Do not simply craft the interface to match the first implementation and the first use case you think of. Once your first initial implementation is complete, review it; once you deploy it, mistakes will be hard to remedy.

不要简单地让接口和第一次实现方式或你想象的第一次使用场景匹配。当第一次完成最初的实现时,重新审视一下;一旦发布,失误很难挽回。

Note(注意)

A need for efficiency does not imply a need for low-level code. High-level code does not imply slow or bloated.

效率方面的需求不一定意味着低层次代码。高层次代码也不一定意味着低速和臃肿。

Note(注意)

Things have costs. Don't be paranoid about costs (modern computers really are very fast), but have a rough idea of the order of magnitude of cost of what you use. For example, have a rough idea of the cost of a memory access, a function call, a string comparison, a system call, a disk access, and a message through a network.

凡事都需要成本。不要偏执于成本(现代的计算机真的非常快),但是对于正在使用的技术(或方法)的代价的顺序,应该有足够的认识。例如,对内存访问,函数调用,字符串比较,系统调用,磁盘访问,网络上的信息传递等的代价有足够的理解。

Note(注意)

If you can only think of one implementation, you probably don't have something for which you can devise a stable interface. Maybe, it is just an implementation detail - not every piece of code needs a stable interface - but pause and consider. One question that can be useful is "what interface would be needed if this operation should be implemented using multiple threads? be vectorized?"

如果只能想到一种实现方式,你可能对如何设计稳定的接口没有什么想法。也许,它只是一个实现细节-不是所有的代码片段都需要稳定的接口-但是停下来想一想。一个有用的问题是:如果你的操作用多线程实现,需要什么样的接口?需要向量化么?

Note(注意)

This rule does not contradict the Don't optimize prematurely rule. It complements it encouraging developers enable later - appropriate and non-premature - optimization, if and where needed.

本规则和【Per.2 不要过早优化】并不矛盾。作为【Per.2 不要过早优化】的补充,本规则鼓励程序员为将来的优化做准备-适当而不是过早的优化,如果需要的话。

Enforcement(实施建议)

Tricky. Maybe looking for void* function arguments will find examples of interfaces that hinder later optimization.

不容易。也许寻找void* 函数参数可以发现将来接口优化的例子。

原文链接

https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#per7-design-to-enable-optimization

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-06-21,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 面向对象思考 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Per.7: Design to enable optimization
  • Reason(原因)
    • Example(示例)
    相关产品与服务
    容器服务
    腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档