当前位置: 首页 > 知识库问答 >
问题:

为什么这个循环每次迭代需要1.32个循环

毛越
2023-03-14

考虑这个简单的C++函数来计算数组的前缀和:

void prefix_sum(const uint32_t* input, uint32_t* output, size_t size) {
    uint32_t total = 0;
    for (size_t i = 0; i < size; i++) {
        total += input[i];
        output[i] = total;
    }
}
.L5:
        add     ecx, DWORD PTR [rdi+rax*4]
        mov     DWORD PTR [rsi+rax*4], ecx
        add     rax, 1
        cmp     rdx, rax
        jne     .L5

它是4个融合的UOP1,这个CPU可以支持4个融合的OPs/周期。

有通过ECXRAX携带的依赖链,每个都是一个循环,但是这些AddUOP可以到4个ALU端口中的任何一个,所以似乎不太可能冲突。融合的CMP需要转到p6,这是一个更令人担忧的问题,但我只测量到p6的1.1 UOPS/迭代。这将解释每次迭代1.1个循环,但不是1.4个循环。如果我将循环展开2倍,端口压力会低得多:对所有p0156来说不到0.7uops,但性能仍然出乎意料地慢,每次迭代1.3个循环。

每个迭代有一个存储,但我们可以每个循环做一个存储。

有趣的是,我尝试了Ithermal性能预测器,它几乎完全正确:估计了1.314个循环,而我的测量值是1.32个。

1我通过uops_subsced.任何计数器确认了宏和微融合融合,该计数器在融合域中计数,并在此循环的每次迭代中读取4.0个融合的uops。

共有1个答案

郑俊美
2023-03-14

我只是玩弄了一下Ithermal性能预测器的说明,我可能已经发现了问题。试用

add     ecx, DWORD PTR [rdi]
mov     DWORD PTR [rsi], ecx
add     rax, 1
cmp     rdx, rax

每次迭代给出惊人的1.131个循环。在每次迭代中添加0的交叉检查(再次给出1.3个循环)消除了存储/加载瓶颈的可能性。这最后提示了寻址模式的一个问题。

(编者注:这是一个有趣的实验数据,与我在Agner Fog博客上发布的帖子相匹配,下面的猜测误解了这一点。更简单的寻址模式可以加快它的速度,即使没有解层。)

>

  • AD融合限制:https://www.agner.org/optimize/blog/read.php?i=415852

    解层:https://easyperf.net/blog/2018/02/15/MicroFusion-in-Intel-CPUs#解层-示例-1

  •  类似资料:
    • 我一直在读一本面向初学者的书,“第一头HTML5编程”,其中有这样一段代码: 目前,如果我调用,它将返回下一次显示是在下午5:00。我将循环条件更改为“I<=movie.showtimes.length;”但它仍然只运行一次,并且只显示下午5点。循环只迭代一次,即使我重写了这个函数: 不是应该跑两次吗?

    • 问题内容: 这是我的内容: 在Jenkins中使用Pipeline插件执行作业时,仅打印列表中的第一项。 有人可以向我解释这种奇怪的行为吗?是虫子吗?还是只是我不了解Groovy语法? 编辑 :预期的作品: 问题答案: 此处接受的答案指出这是一个已知的错误,并且使用了对我不起作用的解决方法,因此,我将提供我最近发现的更新。 尽管有了JENKINS-26481的解决方案(在撰写本文时,它还算是最近的

    • 我发现这样的php代码: 我希望这个循环会执行4次,因为$I变成了对$的引用(对吗?)。然而,循环只执行一次,并输出: a=10,i=10 我不明白为什么它会这样工作。有什么想法吗?

    • 我在玩node.js,我发现这个简单的程序运行得非常慢,我甚至没有等看它在3分钟后花了多长时间。 我使用不同的值进行了实验,发现1-112050需要3秒,而1-112051需要一分钟。这种突然的下降很奇怪。python中的相同程序或等效的shell脚本“seq 1 112051”在合理的时间内(0-2秒)运行。 请注意,此节点。js应用程序运行速度更快: 谁能给我解释一下为什么是node。js的行

    • 有一个循环执行蛮力算法来计算5*3,而不使用算术运算符。 我只需要加5 3次,这样它就需要O(3),如果输入是x*y,它就是O(y)。但是,在一本书中,它说它需要O(2^n),其中n是输入中的位数。我不明白为什么它用O(2^n)来表示它O(y)。这是显示时间复杂度的更好方法吗?。你能解释一下吗? 我没有要求其他算法来计算这个。

    • 我如何通过for循环的每次迭代声明一个新变量? 例如: 循环完成后,我想要4个变量,分别名为、、和,每个变量分别设置为、、和(当我在上面的代码中将设置为时,我实际上是将其设置为,因为这是在整个特定迭代中的值