我这个问题已经很久了,但是却不知道在哪里看。如果某个操作多次编写,编译器会简化它还是运行完全相同的操作并获得完全相同的答案?
例如,在下文(i%3)*10中,多次重复类似c的伪代码。
for(int i=0; i<100; i++) {
array[(i%3)*10] = someFunction((i%3)*10);
int otherVar = (i%3)*10 + array[(i%3)*10];
int lastVar = (i%3)*10 - otherVar;
anotherFunction(lastVar);
}
Run Code Online (Sandbox Code Playgroud)
我了解变量在视觉上会更好,但会更快吗?是(i%3)*10计算每循环5次?
在某些情况下,我不知道使用变量还是仅保留原始操作更快。
编辑:在胜利10上使用gcc(MinGW.org GCC-8.2.0-3)8.2.0
我已经将我的大部分SIMD代码转换为GCC的向量扩展.但是,我没有找到一个很好的广播解决方案,如下所示
__m256 areg0 = _mm256_broadcast_ss(&a[i]);
Run Code Online (Sandbox Code Playgroud)
我想要做
__m256 argeg0 = a[i];
Run Code Online (Sandbox Code Playgroud)
如果你通过使用SSE常数看到我在Mutiplying向量的答案,我设法让广播与另一个SIMD寄存器一起工作.以下作品:
__m256 x,y;
y = x + 3.14159f; // broadcast x + 3.14159
y = 3.14159f*x; // broadcast 3.14159*x
Run Code Online (Sandbox Code Playgroud)
但这不起作用:
__m256 x;
x = 3.14159f; //should broadcast 3.14159 but does not work
Run Code Online (Sandbox Code Playgroud)
我怎样才能用GCC做到这一点?
我如何编写一个可移植的GNU C内置向量版本,它不依赖于x86 set1内在函数?
typedef uint16_t v8su __attribute__((vector_size(16)));
v8su set1_u16_x86(uint16_t scalar) {
return (v8su)_mm_set1_epi16(scalar); // cast needed for gcc
}
Run Code Online (Sandbox Code Playgroud)
当然必须有一个更好的方式
v8su set1_u16(uint16_t s) {
return (v8su){s,s,s,s, s,s,s,s};
}
Run Code Online (Sandbox Code Playgroud)
我不想写一个用于广播单个字节的AVX2版本!
对于你想要分配给变量而不是仅仅使用二元运算符的操作数(这与gcc一起工作,见下文),即使只对gcc-only或clang-only这一部分的答案也会很有趣.
如果我想使用广播标量作为二元运算符的一个操作数,则可以使用gcc(如手册中所述),但不能使用clang:
v8su vecdiv10(v8su v) { return v / 10; } // doesn't compile with clang
Run Code Online (Sandbox Code Playgroud)
有了clang,如果我只针对x86并且只使用本机向量语法来让编译器为我生成模数乘法逆常数和指令,我可以写:
v8su vecdiv_set1(v8su v) {
return v / (v8su)_mm_set1_epi16(10); // gcc needs the cast
}
Run Code Online (Sandbox Code Playgroud)
但是如果我加宽向量(to _mm256_set1_epi16),我必须改变内在,而不是通过改变到vector_size(32)一个地方(对于不需要改组的纯垂直SIMD )将整个代码转换为AVX2 .它也会破坏本机向量的部分目的,因为它不会为ARM或任何非x86目标编译.
丑陋的演员阵容是必需的,因为与clang不同,gcc不v8us {aka __vector(8) short …
我在Rust中使用了一些算法(尽管这个语言对我的问题并不重要).考虑一下代码:
#[no_mangle]
pub fn test(x: f32) -> f32 {
let m = 0.;
x + m
}
fn main() {
test(2.);
}
Run Code Online (Sandbox Code Playgroud)
它生成以下LLVM IR和相应的x86_64 asm(已启用优化):
;; LLVM IR
define float @test(float %x) unnamed_addr #0 {
start:
%0 = fadd float %x, 0.000000e+00
ret float %0
}
;; x86_64
; test:
xorps xmm1, xmm1
addss xmm0, xmm1
ret
Run Code Online (Sandbox Code Playgroud)
如果我更改let m = 0.;为let m = -0.;浮点,则添加优化:
;; LLVM IR
define float @test(float returned %x) unnamed_addr #0 {
start: …Run Code Online (Sandbox Code Playgroud)