- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
我看到有人在使用 -msse -msse2 -mfpmath=sse
默认情况下标记为希望这将提高性能。我知道当在 C 代码中使用特殊的 vector 类型时,SSE 就会参与进来。但是这些标志对常规 C 代码有什么影响吗?编译器是否使用 SSE 来优化常规 C 代码?
最佳答案
是的,如果您使用完全优化进行编译,现代编译器会使用 SSE2 自动矢量化。 clang 甚至在 -O2 时启用它,gcc 在 -O3 时启用它。
即使在 -O1 或 -Os 处,编译器也会使用 SIMD 加载/存储指令来复制或初始化结构体或比整数寄存器更宽的其他对象。这不算是自动矢量化;它更像是他们针对固定大小的小块的默认内置 memset/memcpy 策略的一部分。但它确实利用并需要支持 SIMD 指令。
SSE2 是 x86-64 的基线/非可选,因此编译器在面向 x86-64 时始终可以使用 SSE1/SSE2 指令 .以后的指令集(SSE4、AVX、AVX2、AVX512 和非 SIMD 扩展,如 BMI2、popcnt 等)必须手动启用,以告诉编译器可以生成不能在旧 CPU 上运行的代码。或者让它生成多个版本的代码并在运行时进行选择,但这会产生额外的开销,并且仅对较大的函数才值得。
-msse -msse2 -mfpmath=sse
已经是 x86-64 的默认设置 ,但不适用于 32 位 i386。一些 32 位调用约定在 x87 寄存器中返回 FP 值,因此使用 SSE/SSE2 进行计算可能很不方便,然后必须存储/重新加载结果才能在 x87 中获取它 st(0)
.与 -mfpmath=sse
,更聪明的编译器可能仍然使用 x87 进行计算以生成 FP 返回值。
在 32 位 x86 上,-msse2
默认情况下可能未启用,这取决于您的编译器的配置方式。如果你使用 32 位是因为你的目标 CPU 太旧以至于无法运行 64 位代码,你可能需要确保它被禁用,或者只是 -msse
.
为您正在编译的 CPU 调整二进制文件的最佳方法是 -O3 -march=native -mfpmath=sse
,并使用链接时间优化 + 配置文件引导优化 . (gcc -fprofile-generate
/在一些测试数据上运行/gcc -fprofile-use
)。
使用 -march=native
如果编译器确实选择使用新指令,则生成可能无法在早期 CPU 上运行的二进制文件。配置文件引导的优化对 gcc 非常有帮助:没有它它永远不会展开循环。但是对于 PGO,它知道哪些循环经常运行/进行大量迭代,即哪些循环是“热的”并且值得花费更多的代码大小。链接时优化允许跨文件内联/常量传播。如果您的 C++ 具有许多实际上并未在头文件中定义的小函数,这将非常有用。
见 How to remove "noise" from GCC/clang assembly output? 有关查看编译器输出并理解它的更多信息。
下面是一些具体的例子on the Godbolt compiler explorer适用于 x86-64 . Godbolt 也有适用于其他几种架构的 gcc,您可以使用 clang 添加 -target mips
或者其他什么,因此您还可以使用正确的编译器选项来查看 ARM NEON 的自动矢量化以启用它。您可以使用 -m32
使用 x86-64 编译器获得 32 位代码生成。
int sumint(int *arr) {
int sum = 0;
for (int i=0 ; i<2048 ; i++){
sum += arr[i];
}
return sum;
}
gcc8.1 -O3
的内循环(没有
-march=haswell
或任何启用 AVX/AVX2 的东西):
.L2: # do {
movdqu xmm2, XMMWORD PTR [rdi] # load 16 bytes
add rdi, 16
paddd xmm0, xmm2 # packed add of 4 x 32-bit integers
cmp rax, rdi
jne .L2 # } while(p != endp)
# then horizontal add and extract a single 32-bit sum
-ffast-math
, 编译器不能重新排序 FP 操作,所以
float
等效的不要自动矢量化(参见 Godbolt 链接:你得到标量
addss
)。 (OpenMP 可以在每个循环的基础上启用它,或使用
-ffast-math
)。
// clang won't contract this into an FMA without -ffast-math :/
// but gcc will (if you compile with -march=haswell)
void scale_array(float *arr) {
for (int i=0 ; i<2048 ; i++){
arr[i] = arr[i] * 2.1f + 1.234f;
}
}
# load constants: xmm2 = {2.1, 2.1, 2.1, 2.1}
# xmm1 = (1.23, 1.23, 1.23, 1.23}
.L9: # gcc8.1 -O3 # do {
movups xmm0, XMMWORD PTR [rdi] # load unaligned packed floats
add rdi, 16
mulps xmm0, xmm2 # multiply Packed Single-precision
addps xmm0, xmm1 # add Packed Single-precision
movups XMMWORD PTR [rdi-16], xmm0 # store back to the array
cmp rax, rdi
jne .L9 # }while(p != endp)
2.0f
结果使用
addps
加倍,将 Haswell/Broadwell 的吞吐量减少 2 倍!因为在 SKL 之前,FP add 只运行在一个执行端口上,但是有两个 FMA 单元可以运行乘法。 SKL 放弃了加法器,并以与 mul 和 FMA 相同的每时钟 2 吞吐量和延迟运行相加。 (
http://agner.org/optimize/ ,并在
the x86 tag wiki 中查看其他性能链接。)
-march=haswell
让编译器对 scale + add 使用单个 FMA。 (但是,除非您使用
-ffast-math
,否则 clang 不会将表达式收缩为 FMA。IIRC 有一个选项可以启用 FP 收缩,而无需其他激进操作。)
关于c - 编译器是否将 SSE 指令用于常规 C 代码?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50786263/
我有一个 if 语句,如下所示 if (not(fullpath.lower().endswith(".pdf")) or not (fullpath.lower().endswith(tup
然而,在 PHP 中,可以: only appears if $foo is true. only appears if $foo is false. 在 Javascript 中,能否在一个脚
XML有很多好处。它既是机器可读的,也是人类可读的,它具有标准化的格式,并且用途广泛。 它也有一些缺点。它是冗长的,不是传输大量数据的非常有效的方法。 XML最有用的方面之一是模式语言。使用模式,您可
由于长期使用 SQL2000,我并没有真正深入了解公用表表达式。 我给出的答案here (#4025380)和 here (#4018793)违背了潮流,因为他们没有使用 CTE。 我很欣赏它们对于递
我有一个应用程序: void deleteObj(id){ MyObj obj = getObjById(id); if (obj == null) { throw n
我的代码如下。可能我以类似的方式多次使用它,即简单地说,我正在以这种方式管理 session 和事务: List users= null; try{ sess
在开发J2EE Web应用程序时,我通常会按以下方式组织我的包结构 com.jameselsey.. 控制器-控制器/操作转到此处 服务-事务服务类,由控制器调用 域-应用程序使用的我的域类/对象 D
这更多是出于好奇而不是任何重要问题,但我只是想知道 memmove 中的以下片段文档: Copying takes place as if an intermediate buffer were us
路径压缩涉及将根指定为路径上每个节点的新父节点——这可能会降低根的等级,并可能降低路径上所有节点的等级。有办法解决这个问题吗?有必要处理这个吗?或者,也许可以将等级视为树高的上限而不是确切的高度? 谢
我有两个类,A 和 B。A 是 B 的父类,我有一个函数接收指向 A 类型类的指针,检查它是否也是 B 类型,如果是将调用另一个函数,该函数接受一个指向类型 B 的类的指针。当函数调用另一个函数时,我
有没有办法让 valgrind 使用多个处理器? 我正在使用 valgrind 的 callgrind 进行一些瓶颈分析,并注意到我的应用程序中的资源使用行为与在 valgrind/callgrind
假设我们要使用 ReaderT [(a,b)]超过 Maybe monad,然后我们想在列表中进行查找。 现在,一个简单且不常见的方法是: 第一种可能性 find a = ReaderT (looku
我的代码似乎有问题。我需要说的是: if ( $('html').attr('lang').val() == 'fr-FR' ) { // do this } else { // do
根据this文章(2018 年 4 月)AKS 在可用性集中运行时能够跨故障域智能放置 Pod,但尚不考虑更新域。很快就会使用更新域将 Pod 放入 AKS 中吗? 最佳答案 当您设置集群时,它已经自
course | section | type comart2 : bsit201 : lec comart2 :
我正在开发自己的 SDK,而这又依赖于某些第 3 方 SDK。例如 - OkHttp。 我应该将 OkHttp 添加到我的 build.gradle 中,还是让我的 SDK 用户包含它?在这种情况下,
随着 Rust 越来越充实,我对它的兴趣开始激起。我喜欢它支持代数数据类型,尤其是那些匹配的事实,但是对其他功能习语有什么想法吗? 例如标准库中是否有标准过滤器/映射/归约函数的集合,更重要的是,您能
关闭。这个问题不符合Stack Overflow guidelines .它目前不接受答案。 这个问题似乎与 help center 中定义的范围内的编程无关。 . 关闭 9 年前。 Improve
我一直在研究 PHP 中的对象。我见过的所有示例甚至在它们自己的对象上都使用了对象构造函数。 PHP 会强制您这样做吗?如果是,为什么? 例如: firstname = $firstname;
...比关联数组? 关联数组会占用更多内存吗? $arr = array(1, 1, 1); $arr[10] = 1; $arr[] = 1; // <- index is 11; does the
我是一名优秀的程序员,十分优秀!