这是一个带有枚举定义和main函数的简单 C 文件:
enum days {MON, TUE, WED, THU};
int main() {
enum days d;
d = WED;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
它转换为以下 LLVM IR:
define dso_local i32 @main() #0 {
%1 = alloca i32, align 4
%2 = alloca i32, align 4
store i32 0, i32* %1, align 4
store i32 2, i32* %2, align 4
ret i32 0
}
Run Code Online (Sandbox Code Playgroud)
%2显然是d变量,它被分配了 2。%1直接返回零对应什么?
在Rust中编写将运行数百万次的整数函数(想想像素处理)时,使用性能最高的操作很有用 - 类似于C/C++.
虽然参考手册解释了行为的变化,但并不总是清楚哪种方法的性能高于标准(参见注释1)整数算术运算.我假设wrapping_add编译成等同于C的补充.
在标准操作(加/减/乘/模/除/移位/位操作...)中,哪些操作具有更高性能的替代方法,默认情况下不使用?
注意:
a + b,i / k或c % e...等等我写了这个非常简单的 Rust 函数:
fn iterate(nums: &Box<[i32]>) -> i32 {
let mut total = 0;
let len = nums.len();
for i in 0..len {
if nums[i] > 0 {
total += nums[i];
} else {
total -= nums[i];
}
}
total
}
Run Code Online (Sandbox Code Playgroud)
我编写了一个基本的基准测试,它使用一个有序数组和一个无序数组调用该方法:
fn criterion_benchmark(c: &mut Criterion) {
const SIZE: i32 = 1024 * 1024;
let mut group = c.benchmark_group("Branch Prediction");
// setup benchmarking for an ordered array
let mut ordered_nums: Vec<i32> = vec![];
for i in 0..SIZE {
ordered_nums.push(i - …Run Code Online (Sandbox Code Playgroud) performance compiler-optimization rust branch-prediction llvm-codegen
我正在研究一个改变舍入模式的Rust箱子(+ inf,-inf,最近或截断).
更改舍入模式的函数使用内联汇编编写:
fn upward() {
let cw: u32 = 0;
unsafe {
asm!("stmxcsr $0;
mov $0, %eax;
or $$0x4000, %eax;
mov %eax, $0;
ldmxcsr $0;"
: "=*m"(&cw)
: "*m"(&cw)
: "{eax}"
);
}
}
Run Code Online (Sandbox Code Playgroud)
当我在调试模式下编译代码时,它按预期工作,当向正无穷大舍入时,我获得0.3333333333337三分之一,但是当我在释放模式下编译时,无论我设置什么舍入模式,我都得到相同的结果.我想这种行为是由于LLVM后端的优化所致.
如果我知道哪个LLVM通过负责此优化,我可以禁用它们,因为我目前没有看到任何其他解决方法.
Rust 在调试和发布模式下以不同的方式处理有符号整数溢出。当它发生时,Rust 在调试模式下会发生恐慌,而在发布模式下默默地执行二进制补码包装。
据我所知,C/C++ 将有符号整数溢出视为未定义行为,部分原因是:
因此,如果 Rust 编译器确实在有符号整数方面执行与 C/C++ 编译器相同类型的优化,那么为什么The Rustonomicon指出:
无论如何,Safe Rust 不会导致未定义行为。
或者即使 Rust 编译器不执行这样的优化,Rust 程序员仍然不希望看到有符号整数环绕。不能称为“未定义行为”吗?
我目前正在玩LLVM,我正在尝试编写一些优化器来熟悉opt和clang.我写了一个test.c文件,如下所示:
int foo(int aa, int bb, int cc){
int sum = aa + bb;
return sum/cc;
}
Run Code Online (Sandbox Code Playgroud)
我编译了源代码并生成了2个.ll文件,一个未经优化,一个用mem2reg优化器传递:
clang -emit-llvm -O0 -c test.c -o test.bc
llvm-dis test.bc
opt -mem2reg -S test.ll -o test-mem2reg.ll
Run Code Online (Sandbox Code Playgroud)
两个.ll文件都给了我以下输出:
ModuleID = 'test.bc'
source_filename = "test.c"
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"
; Function Attrs: noinline nounwind optnone uwtable
define i32 @foo(i32 %aa, i32 %bb, i32 %cc) #0 {
entry:
%aa.addr = alloca i32, align 4
%bb.addr = alloca i32, align 4
%cc.addr = …Run Code Online (Sandbox Code Playgroud) 我在llvm clang Apple LLVM 8.0.0版(clang-800.0.42.1)上反汇编代码:
int main() {
float a=0.151234;
float b=0.2;
float c=a+b;
printf("%f", c);
}
Run Code Online (Sandbox Code Playgroud)
我编译时没有-O规范,但我也试过-O0(给出相同)和-O2(实际上计算值并存储它预先计算)
产生的反汇编如下(我删除了不相关的部分)
-> 0x100000f30 <+0>: pushq %rbp
0x100000f31 <+1>: movq %rsp, %rbp
0x100000f34 <+4>: subq $0x10, %rsp
0x100000f38 <+8>: leaq 0x6d(%rip), %rdi
0x100000f3f <+15>: movss 0x5d(%rip), %xmm0
0x100000f47 <+23>: movss 0x59(%rip), %xmm1
0x100000f4f <+31>: movss %xmm1, -0x4(%rbp)
0x100000f54 <+36>: movss %xmm0, -0x8(%rbp)
0x100000f59 <+41>: movss -0x4(%rbp), %xmm0
0x100000f5e <+46>: addss -0x8(%rbp), %xmm0
0x100000f63 <+51>: movss %xmm0, -0xc(%rbp)
...
Run Code Online (Sandbox Code Playgroud)
显然它正在做以下事情:
为什么 clang 变成fabs(double)了vandps而不是vandpd(像 GCC 一样)?
编译器资源管理器的示例:
#include <math.h>
double float_abs(double x) {
return fabs(x);
}
Run Code Online (Sandbox Code Playgroud)
-std=gnu++11 -Wall -O3 -march=znver3.LCPI0_0:
.quad 0x7fffffffffffffff # double NaN
.quad 0x7fffffffffffffff # double NaN
float_abs(double): # @float_abs(double)
vandps xmm0, xmm0, xmmword ptr [rip + .LCPI0_0]
ret
Run Code Online (Sandbox Code Playgroud)
-std=gnu++11 -Wall -O3 -march=znver3float_abs(double):
vandpd xmm0, xmm0, XMMWORD PTR .LC0[rip]
ret
.LC0:
.long -1
.long 2147483647
.long 0
.long 0
Run Code Online (Sandbox Code Playgroud)
(讽刺的是,GCC 使用vandpd但将常量定义为 32 位.long块(有趣的是上半部分为零),而 …
代码很幼稚:
use std::time;
fn main() {
const NUM_LOOP: u64 = std::u64::MAX;
let mut sum = 0u64;
let now = time::Instant::now();
for i in 0..NUM_LOOP {
sum += i;
}
let d = now.elapsed();
println!("{}", sum);
println!("loop: {}.{:09}s", d.as_secs(), d.subsec_nanos());
}
Run Code Online (Sandbox Code Playgroud)
输出是:
$ ./test.rs.out
9223372036854775809
loop: 0.000000060s
$ ./test.rs.out
9223372036854775809
loop: 0.000000052s
$ ./test.rs.out
9223372036854775809
loop: 0.000000045s
$ ./test.rs.out
9223372036854775809
loop: 0.000000041s
$ ./test.rs.out
9223372036854775809
loop: 0.000000046s
$ ./test.rs.out
9223372036854775809
loop: 0.000000047s
$ ./test.rs.out
9223372036854775809
loop: 0.000000045s
Run Code Online (Sandbox Code Playgroud)
该计划几乎立即结束.我还在C中使用for循环编写了一个等效代码,但它运行了很长时间.我想知道是什么让Rust代码如此之快.
C代码:
#include <stdint.h> …Run Code Online (Sandbox Code Playgroud) 我有两个(相当于?)程序,一个在另一个在Rust中.平均执行时间是:
走
package main
import (
"fmt"
"time"
)
func main() {
work := []float64{0.00, 1.00}
start := time.Now()
for i := 0; i < 100000000; i++ {
work[0], work[1] = work[1], work[0]
}
elapsed := time.Since(start)
fmt.Println("Execution time: ", elapsed)
}
Run Code Online (Sandbox Code Playgroud)
锈
我编译了 --release
use std::time::Instant;
fn main() {
let mut work: Vec<f64> = Vec::new();
work.push(0.00);
work.push(1.00);
let now = Instant::now();
for _x in 1..100000000 {
work.swap(0, 1);
}
let elapsed = now.elapsed();
println!("Execution time: {:?}", elapsed); …Run Code Online (Sandbox Code Playgroud) llvm-codegen ×10
rust ×6
c ×3
assembly ×2
clang ×2
performance ×2
avx ×1
go ×1
llvm ×1
optimization ×1
rounding ×1
ssa ×1
x86 ×1
x86-64 ×1