问题标签 [loop-unrolling]
For questions regarding programming in ECMAScript (JavaScript/JS) and its various dialects/implementations (excluding ActionScript). Note JavaScript is NOT the same as Java! Please include all relevant tags on your question; e.g., [node.js], [jquery], [json], [reactjs], [angular], [ember.js], [vue.js], [typescript], [svelte], etc.
c++ - Unroll and Jam 对大循环的影响
当我编译我的应用程序时,性能比我预期的要差,我发现编译器正在报告如下所示的警告。
备注 #25461: 不完美的循环展开 - 被 2 卡住(前向量)
这是我的短代码。
索引变量 num 始终为 1024。为什么循环展开卡住不完美?另外,当我像下面这样修改代码时,不会发生 unroll-jamming!发生展开卡纸的条件是什么?
实际上,代码是大文件的一部分,所以我不能在这里写完整的代码。但下面是包含 func() 的代码。
fortran - fortran循环的simd向量长度和展开因子
我想用 SIMD 指令对下面的 fortran 进行矢量化
我使用了指令avx2。该程序由
然后我想VECTORLENGTH(n)
在SIMD
. 如果没有这样的子句或 n = 2、4,则该信息不会提供有关展开因子的信息
如果 n = 8, 16, vectorization support: unroll factor set to 2
.
我读过英特尔关于矢量化支持的文章:unroll factor set to xxxx所以我猜循环被展开为:
然后 2 X 进入向量寄存器, 2 W 进入另一个,做加法。但是 VECTORLENGTH 的值是如何起作用的呢?或者也许我真的不明白向量长度是什么意思。
而且由于我使用 avx2 指令,对于DOUBLE PRECISION
type X
,可以达到的最大长度是多少?
这是使用 SSE2、VL=8 的循环程序集的一部分,编译器告诉我展开因子是 2。但是它使用 4 个寄存器而不是 2 个。
c++ - 这种优化甚至重要吗?
此页面推荐“循环展开”作为优化:
可以通过减少迭代次数和复制循环体来减少循环开销。
例子:
在下面的代码片段中,循环体可以复制一次,迭代次数可以从 100 次减少到 50 次。
下面是循环展开后的代码片段。
使用 GCC 5.2,除非您使用,否则不会启用循环展开-funroll-loops
(在-O2
或中均未启用-O3
)。我已经检查了组件,看看是否有显着差异。
版本 1:
版本 2:
版本 2 产生更多的迭代。我错过了什么?
c - 展开循环并使用矢量化进行独立求和
对于以下循环,如果我告诉它使用关联数学,例如 with ,GCC 只会对循环进行矢量化-Ofast
。
这是与-Ofast -mavx
这清楚地表明循环已被矢量化。
但是这个循环也有一个依赖链。为了克服加法的延迟,我需要在 x86_64 上展开并至少进行 3 次部分求和(不包括需要展开 8 次的 Skylake 以及需要在 Haswell 和 Broadwell 上展开 10 次的 FMA 指令进行加法) . 据我了解,我可以使用-funroll-loops
.
这是与-Ofast -mavx -funroll-loops
.
GCC 确实展开了八次循环。但是,它不进行独立求和。它做了八个相关的和。这是没有意义的,也没有比不展开更好。
如何让 GCC 展开循环并进行独立的部分求和?
编辑:
即使没有 SSE,Clang 也会展开到四个独立的部分总和-funroll-loops
,但我不确定它的 AVX 代码是否同样有效。无论如何编译器都不应该需要-funroll-loops
,-Ofast
所以很高兴看到 Clang 至少对 SSE 这样做是正确的。
Clang 3.5.1 与-Ofast
.
ICC 13.0.1-O3
展开到两个独立的部分总和。ICC 显然只假设关联数学与-O3
.
c++ - 如何在运行时实现 base 2 循环展开以进行优化
考虑一些需要重复执行 1-1,000,000 次的代码,并且在编译时不知道重复次数。据我了解,考虑到大量循环,循环展开将是一个可以忽略不计的优化,并且只会优化到编译时指定的 max_unrolls。我想出的想法是实现一个二进制(或基数 2)部分循环展开器,它基本上重复执行 some_function 在运行时指定的次数。我想出了一些代码来演示这个想法,下面显示了一个浓缩版本。从可用性的角度来看,以下代码中使用的方法存在许多问题。
- 它需要编码人员手动复制出 base 2 unroll 基本上复制出 unroll 2^n-1 次。
- 对于需要使用此方法的每个新功能,也需要重新执行此操作。
我的问题是三方面的。首先,我是否遗漏了一些东西,编译器是否已经足够智能,可以自己做这件事?for
其次,什么是实现这一点的有效方法,以便在解决上述问题的同时将其与标准循环进行基准测试变得可行。第三,据您所知,有一个库已经实现了这一点。
请注意:我这样做纯粹是为了好玩,但没有专业知识知道这是否有效。我已经对代码进行了测试,但只发现了非常小的改进,但是我相信我无法手动展开足够远的距离来进行公平的比较。我也知道这种方法有可能创建大量的二进制大小,但是我相信这将是一个值得的时间内存权衡。此外,如果您发布任何程序集,我可能需要一年左右的时间才能理解它。
cuda - 确定 CUDA 中#pragma unroll N 的最佳值
我了解如何#pragma unroll
工作,但如果我有以下示例:
我想确定LIMIT
上面内核中的最佳值,它将以x
线程y
数和块数启动。LIMIT
可以是从2
到的任何地方1<<20
。由于 100 万对于变量来说似乎是一个非常大的数字(展开的 100 万个循环会导致寄存器压力,我不确定编译器是否会这样做),如果有的话,什么是“公平”数字?我如何确定这个限制?
c++ - 编译器(gcc)可以保证 c++ 循环吗?
我必须进行以下 AVX 操作:
这可以这样重写:
这在 gcc 4.9.1 中编译,尽管_mm256_shuffle_ps
只接受立即整数值作为第三个参数。这意味着,thati
被接受为立即数,因此意味着循环已展开。
所以我很好奇:这是编译器保证的,还是当优化标志被修改或 gcc 版本改变时会导致编译错误?使用其他编译器(msvc、icc、clang...)怎么样?
c++ - SSE Intrinsics 和循环展开
我正在尝试优化一些循环并且我已经做到了,但我想知道我是否只是部分正确地完成了它。例如说我有这个循环:
将其展开 3 倍,得到:
现在是 SSE 翻译等价物:
或者是:
我对代码部分有点困惑:
这是做什么的?是否只是做额外的部分,例如,如果循环不能被您选择展开它的因素整除?谢谢你。
c - 如何要求 GCC 完全展开这个循环(即剥离这个循环)?
有没有办法指示 GCC(我使用的是 4.8.4)完全展开底部函数中的循环while
,即剥离这个循环?循环的迭代次数在编译时已知:58。
让我先解释一下我尝试过的方法。
通过检查 GAS 输出:
使用了 12 个寄存器 XMM0 - XMM11。如果我将标志传递-funroll-loops
给 gcc:
循环仅展开两次。我检查了 GCC 优化选项。GCC 表示它-funroll-loops
也会打开-frename-registers
,因此当 GCC 展开循环时,它对寄存器分配的优先选择是使用“剩余”寄存器。但是 XMM12 - XMM15 只剩下 4 个,所以 GCC 最多只能展开 2 次。如果有 48 个而不是 16 个 XMM 寄存器可用,GCC 将毫无问题地展开 while 循环 4 次。
然而我又做了一个实验。我首先手动展开了两次 while 循环,获得了一个函数GEPDOT_2
。那么两者之间完全没有区别
和
由于GEPDOT_2
已用完所有寄存器,因此不执行展开。
GCC 确实注册了重命名以避免引入潜在的错误依赖。但我确信我的 ; 不会有这样的潜力GEPDOT
;即使有,也不重要。我自己尝试展开循环,展开 4 次比展开 2 次快,比不展开快。当然,我可以手动展开更多次,但这很乏味。GCC 可以为我做这件事吗?谢谢。
更新 1
感谢@user3386109 的评论,我想稍微扩展一下这个问题。@user3386109 提出了一个非常好的问题。实际上,当有这么多并行指令要调度时,我确实对编译器优化寄存器分配的能力有些怀疑。
我个人认为一种可靠的方法是首先在asm内联汇编中对循环体(HPC 的关键)进行编码,然后根据需要多次复制它。今年早些时候我有一个不受欢迎的帖子:内联汇编。代码有点不同,因为循环迭代的次数 j 是一个函数参数,因此在编译时是未知的。在那种情况下,我无法完全展开循环,所以我只复制了两次汇编代码,并将循环转换为标签并跳转。事实证明,我编写的程序集的结果性能比编译器生成的程序集高约 5%,这可能表明编译器未能以我们预期的最佳方式分配寄存器。
我曾经(现在仍然)是汇编编码方面的婴儿,因此这是一个很好的案例研究,可以让我学习一点 x86 汇编。但从长远来看,我并不倾向于编写GEPDOT
大量用于汇编的代码。主要有以下三个原因:
- asm内联汇编因不可移植而受到批评。虽然我不明白为什么。也许是因为不同的机器有不同的寄存器被破坏?
- 编译器也越来越好。所以我还是更喜欢算法优化和更好的 C 编码习惯来帮助编译器生成好的输出;
- 最后一个原因更重要。迭代次数可能并不总是 58。我正在开发一个高性能的矩阵分解子程序。对于缓存阻塞因子
nb
,迭代次数为nb-2
。我不会nb
像在之前的帖子中那样将其作为函数参数。这是一个机器特定的参数,将被定义为一个宏。因此迭代次数在编译时是已知的,但可能因机器而异。猜猜我在为各种nb
. 因此,如果有一种方法可以简单地指示编译器剥离循环,那就太好了。
如果您还可以分享一些生产高性能但可移植的库的经验,我将不胜感激。
assembly - 使用循环展开计算正数、负数和零数的最有效方法
假设我有以下指令,只需检查一个数字是否为正数(负数或零),如果它是正数,则将 1 添加到我们的计数器(我们不在乎数字是负数还是零)。我可以通过一个简单的循环展开来做到这一点:
我的问题是,如果我还想检查是否为零或负数,如何获得相同的有效结构。在这种情况下,我将有三个计数器(一个用于 pos,一个用于 neg,一个用于 0) 一种低效的方法是这样的。我正在尝试制作与前一个示例相同的结构(我们将正数存储在 中%rax
,负数存储在 中%rbx
,零存储在 中%rcx
):