- android - 多次调用 OnPrimaryClipChangedListener
- android - 无法更新 RecyclerView 中的 TextView 字段
- android.database.CursorIndexOutOfBoundsException : Index 0 requested, 光标大小为 0
- android - 使用 AppCompat 时,我们是否需要明确指定其 UI 组件(Spinner、EditText)颜色
我最近正在学习 Herb Sutter 的“Exceptional C++”,我对他在第 6 项 - 临时对象中给出的特定建议深表怀疑。
他提出在以下代码中查找不必要的临时对象:
string FindAddr(list<Employee> emps, string name)
{
for (list<Employee>::iterator i = emps.begin(); i != emps.end(); i++)
{
if( *i == name )
{
return i->addr;
}
}
return "";
}
作为示例之一,他建议在循环之前预先计算 emps.end()
的值,因为每次迭代都会创建一个临时对象:
For most containers (including list), calling end() returns a temporary object that must be constructed and destroyed. Because the value will not change, recomputing (and reconstructing and redestroying) it on every loop iteration is both needlessly inefficient and unaesthetic. The value should be computed only once, stored in a local object, and reused.
他建议用以下内容替换:
list<Employee>::const_iterator end(emps.end());
for (list<Employee>::const_iterator i = emps.begin(); i != end; ++i)
对我来说,这是不必要的并发症。即使用紧凑的 auto
替换丑陋的类型声明,他仍然得到两行代码而不是一行。更重要的是,他在外部作用域中有这个 end
变量。
我确信现代编译器无论如何都会优化这段代码,因为我实际上在这里使用了 const_iterator
并且很容易检查循环内容是否以某种方式访问容器。编译器在过去 13 年里变得更聪明了,对吧?
无论如何,在大多数情况下,我更喜欢带有 i != emps.end()
的第一个版本,因为我不太担心性能。但我想确定地知道,这是否是一种我可以依靠编译器来优化的构造?
更新
感谢您就如何改进这段无用的代码提出建议。请注意,我的问题是关于编译器,而不是编程技术。目前唯一相关的答案来自NPE和 Ellioh .
最佳答案
UPD:如果我没有记错的话,您所说的这本书已于 1999 年出版。那是 14 年前的事了,而在现代编程中 14 年已经很长了。许多在 1999 年还不错和可靠的建议,现在可能已经完全过时了。虽然我的回答是关于单一编译器和单一平台,但还有一个更普遍的想法。
关心额外的变量、重用普通方法的返回值和旧 C++ 的类似技巧是向 1990 年代的 C++ 倒退一步。像 end()
这样的普通方法应该很好地内联,内联的结果应该作为调用它的代码的一部分进行优化。 99% 的情况根本不需要手动操作,例如创建 end
变量。只有在以下情况下才应该做这样的事情:
我查看了 64 位 g++ 生成的内容:
gcc version 4.6.3 20120918 (prerelease) (Ubuntu/Linaro 4.6.3-10ubuntu1)
一开始我觉得优化一下应该没问题,两个版本应该没什么区别。但看起来事情很奇怪:您认为非最佳的版本实际上更好。我认为,道德是:没有理由尝试比编译器更聪明。让我们看看这两个版本。
#include <list>
using namespace std;
int main() {
list<char> l;
l.push_back('a');
for(list<char>::iterator i=l.begin(); i != l.end(); i++)
;
return 0;
}
int main1() {
list<char> l;
l.push_back('a');
list<char>::iterator e=l.end();
for(list<char>::iterator i=l.begin(); i != e; i++)
;
return 0;
}
然后我们应该优化编译(我使用 64 位 g++
,你可以试试你的编译器)并反汇编 main
和 main1
:
对于 main
:
(gdb) disas main
Dump of assembler code for function main():
0x0000000000400650 <+0>: push %rbx
0x0000000000400651 <+1>: mov $0x18,%edi
0x0000000000400656 <+6>: sub $0x20,%rsp
0x000000000040065a <+10>: lea 0x10(%rsp),%rbx
0x000000000040065f <+15>: mov %rbx,0x10(%rsp)
0x0000000000400664 <+20>: mov %rbx,0x18(%rsp)
0x0000000000400669 <+25>: callq 0x400630 <_Znwm@plt>
0x000000000040066e <+30>: cmp $0xfffffffffffffff0,%rax
0x0000000000400672 <+34>: je 0x400678 <main()+40>
0x0000000000400674 <+36>: movb $0x61,0x10(%rax)
0x0000000000400678 <+40>: mov %rax,%rdi
0x000000000040067b <+43>: mov %rbx,%rsi
0x000000000040067e <+46>: callq 0x400610 <_ZNSt8__detail15_List_node_base7_M_hookEPS0_@plt>
0x0000000000400683 <+51>: mov 0x10(%rsp),%rax
0x0000000000400688 <+56>: cmp %rbx,%rax
0x000000000040068b <+59>: je 0x400698 <main()+72>
0x000000000040068d <+61>: nopl (%rax)
0x0000000000400690 <+64>: mov (%rax),%rax
0x0000000000400693 <+67>: cmp %rbx,%rax
0x0000000000400696 <+70>: jne 0x400690 <main()+64>
0x0000000000400698 <+72>: mov %rbx,%rdi
0x000000000040069b <+75>: callq 0x400840 <std::list<char, std::allocator<char> >::~list()>
0x00000000004006a0 <+80>: add $0x20,%rsp
0x00000000004006a4 <+84>: xor %eax,%eax
0x00000000004006a6 <+86>: pop %rbx
0x00000000004006a7 <+87>: retq
查看位于 0x0000000000400683-0x000000000040068b 的命令。那是循环体,它似乎是完美优化的:
0x0000000000400690 <+64>: mov (%rax),%rax
0x0000000000400693 <+67>: cmp %rbx,%rax
0x0000000000400696 <+70>: jne 0x400690 <main()+64>
对于 main1
:
(gdb) disas main1
Dump of assembler code for function main1():
0x00000000004007b0 <+0>: push %rbp
0x00000000004007b1 <+1>: mov $0x18,%edi
0x00000000004007b6 <+6>: push %rbx
0x00000000004007b7 <+7>: sub $0x18,%rsp
0x00000000004007bb <+11>: mov %rsp,%rbx
0x00000000004007be <+14>: mov %rsp,(%rsp)
0x00000000004007c2 <+18>: mov %rsp,0x8(%rsp)
0x00000000004007c7 <+23>: callq 0x400630 <_Znwm@plt>
0x00000000004007cc <+28>: cmp $0xfffffffffffffff0,%rax
0x00000000004007d0 <+32>: je 0x4007d6 <main1()+38>
0x00000000004007d2 <+34>: movb $0x61,0x10(%rax)
0x00000000004007d6 <+38>: mov %rax,%rdi
0x00000000004007d9 <+41>: mov %rsp,%rsi
0x00000000004007dc <+44>: callq 0x400610 <_ZNSt8__detail15_List_node_base7_M_hookEPS0_@plt>
0x00000000004007e1 <+49>: mov (%rsp),%rdi
0x00000000004007e5 <+53>: cmp %rbx,%rdi
0x00000000004007e8 <+56>: je 0x400818 <main1()+104>
0x00000000004007ea <+58>: mov %rdi,%rax
0x00000000004007ed <+61>: nopl (%rax)
0x00000000004007f0 <+64>: mov (%rax),%rax
0x00000000004007f3 <+67>: cmp %rbx,%rax
0x00000000004007f6 <+70>: jne 0x4007f0 <main1()+64>
0x00000000004007f8 <+72>: mov (%rdi),%rbp
0x00000000004007fb <+75>: callq 0x4005f0 <_ZdlPv@plt>
0x0000000000400800 <+80>: cmp %rbx,%rbp
0x0000000000400803 <+83>: je 0x400818 <main1()+104>
0x0000000000400805 <+85>: nopl (%rax)
0x0000000000400808 <+88>: mov %rbp,%rdi
0x000000000040080b <+91>: mov (%rdi),%rbp
0x000000000040080e <+94>: callq 0x4005f0 <_ZdlPv@plt>
0x0000000000400813 <+99>: cmp %rbx,%rbp
0x0000000000400816 <+102>: jne 0x400808 <main1()+88>
0x0000000000400818 <+104>: add $0x18,%rsp
0x000000000040081c <+108>: xor %eax,%eax
0x000000000040081e <+110>: pop %rbx
0x000000000040081f <+111>: pop %rbp
0x0000000000400820 <+112>: retq
循环的代码类似,就是:
0x00000000004007f0 <+64>: mov (%rax),%rax
0x00000000004007f3 <+67>: cmp %rbx,%rax
0x00000000004007f6 <+70>: jne 0x4007f0 <main1()+64>
但是循环周围还有很多额外的东西。显然,额外的代码使事情变得更糟。
关于c++ - pIter != cont.end() 在 for 循环中的表现,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38882625/
我正在用 C++ 开发一个程序,我必须实现一个 cron。由于不同的原因,这个 cron 应该每小时和每 24 小时执行一次。我的第一个想法是创建一个独立的 pthread 并在每次 1h 内休眠。这
我需要向同一场景几何添加多个体素(立方体等于),但每个体素具有不同的纹理。 我的体素超过 500 个,导致性能出现严重错误。 这是我的代码: texture = crearTextura(voxel.
对于 MySQL 数据库,我有 2 个场景,我不确定该选择哪一个,并且对于一些表我也遇到了同样的困境。 我正在制作一个仅供成员(member)访问的网络应用程序。每个成员都有自己的交易、费用和“列表”
我想知道一个简单的事情: 当设置一个被所有 child 继承的样式时,是否建议最具体? Structure: html > body > parent_content > wrapper > p 我想
很难说出这里要问什么。这个问题模棱两可、含糊不清、不完整、过于宽泛或夸夸其谈,无法以目前的形式得到合理的回答。如需帮助澄清此问题以便重新打开,visit the help center . 关闭 1
这些天我正在阅读有关 JPA 的内容。我了解到可以在 JPQL 中使用 explicit 或 implicit JOIN。 显式加入 em.createQuery(“SELECT b.title, p
我有一种情况需要连接几个字符串以形成一个类的 id。基本上,我只是在列表中循环以获取对象的 ToString 值,然后将它们连接起来。 foreach (MyObject o in myList)
我正在检查我的游戏在拖尾效果下的性能会降低多少。但我注意到每秒的操作次数更多了。这怎么可能? 这是怎么回事... context.fillRect(0, 0, 500, 500); // cl
如果我可以选择使用全局变量或传递变量,哪个选项在速度和内存使用方面更好? // global variable function func(){ global $var; echo $var;
我有一个类似这样的表“tbl”:ID bigint(20) - 主键,自增字段1字段2字段3 该表有 60 万多行。 查询:SELECT * from tbl ORDER by ID LIMIT 60
谁能告诉我,我如何比较 TSP 最优和启发式算法?我已经实现了 TSP,但不知道如何比较它们。事实上,我怎样才能找到 TSP 的最优成本?有什么方法或猜测吗? 谢谢 最佳答案 用众所周知的基准实例检查
我有一个 NSTextStorage里面有长文本(比如一本书有 500 页,当前字体在设备上超过 9000 页)。我以这种方式为 textcontainer 分发此文本: let textStorag
我有一个根据邮政编码搜索项目的应用程序。 在搜索邮政编码时,我返回了来自该城市/社区的所有产品(通过解析邮政编码完成)。 我现在需要根据与原始邮政编码的距离对这些产品进行分类。 我将纬度/经度存储在数
我有许多进程(大约100到1000个进程),每个进程都必须向其他进程(例如大约10个)发送一些数据。 (通常,但不一定总是这样,如果A发送给B,B也发送给A。)每个进程都知道必须从哪个进程接收多少数据
我知道无状态组件使用起来更舒服(在特定场景下),但是既然你不能使用shouldComponentUpdate,这是否意味着组件将在每次props更改时重新渲染?我的问题是,使用带有智能 shouldC
我正在研究 Google Pagespeed 的加速页面加载时间指南列表。其中之一是缩小 CSS 和 JS 文件。 由于这些文件经常更改,我正在考虑使用 PHP 脚本根据请求(来自浏览器)即时缩小此脚
我正在尝试从下表构建 SQL 查询(示例): Example of table with name "performances" 这是带有运动表现的表格。我想从这个表中选择每个学科和一组一个或多个类别
假设我们有一个字符串 var "sA",我想检查字符串 "123"是否在 sA 的末尾。 什么更好,为什么: if(sA.length() > 2) sA.substr(sA.length()-3)
关于受这篇文章启发的可参数化查询 LINQ group by property as a parameter我获得了一个很好的参数化查询,但在性能上有一个缺点。 public static void
| 和| 之间有什么主要区别吗?和 + 从长远来看会影响代码的性能吗?或者都是 O(1)?我正在使用的代码是这样的: uint64_t dostuff(uint64_t a,uint64_t b){
我是一名优秀的程序员,十分优秀!