P.9: Don't waste time or space(不要浪费时间和空间)

Reason(原因)

This is C++.

我们在用C++。

译者注:之所以选择C++而不是其他语言,就是希望使用最小的内存,获得更快的性能。没有道理不考虑时间和空间。





Note(注意)

Time and space that you spend well to achieve a goal (e.g., speed of development, resource safety, or simplification of testing) is not wasted. "Another benefit of striving for efficiency is that the process forces you to understand the problem in more depth." - Alex Stepanov

为达成目的而正确利用时间和空间(为了提高开发速度,资源安全,简化测试等)不是浪费。“努力提高效率的另一个好处是这个过程会促使你在更深层次上理解问题。”-亚历山大 斯特潘诺夫(STL之父)

Example, bad(反面示例)

 

struct X {    char ch;    int i;    string s;    char ch2;
X& operator=(const X& a); X(const X&);};
X waste(const char* p){ if (!p) throw Nullptr_error{}; int n = strlen(p); auto buf = new char[n]; if (!buf) throw Allocation_error{}; for (int i = 0; i < n; ++i) buf[i] = p[i]; // ... manipulate buffer ... X x; = 'a'; x.s = string(n); // give x.s space for *p for (gsl::index i = 0; i < x.s.size(); ++i) x.s[i] = buf[i]; // copy buf into x.s delete[] buf; return x;}
void driver(){ X x = waste("Typical argument"); // ...}


 


Yes, this is a caricature, but we have seen every individual mistake in production code, and worse. Note that the layout of ​​X​​​ guarantees that at least 6 bytes (and most likely more) are wasted. The spurious definition of copy operations disables move semantics so that the return operation is slow (please note that the Return Value Optimization, RVO, is not guaranteed here). The use of ​​new​​​ and ​​delete​​​ for ​​buf​​​ is redundant; if we really needed a local string, we should use a local ​​string​​. There are several more performance bugs and gratuitous complication.

是的,这个例子有点夸张,但是我在产品级代码中看到过所有特别的错误,甚至更糟。注意X的内存结构中至少有6个字节(很有可能更多)是多余的。这个关于copy操作的假想定义禁止了移动语法,因此返回操作会慢(请注意这里没有保证RVO,即返回值优化)。buffer的new和delete操作是多余的;如果确实需要局部字符串,我们应该使用局部的string对象。这里还有几个性能方面的问题和无谓的并发症。

 

译者注:返回值优化(RVO)的目的在于函数在返回对象时减少必要的拷贝和构造动作,具体可以参照一下连接:

​https://www.ibm.com/developerworks/community/blogs/5894415f-be62-4bc0-81c5-3956e82276f3/entry/RVO_V_S_std_move?lang=en​



Example, bad(反面示例)

 

void lower(zstring s){    for (int i = 0; i < strlen(s); ++i) s[i] = tolower(s[i]);}


 


This is actually an example from production code. We can see that in our condition we have ​​i < strlen(s)​​​. This expression will be evaluated on every iteration of the loop, which means that ​​strlen​​​ must walk through string every loop to discover its length. While the string contents are changing, it's assumed that ​​toLower​​ will not affect the length of the string, so it's better to cache the length outside the loop and not incur that cost each iteration.

这是一段来源于产品代码的实际的例子。我们可以看到i<strlen(s)的循环条件。这个表达式会在每次循环迭代时被计算,这意味着strlen必须每次迭代都遍历一次字符串以获取长度。当字符串的内容被修改时,我们可以认为toLower操作不会影响字符串长度,因此较好的做法是在循环外缓存字符串的长度而不必每次迭代都付出那么大的代码。

Note(注意)

An individual example of waste is rarely significant, and where it is significant, it is typically easily eliminated by an expert. However, waste spread liberally across a code base can easily be significant and experts are not always as available as we would like. The aim of this rule (and the more specific rules that support it) is to eliminate most waste related to the use of C++ before it happens. After that, we can look at waste related to algorithms and requirements, but that is beyond the scope of these guidelines.

关于浪费的单独的例子很少会造成显著影响,即使造成显著影响,通常也会很容易地被专家程序员排除。然而浪费会很容易地通过代码传播并显著化,而且专家程序员也不是只要我们需要就能有的。这个规则(和支持它的更具体的规则)的目的是在发生之前排除和C++用法相关的大部分浪费。

Enforcement(实施建议)

Many more specific rules aim at the overall goals of simplicity and elimination of gratuitous waste.

很多更具体的规则的总目标是简单性和无谓浪费的排除。

  • Flag an unused return value from a user-defined non-defaulted postfix​​operator++​​​ or​​operator--​​ function. Prefer using the prefix form instead. (Note: "User-defined non-defaulted" is intended to reduce noise. Review this enforcement if it's still too noisy in practice.)

识别用户定义的非缺省后缀++操作符或者--操作符函数中的无用的返回值。推荐使用prefix格式。(注意:“用户定义的非缺省”目的是为排除干扰。如果在实践中仍然存在太多干扰,重新考虑本建议)

 

觉得本文有帮助,欢迎点赞并分享给更多的朋友!

阅读更多更新文章,请关注微信公众号【面向对象思考】