了解 For 循环的每个元素的循环数

Understanding Cycles Per Element for For Loops

我(模糊地)理解每条指令的周期数 (CPI) 和每周期指令数 (IPC) 的含义。 CPI 是执行程序所需的时钟周期数除以程序执行的指令数运行。另一方面,IPC 是 运行 执行程序时执行的指令数除以执行程序所需的时钟周期数。

但是,当与循环关联时,我无法理解每个元素的循环数是什么意思。

例如,在下面的代码中,

void combine4(vec_ptr v, data_t *dest) {
    long i;
    long length = vec_length(v);
    data_t *d = get_vec_start(v);
    data_t t = IDENT;
    for (i = 0; i < length; i++)
       t = t OP d[i];
    *dest = t; 
}

我们可以通过改变for循环的方式来进行多重优化

一种方法称为循环展开

/* Combine 2 elements at a time */
for (i = 0; i < limit; i += 2) {
     x = (x OP d[i]) OP d[i + 1];
}
/* Finish any remaining elements */
for (; i < length; i++) {
     x = x OP d[i];
}

为了进一步改进它,我们可以在数组访问周围加上括号。

x = x OP (d[i] OP d[i + 1]);

我相信我们开始计算下一个循环的信息是因为我们没有相关数据。术语 CPE 如何应用于此优化? CPE会下降吗?因为 运行 通过所有元素需要更少的周期?

Cycles Per Element 是指当您迭代数组、向量或其他元素容器时,循环每次迭代的 CPU 周期数的术语。对于 combine4(),您将测量 运行 整个 for 循环所需的 CPU 循环总数,并将其除以 length 得到CPE.

如今的编译器非常擅长优化代码,例如,它们可能会自动展开或向量化循环。他们还可以改变操作的执行顺序,利用指令时序和其他微架构细节的知识来产生最佳的指令序列,只要它能证明这种重新排序不会改变最终结果。因此,手动更改可能无法达到您预期的效果。除了对您的更改进行基准测试外,您还应该查看编译器的汇编输出以查看它生成的机器代码类型。当然不要忘记启用编译器优化。 https://godbolt.org/.

是一个很好的基于 Web 的工具来执行此操作

(您的代码示例来自教科书《计算机系统:程序员的视角》。)

这里每个元素的循环数是一个更高级别的指标。与测量 CPI 或 IPC 不同,对示例循环重要的实际单位是向量的元素。因此,在 运行 跨越数百或数千个元素并测量整个执行所需的循环的循环中,我们可以绘制结果测量值并计算斜率(即每个元素的循环数)。

括号的移动改变了操作的关联,从而使两个数据元素的OP独立。如果有足够的硬件资源来执行额外的独立操作,这可以提高循环的 CPE。

结论应该是,即使对代码进行简单的更改也很难确定是否会缩短执行时间,因为它依赖于系统的所有部分。通常,程序员应该依靠编译器及其出色的优化来获得良好的性能,而无需求助于细粒度调整。