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

架构在使用SSE/AVX Intrinisics时的影响

宰父俊彦
2023-03-14

我想知道编译器是如何处理内部函数的。

如果使用SSE2内部函数(使用代码),则包括

如果使用AVX2内部函数(使用代码),则包括

编译器如何处理内部函数
如果使用内部函数,是否有助于编译器理解循环中的依赖关系以更好地进行矢量化?

例如,这里发生了什么-https://godbolt.org/z/Y4J5OA(或https://godbolt.org/z/LZOJ2K)?
查看所有3个窗格。

我正在尝试用不同的CPU特性(SSE4和AVX2)构建相同功能的不同版本
我正在用SSE Intrinsics编写同一版本,用AVX Intrinsics编写同一版本
让我们假设它们是名称MyFunSSE()和MyFunAVX()。两者都在同一个文件中。

如何使编译器(MSVC、GCC和ICC应使用相同的方法)仅使用各自的函数构建它们中的每一个?

共有2个答案

顾池暝
2023-03-14

如果您在启用-mavx2的情况下编译代码,您的编译器将(通常)生成所谓的“VEX编码”指令。在_mm_loadu_ps的情况下,这将生成vmovup而不是movup,这几乎是等效的,除了后者只会修改目标寄存器的较低128位,而前者会将较低128位以上的所有内容归零。但是,它只会在至少支持AVX的机器上运行。[v]movup的详细信息在这里。

对于其他指令,如addps,AVX具有允许三个操作数的额外优势(即目标可以与两个源不同),这在某些情况下可以避免复制寄存器。例如。,

_mm_mul_ps(_mm_add_ps(a,b), _mm_sub_ps(a,b));

为SSE编译时需要寄存器副本(movaps),但为AVX编译时不需要:https://godbolt.org/z/YHN5OA

关于使用AVX内部函数但在没有AVX的情况下编译,编译器要么失败(如gcc/clang),要么自动生成相应的指令,然后在没有AVX支持的机器上失败(有关详细信息,请参阅@PeterCordes answer)。

附录:如果您想根据架构实现不同的功能(在编译时),您可以使用#ifdef__AVX__#if定义(__AVX__):https://godbolt.org/z/ZVAo-7

我认为,在同一个编译单元中实现它们很困难。最简单的解决方案是构建不同的共享库或甚至不同的二进制文件,并使用一个小二进制文件来检测可用的CPU功能并加载相应的库/二进制文件。我想在这个话题上有相关的问题。

陆翰学
2023-03-14

GCC和clang要求您启用所使用的所有扩展。否则,这是一个编译时错误,如错误:内联未能调用always\u inline错误:内联未能调用always\u inline“\uu m256d \u mm256\u mask\u loadu\u pd(\uu m256d,\uu mmask8,const void*)”:目标特定选项不匹配

使用-march=native-march=haswell或任何优于启用特定扩展的选项,因为这也设置了适当的调优选项。您不会忘记像mpopcnt这样有用的函数,它可以让std::bitset::count()内联popcnt指令,并使用BMI2shlx/shrx使所有变量计数移位更有效(1 uop vs.3)

MSVC和ICC没有,并且允许您使用内部函数来发出它们无法自动矢量化的指令。

如果使用AVX内部函数,则必须启用AVX。没有启用AVX的旧版MSVC并不总是在需要时自动使用vzeroupper,但这已经被修复了几年。尽管如此,如果您的整个程序都可以假定AVX支持,那么即使对于MSVC,也一定要告诉编译器。

