我使用一些使用某些容器来存储数据的类;有多维容器的类。这些类重载operator ()
以索引数据。我在循环中使用了很多这样的对象,并希望对它们进行矢量化。海湾合作委员会无法直接对其进行矢量化;它说“在基本块中找不到SLP机会”并且驳回了矢量化
我将如何进行矢量化代码?
我还没有检查过其他编译器,因为我希望这可以被少数使用的着名编译器进行矢量化。
答案 0 :(得分:2)
首先,我同意评论说如果你打算成功地对你的循环进行矢量化,你必须“非常接近地管理你的记忆”。如果不知道这一点 - 请参阅本答案末尾的脚注,以获得关于记忆对齐的简短而肤浅的介绍。
然而,即使你的记忆很好地对齐,也有可能让你退缩。 Georg Hager和Gerhard Wellein是受人尊敬的着作“科学家和工程师高性能计算简介”的作者,他明确表示C ++运算符重载可能会阻止循环向量化
用他们自己的话说:
“(....)STL可以通过以下方式定义此运算符(改编自GNU ISO C ++库源代码):
const T& operator[](size_t __n) const{ return *(this->_M_impl._M_start + __n); }
虽然这看起来很简单,可以有效地内联,但是当前的编译器拒绝了 将SIMD矢量化应用于上面的求和循环。单层抽象,在这种情况下是重载索引运算符,因此可以防止创建 最佳循环代码。“
一位好朋友让我相信,对于stl容器来说,这实际上并不正确,因为编译器可以消除与operator[]
相关联的间接层。但是,您似乎编写了自己的容器,因此必须检查编译器是否可以消除与您自己的operator()
关联的间接层!一个好的交叉检查是为自己提供一种直接处理容器所包含的底层数组的方法(意思是:编写类似于std::vector.data()
的成员函数,并在循环中使用C指针作为“迭代器”)。
关于内存对齐的脚注:
问题:假设您要向量化c[i] = a[i] + b[i]
。
第一个事实:size(double)
= 8个字节= 64位。
第二个事实:有一个汇编指令在存储器中读取2个双精度数并将它们置于128位寄存器=>使用一个汇编指令,您可以阅读2个双打=>他们可以阅读a[0]
和a[1]
,然后b[0]
和b[1]
!
第三个事实:当您在寄存器上应用指令时,您可以同时生成两个64位double
的和。
问题是,只有当a[0]
和a[1]
位于16的倍数的内存地址中时,程序集才能同时读取a[0]
和b[0]
(如果他们不是,他可以测试a[1]
和b[1]
是否对齐等等。这就是为什么内存可能成为阻止矢量化的问题。要解决此问题,您必须编写容器分配器,以确保容器的第一个元素将写入16的倍数的内存地址。
更新:This answer提供了有关如何编码对齐内存的分配器的详细说明。
更新2:另一个有用的answer,用于学习如何编码分配器
的替代方法