Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/performance/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 当字符指针指向的字符串变长时,取消引用字符指针的速度会变慢。为什么?_C++_Performance_Pointers - Fatal编程技术网

C++ 当字符指针指向的字符串变长时,取消引用字符指针的速度会变慢。为什么?

C++ 当字符指针指向的字符串变长时,取消引用字符指针的速度会变慢。为什么?,c++,performance,pointers,C++,Performance,Pointers,我遇到了一个奇怪的问题:我有以下代码: int matches = 0; for (int str_id = 0; str_id < STR_COUNT; str_id++) { if (test(strings1[str_id], strings2[str_id]) == 0) matches++; } 然后,运行时间不取决于存储在strings1和strings2中的字符串的长度。另一方面,如果我使用 int test(char* a, char* b) {

我遇到了一个奇怪的问题:我有以下代码:

int matches = 0;
for (int str_id = 0; str_id < STR_COUNT; str_id++) {
    if (test(strings1[str_id], strings2[str_id]) == 0)
        matches++;
}
然后,运行时间不取决于存储在strings1和strings2中的字符串的长度。另一方面,如果我使用

int test(char* a, char* b) {
    return (*a != *b)
}
然后,运行时间随
strings1
strings2
中存储的字符串长度线性增加

为什么会发生这种情况


编辑:此处问题的完整示例:

,因为在第一种情况下,只要
strings1!=strings2
,则该条件永远不会为真。优化编译器可以推断整个循环永远不会有任何可观察的副作用,因此它可以将其优化为遗忘

假设
strings[str_id]
等于
strings+str_id*sizeof(*strings)
;为了简单起见,让我们假设
sizeof
等于1(我们可以在不损失通用性的情况下这样做)。然后,您的情况变为:

if (test(strings1 + str_id, strings2 + str_id) == 0)
如果编译器能够内联
测试
,那么使用第一个版本的
测试
,代码将变为

if ((strings1 + str_id != strings2 + str_id) == 0)
或(连续简化但等效的形式)

既然
strings1!=strings2
(几乎可以肯定是这种情况),而且由于编译器可以假定
strings1
strings2
不会被外部原因修改,因此它可以跳过整个循环,而不做任何事。什么都不做是固定的时间


对于第二个版本的
test
,除了在每次迭代中实际执行循环并取消对指针的引用之外,无法知道条件是否为真,因此运行时间变为线性。

您可以看到数据局部性的影响

在仅比较指针的情况下,该操作仅访问两个向量中的内存。向量连续地存储它们的元素,因此每次访问内存的位置都非常接近在上一次迭代中访问的位置。这是一个非常好的位置,缓存向您微笑

在取消引用指针的情况下,将向混合添加额外的内存访问,因此缓存有更多的工作要做,并且效果在很大程度上取决于实现

从数据推断,这些字符串似乎在内存中打包在一起,因此从一个字符串的开头到下一个字符串的开头的距离取决于字符串的长度。短串比长串紧密地排列在一起


特别是,您可以将几个非常短的字符串打包到单个缓存线中。当这种情况发生时,单个缓存线可以为多次迭代的内存访问提供服务。随着字符串变长,单个缓存线中容纳的字符串会越来越少,因此缓存效率会降低。最后,字符串足够长,每个字符串占用一个单独的缓存线,而缓存没有任何好处。

Hi Jon,谢谢。我理解你的答案,但我仍然不明白为什么另一个函数(*a!=*b)也不是常数时间,因为
stru COUNT
是一个常数。@Clément:因为在
*(a+x)=*(b+x)
中,你不能从两边移除
x
,并用
a+x==b+x
尽可能称之为相等。您必须取消对指针的引用并比较结果。@Clément:正如Raymond所说,我们可能在这里看到缓存效果。运行时间与字符数绝对不是线性关系;在~60个字符之后,我几乎看不到任何有意义的增加,而在60个字符时,它仅是1的3倍左右。@Clément在Xcode中详细研究了这一点(是的,我必须集成一个不同的性能计数器系统)并看到您得到的输出类型后,我可以(a)撤销我上面关于STPA的理论,它将被删除,以及(b)与Jon就运行时影响达成一致。我在我的Mac电脑上看不到性能的变化,这显然是一个不同于您的Win32 box的运行时。@Clément:不要低估自己。这是一个令人敬畏的观察,其本质是微妙的。这个问题本身,虽然一开始有点难,但很有趣,因为它本身就是+1。顺便说一句,雷蒙德关于缓存的猜测很精明。一个词:局部性。更长的字符串会将数据进一步分散,从而降低缓存效率。谢谢Raymond!你能补充一下吗?
if ((strings1 + str_id != strings2 + str_id) == 0)
if (strings1 + str_id == strings2 + str_id)

if (strings1 == strings2)