如何看待C++中的各种 for 循环?
How to think about varieties of for loops in C++?
我的IDE(CLion(建议将for循环替换为foreach,其中循环的元素是char类型的值的地址(选项2(。我对以下内容感到好奇:
-
阐明选项 2 中发生的事情的最佳方式是什么?我们是否以明文形式循环访问每个字符的内存位置?
-
选项 2 和 3 有何不同?
-
选项 3 是否在每次迭代时为新字符分配内存?
选项 1
void Cipher(std::string &plaintext, int key) {
for (int i = 0; i < plaintext.length(); i++) {...}
}
选项 2
void Cipher(std::string &plaintext, int key) {
for (char &letter : plaintext) {...}
}
选项 3
void Cipher(std::string &plaintext, int key) {
for (char letter : plaintext) {...}
}
1 点
阐明选项 2 中发生的事情的最佳方式是什么?我们是否以明文形式循环访问每个字符的内存位置?
它正在迭代plaintext
中的字符。这意味着它将遍历所有内存位置,但每隔一个循环也会循环。letter
是对plaintext
中字符的引用,另一个名称。不要将引用视为内存位置或指针(尽管引用可以在后台使用指针实现(。把它想象成letter
和plaintext[0]
是一回事,假设plaintext[0]
存在。没有letter
,只有一个指代plaintext[0]
的标识符。当循环完成第一次迭代并进入第二次迭代(如果确实如此(时,将出现一个新的letter
(不能引用引用不同的对象(,并且它将plaintext[1]
。
要点 2
选项 2 和 3 有何不同?
如选项 1 中的第1 点所述,在选项 2 中,letter
是plaintext
中的字符之一。在选项 3 中,letter
是一个新变量,它是plaintext
中字符之一的副本。
要点 3
选项 3 是否在每次迭代时为新字符分配内存?
是的,为循环的每次迭代分配一个新的letter
。但是,该字符是一个自动变量,根本不占用内存中的任何空间。它可能位于 CPU 寄存器中。它可能位于堆栈中,存储已经分配,并且簿记只是更新,表明内存现在正在使用中。它可能漂浮在小精灵的灰尘中。无论发生什么,一旦优化编译器完成,您甚至可能无法检测到它。
应不惜一切代价避免使用选项1!!这里的问题是方法的输入(纯文本(是引用,因此字符串存在于方法范围之外。这意味着编译器无法确定该变量的范围,因此无法确定执行优化是否安全(并非总是如此,但在此处(。
https://godbolt.org/z/EBtVp7
在这里实现一个愚蠢的方法(每个字符只加 12(。您会注意到第一个版本的 ASM 看起来"不错"。它非常简单,而且非常小,很棒。但是,如果您将 1 切换到 0 并与第二种方法进行比较,您会注意到第二种方法在生成的 asm 量方面出现了爆炸,但是当您仔细观察时,情况并没有那么糟糕。
看看第一个代码片段,我们可以在内部循环的第一行看到这一点:
mov rcx, qword ptr [rdi]
这有点糟糕。它实际上是在每次迭代时读取字符串"begin"指针(假设是另一个线程*可能*调整字符串的大小,因此更改字符串长度(。
但是,如果您查看第二种方法,它会使用 vpaddb 指令(使用 YMM 寄存器(生成一些展开的循环。这意味着它一次处理 32 个字符(与第一种方法不同,该方法一次只能处理 1 个字符(。
如果你想开始使选项1接近选项2的性能,你需要做一些严峻的事情,比如:
void Cipher(std::string &plaintext, int key) {
if(!plaintext.empty())
{
char* ptr = &plaintext[0];
for (int i = 0, length = plaintext.length(); i < length; i++) {
ptr[i] += 12;
}
}
}
这个可怕的变化现在意味着编译器可以看到ptr和长度变量在函数范围内没有变化,因此它现在能够对代码进行矢量化。(选项 2 和 3 仍然更有效!
选项 3 不会在每次迭代时分配 char(它会将一个 char 加载到通用寄存器中,或将一组字符加载到 YMM 寄存器中(。在这种情况下,性能差异是没有意义的。如果要修改字符串,请使用选项 2,如果字符串是只读的,请使用选项 3。
实现相同目标的较旧替代方案是 std::for_each,但这不再比基于范围的 for 循环更可取。
- 如何在C++中从两个单独的for循环中添加两个数组
- 为什么我的for循环不能正确获取argv
- 在基于范围的for循环中使用结构化绑定声明
- 通过for循环使用用户输入填充列表
- 使用for循环检查数组中的重复项
- 在for循环中使用auto vs decltype(vec.size())来处理字符串的向量
- 为什么 const std::p air<K,V>& 在 std::map 上基于范围的 for 循环不起作用?
- 正在使用for循环创建QScatterSerie
- Python中的for循环与C++有何不同
- 在更改for循环的第三部分后,未使用for循环结果
- 在 for 循环中查找问题时遇到困难
- 嵌套for循环C++的问题(初学者)
- 如何用for循环在c++中生成单词三角形
- 如何在for循环中包含两个索引值的测试条件
- 带有多个独立参数的C++For循环
- 为什么我的程序在for循环中k=0时返回垃圾值
- 如何通过替换顺序代码的while循环来添加OpenMP for循环
- C++-For循环未执行
- 基于范围的 for 循环:迭代使用一个元素扩展的向量
- C++ 无法在字符数组中使用 for 循环打印字母模式