C ++ 17为标准库添加并行扩展(例如std::sort(std::execution::par_unseq, arr, arr + 1000)
,这将允许使用多个线程和向量指令进行排序)。
我注意到微软的实验性实现提到VC ++编译器缺乏对矢量化over here的支持,令我感到惊讶 - 我认为现代C ++编译器能够推断出循环的可矢量化,但是显然VC ++编译器/优化器即使明确告知这样做也无法生成SIMD代码。似乎缺乏自动矢量化支持与Quora上this 2011 question的答案相矛盾,这表明编译器将在可能的情况下进行矢量化。
也许,编译器只会对非常明显的情况进行矢量化,例如std::array<int, 4>
,而且不会更多,因此C ++ 17的显式并行化会很有用。
因此我的问题是:当没有明确告知这样做时,当前的编译器会自动向量化我的代码吗? (为了使这个问题更加具体,让我们将其缩小到支持SIMD的Intel x86 CPU,以及最新版本的GCC,Clang,MSVC和ICC。)
作为扩展:其他语言的编译器能否做更好的自动矢量化(可能是由于语言设计)(因此C ++标准委员会认为它对于显式(C ++ 17风格)矢量化是必要的)?
答案 0 :(得分:7)
自动发现SIMD样式矢量化的最佳编译器(当被告知它可以为适当的指令集生成操作码时)当然是英特尔编译器(根据实际需要,可根据实际CPU生成动态调度代码)紧随其后的是GCC和Clang,以及MSVC最后(你的四个)。
我意识到这一点也许并不令人惊讶 - 英特尔在帮助开发人员利用他们为其产品添加的最新功能方面确实有一定的兴趣。
我正在与英特尔密切合作,虽然他们热衷于展示他们的编译器如何发现自动矢量化,但他们也非常正确地指出使用他们的编译器还允许你使用pragma simd结构来进一步显示编译器的假设可以或不可以(从纯粹的语法层面不清楚),因此允许编译器进一步矢量化代码而不诉诸内在函数。
我认为,这个问题指出了编译器(对于C ++或其他语言)将完成所有向量化工作...如果你有简单的向量处理循环(例如,将向量中的所有元素相乘)然后是的,您可以预期4个编译器中的3个会发现它。
但是对于更复杂的代码,可以产生的矢量化增益不是来自简单的循环展开和组合迭代,而是来自实际使用不同的或经过调整的算法,并且即使不是不可能,编译器也会很难完成单独。然而,如果您了解矢量化如何应用于算法,并且您可以构建代码以允许编译器查看机会这样做,可能使用pragma simd构造或OpenMP,那么您可以获得所需的结果。
当代码对底层CPU和内存总线有一定的机械同情时,就会出现矢量化 - 如果你有,那么我认为英特尔编译器将是你最好的选择。没有它,改变编译器可能没什么区别。
我可以推荐Matt Godbolt的Compiler Explorer作为实际测试的方法 - 将你的c ++代码放在那里,看看不同的编译器实际生成了什么?非常方便......它不包括旧版本的MSVC(我认为它目前支持VC ++ 2017及更高版本),但会向您展示ICC,GCC,Clang和其他版本的不同版本的代码...