对于支持GNU扩展(GCC、clang、ICC)的编译器,可以在编译单元中的特定函数上使用类似于\uu attribute\uu((target(“avx”))的东西。或者更好的是,\uuu attribute\uuu((target(“arch=haswell”)))也可以设置调优选项。(这也会启用AVX2和FMA,这可能是您不想要的。我不确定属性是否设置了)。看见https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html

\uuuu attribute\uu((target()))将阻止它们内联到具有其他目标选项的函数中,因此如果函数本身太小,请小心在它们将内联到的函数中使用此选项。在包含循环的函数上使用它,而不是在循环中调用的辅助函数。

另请参见https://gcc.gnu.org/wiki/FunctionMultiVersioning用于在相同函数名的多个定义上使用不同的目标选项,用于编译器支持的运行时调度。但我不认为有一种可移植(到MSVC)的方法可以做到这一点。

使用MSVC,您不需要任何东西,尽管就像我说的,我认为在没有-arch: AVX的情况下使用AVX内部函数通常是个坏主意,所以您最好将它们放在单独的文件中。但是对于AVX vs. AVX2 FMA,或者SSE2 vs. SSE4.2,没有任何东西都可以。

只需将AVX2\u函数定义为空字符串,而不是将AVX2\u属性定义为空字符串((目标(“AVX2,fma”))

#if defined(__GNUC__) && !defined(__INTEL_COMPILER)
// apparently ICC doesn't support target attributes, despite supporting GNU C
#define TARGET_HASWELL __attribute__((target("arch=haswell")))
#else
#define TARGET_HASWELL   // empty
 // maybe warn if __AVX__ isn't defined for functions where this is used?
 // if you need to make sure MSVC uses vzeroupper everywhere needed.
#endif


TARGET_HASWELL
void foo_avx(float *__restrict dst, float *__restrict src)
{
   for (size_t i = 0 ; i<1024 ; i++) {
       __m256 v = _mm256_loadu_ps(src);
       ...
       ...
   }
}

使用GCC和clang,宏扩展到__attribute__((目标))东西;使用MSVC和ICC则不然。

https://software.intel.com/en-us/cpp-compiler-developer-guide-and-reference-optimization-parameter记录要放在AVX函数之前的pragma,以确保在使用内部函数的函数中正确使用vzeroupper。

#pragma intel optimization_parameter target_arch=AVX

对于ICC,您可以这样定义TARGET\u AVX,并始终在函数前的一行中单独使用它,您可以在其中放置属性或pragma。如果ICC不希望在声明中使用宏,您可能还需要单独的宏来定义和声明函数。和一个宏,用于结束AVX函数块,如果您想在它们后面有非AVX函数。(对于非ICC编译器,这将是空的。)

 类似资料:
  • 既然介绍数据流向,首先第一步就是:写入的数据是如何变成 Elasticsearch 里可以被检索和聚合的索引内容的? 以单文件的静态层面看,每个全文索引都是一个词元的倒排索引,具体涉及到全文索引的通用知识,这里不单独介绍,有兴趣的读者可以阅读《Lucene in Action》等书籍详细了解。 动态更新的 Lucene 索引 以在线动态服务的层面看,要做到实时更新条件下数据的可用和可靠,就需要在倒

  • 本文向大家介绍java使用三层架构实现电影购票系统,包括了java使用三层架构实现电影购票系统的使用技巧和注意事项,需要的朋友参考一下 使用三层架构实现电影购票系统,分用户和管理员,用户功能:展示电影,查找电影(模糊查询),查看电影详情,查找场次,购买影票,订制座位,退订影票等功能,界面美观漂亮,逻辑严谨,附加电影评论功能,订票超过五张打0.9折的打折功能。管理员功能:影院的增删改查,场次的增删改

  • 问题内容: 我想在Django中使用postgreSQL模式,该怎么做? 问题答案: 我一直在使用: 过去没有意识到仅适用于只读操作。当你尝试添加新记录时,它将失败,因为该序列类似于“ schema.tablename” _column_id_seq。 到目前为止还可以。谢谢。

  • 下午好,我最近才开始与Kafka合作,我有一个关于制作人与模式的问题。 最初,我尝试在C#中构建一个没有模式的简单生产者。到目前为止,这是可行的,代码也在一个简短的版本中给出。 无模式生产者代码: 但是模式会给我带来问题(请参阅下一节)。 假设我给了一个消费者,比如说Python中的消费者,他使用以下方案来接收整数: 我现在想创建一个使用此方案并向Python消费者发送消息的C#生产者。根据该方案

  • 通过上节内容,我们知道了数据怎么进入 ES 并且如何才能让数据更快的被检索使用。其中用一句话概括了 Lucene 的设计思路就是”开新文件”。从另一个方面看,开新文件也会给服务器带来负载压力。因为默认每 1 秒,都会有一个新文件产生,每个文件都需要有文件句柄,内存,CPU 使用等各种资源。一天有 86400 秒,设想一下,每次请求要扫描一遍 86400 个文件,这个响应性能绝对好不了! 为了解决这

  • 我一直在遵循“Django by example”的教程,该教程介绍了Solr和Haystack,但遇到了一个问题。我已在settings.py中对已安装的_应用程序进行了必要的更改,并添加了以下内容: 然后我就跑 我得到这个错误 这是我的search_index.py文件 当我运行django shell时,我可以很好地导入haystack,但当我运行以下命令时: 进入第二行后,我得到了完全相同