Per.7:设计要为优化做准备
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++)标准:
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.
你什么时候有过内存排序的需求?事实上,我们对通常保存在容器中的元素排序。快速排序调用丢掉了特别有用信息(例如元素类型),要求用户重复提供已知的信息(例如元素大小),要求用户书写额外的代码(例如双精度数比较函数)。这个实现会增加程序员的工作,易错,使编译器无法获得可用于优化的信息。
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)
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加概念,我们可以做得更好
// 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结构仍然有弱点:它隐含要求元素类型定义了小于(<)运算符。
// 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
过早的优化被认为是万恶之源,但是这不应该成为轻视性能的理由。考虑一个设计如何适应变化永远不嫌早,改善性能也是最普通的提高。努力建立一套可以自然产生有效,可维护,可优化代码的习惯。通常情况下,当你编写一个可以复用的函数时:
Consider(考虑):
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)就足够了,但是好的接口会向调用者提供需要的信息。因此,标准库也提供了
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依然没有返回可以满足所有使用者需求的足够的信息。因此标准库也提供了
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返回了定义第一个匹配元素和最后一个匹配元素的下一个位置的迭代器对。
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