Mar*_* Ba 16 c++ performance loops
在C++中是否有一个优雅的解决方案,以防止必须声明复杂的对象变量,这些变量仅出于效率原因而仅在循环外的循环中使用?
一位同事提出了一个有趣的观点.我们的代码策略,其中说明(释义):始终对变量使用最小范围,并在第一次初始化时声明变量.
编码指南示例:
// [A] DO THIS
void f() {
...
for (int i=0; i!=n; ++i) {
const double x = calculate_x(i);
set_squares(i, x*x);
}
...
}
// [B] DON'T do this:
void f() {
int i;
int n;
double x;
...
for (i=0; i!=n; ++i) {
x = calculate_x(i);
set_squares(i, x*x);
}
...
}
Run Code Online (Sandbox Code Playgroud)
这一切都很好,在你从原始类型转移到对象之前,这肯定没有错.(对于某种界面)
例:
// [C]
void fs() {
...
for (int i=0; i!=n; ++i) {
string s;
get_text(i, s); // void get_text(int, string&);
to_lower(s);
set_lower_text(i, s);
}
...
}
Run Code Online (Sandbox Code Playgroud)
这里,字符串s将被破坏,它的内存释放每个循环周期,然后每个循环该get_text函数将不得不为s缓冲区重新分配内存.
写作显然更有效:
// [D]
string s;
for (int i=0; i!=n; ++i) {
get_text(i, s); // void get_text(int, string&);
to_lower(s);
set_lower_text(i, s);
}
Run Code Online (Sandbox Code Playgroud)
因为现在s缓冲区中分配的内存将在循环运行之间保留,我们很可能会节省分配.
免责声明: 请注意:由于这是循环,我们正在谈论内存分配,我不认为通常考虑这个问题是不成熟的优化.当然有些情况和循环,开销无关紧要; 但是n,开发人员最初期望的那种唠叨倾向会更大,并且代码有一种唠叨的倾向,即在性能确实重要的环境中运行.
无论如何,现在对于"通用"循环结构来说,更有效的方法是违反代码局部性并将复杂对象声明到位,"以防万一".这让我感到很不安.
请注意,我考虑这样写:
// [E]
void fs() {
...
{
string s;
for (int i=0; i!=n; ++i) {
get_text(i, s); // void get_text(int, string&);
to_lower(s);
set_lower_text(i, s);
}
}
...
}
Run Code Online (Sandbox Code Playgroud)
是无解的可读性遭受甚至更多!
进一步思考,get_text函数的接口无论如何都是非惯用的,因为无论如何out params是如此,并且"good"接口将按值返回:
// [F]
for (int i=0; i!=n; ++i) {
string s = get_text(i); // string get_text(int);
to_lower(s);
set_lower_text(i, s);
}
Run Code Online (Sandbox Code Playgroud)
在这里,我们不会为内存分配支付双倍的费用,因为它极有可能s通过RVO从返回值构造,因此对于[F],我们支付与 [C]中相同的分配开销.然而,与 [C]情况不同,我们无法优化此接口变体.
因此,底线似乎是使用最小范围(可能)会损害性能并使用干净的界面我至少认为按值返回比使用out-ref-param的东西更清洁会阻止优化机会 - 至少在一般情况下.
该问题是没有这么多,一个人必须要放弃,有时为了效率干净的代码,问题是,尽快启动离散事件找到这样的特殊情况下,整个编码指南(见[A],[B])失去权力.
现在的问题是:见第一段
我要么:
for使用保存计数器/迭代器和临时对象的多元素对象在循环范围中声明本地对象。std::pair<int,std::string>尽管专门的容器可以减少语法噪音,但这将是一种选择。(在很多情况下,out 参数会比 RVO 样式更快)