介绍

大家好,我是@yagisawa,一位传统的嵌入式工程师。
在网上浏览有关 Rust 的信息时,

毕竟,它不是C的对手。嵌入式是装配速度调整的世界。

有时我看到这样的写作
所以,他们无敌可能是真的,但他们到底有多无敌呢?我试图找出答案。
在研究 Rust 的速度时,发现了下面这篇文章,所以我使用了蒙特卡洛方法进行比较过程。

由于内置倾向于避免浮点运算(出于速度原因),本文处理整数值乘以 100(即计算结果在 314 左右)。

环境

常见的

  • 操作系统
    • Windows 10 家庭版 21H2(64 位)

C。

  • arm-none-eabi-gcc
    • 10.3.1 20210824
  • 优化
    • -Ofast

代码

我编写了一个代码,它使用 SysTick 以 1 毫秒为单位测量算法的执行时间。
这可能是包括库在内的语言的技能,但我使用线性同余方法生成了自己的随机数,因此不会有任何区别。

C。
static int cnt = 0;

void SysTick_Handler() {
	cnt++;
}

int32_t rand() {
	static int64_t x = 10;
	const int64_t  a = 48271;
	const int64_t  b = 2147483647;
	x = (a * x) & b;

	return (int32_t)x;
}

int32_t montecarlo() {
	const int32_t times = 100000;
	int32_t       cnt   = 0;

	for (int32_t i = 0; i < times; i++) {
		const int32_t x = rand() % 1000;
		const int32_t y = rand() % 1000;

		if ((x * x + y * y) <= 1000000) {
			cnt++;
		}
	}

	return cnt * 400 / times;
}

int main() {
	const int32_t    times  = 10;
	volatile int32_t result = 0;

	volatile uint32_t* stk_ctrl_addr = (uint32_t*)0xE000E010;
	volatile uint32_t* stk_load_addr = (uint32_t*)0xE000E014;
	volatile uint32_t* stk_val_addr  = (uint32_t*)0xE000E018;

    *stk_val_addr  = 0;
    *stk_load_addr = 2000;
    *stk_ctrl_addr = 0x3;

	for (int32_t i = 0; i < times; i++) {
		result += montecarlo();
	}
	result = (result + times - 1) / times;

	while ( 1 );
}
static mut CNT: i32 = 0;

#[no_mangle]
unsafe extern "C" fn SysTick() {
    CNT += 1;
}

fn rand() -> i32 {
    static mut X: i64 = 10;
    let        a: i64 = 48271;
    let        b: i64 = 2147483647;
    unsafe {
        X = (a * X) & b;
        X as i32
    }
}

fn montecarlo() -> i32 {
    let times   = 100000;
    let mut cnt = 0;

    for _ in 0..times {
        let x = rand() % 1000;
        let y = rand() % 1000;

        if (x * x + y * y) <= 1000_000 {
            cnt += 1;
        }
    }

    cnt * 400 / times
}

fn main() -> ! {
    let     times  = 10;
    let mut result = 0;

    let stk_ctrl_addr  = 0xE000_E010 as *mut usize;
    let stk_load_addr  = 0xE000_E014 as *mut usize;
    let stk_val_addr   = 0xE000_E018 as *mut usize;

    unsafe {
        write_volatile(stk_val_addr,  0);
        write_volatile(stk_load_addr, 2_000);
        write_volatile(stk_ctrl_addr, 0x3);
    }

    for _ in 0..times {
        result += montecarlo();
    }
    unsafe {
        write_volatile(&mut result, (result + times - 1) / times);
    }

    loop {}
}

结果

首先,当我在没有优化的情况下进行比较时,它变成了如下。

C。
11,090 毫秒 22,832 毫秒

啊,这是个坏人……
结果慢了两倍多。
看,时间不早了!好像说...

嗯,不优化嵌入式软件是不可能的,所以当我将它与最喜欢的优化进行比较时,如下所示。

C。
1,126 毫秒 1,186 毫秒

哦,差别不大!
由于相同的过程重复了 10 次,因此每次有 6ms 的差异。
老实说,嵌入式 6 ms 的差异是巨大的,但我认为你已经尽力了。

借口是启动是一个懒惰的实现,它运行在 16MHz (1/5.25) 而它应该运行在 84MHz,所以如果你认真一点,它会相差大约 1ms。虽然取决于要制作的系统,但最近出现了频率从几百MHz到1GHz的微型计算机,因此这种微型计算机的差异甚至更小。
好吧,这并没有改变迟到的事实,但是_| ̄|○

检查

如果我有时间,我会尝试比较生成什么样的机器语言。

首先,如果您公开发布版本的部分大小,

C。
Sections:
Name          Size      VMA       LMA       File off  Algn
.isr_vector   00000040  08000000  08000000  00010000  2**0
.text         000001b4  08000040  08000040  00010040  2**2
.rodata       00000000  080001f4  080001f4  00020008  2**0
.data         00000008  20000000  080001fc  00020000  2**3
.bss          00000020  20000008  08000204  00020008  2**2
Sections:
Name            Size     VMA      LMA      Type
.vector_table   00000040 08000000 08000000 DATA
.text           000017b6 08000040 08000040 TEXT
.rodata         00000000 080017f6 080017f6 TEXT
.bss            00000004 20000000 20000000 BSS
.data           00000008 20000008 080017f6 DATA

这就是结果。

Rust 的优化以牺牲二进制大小为代价优化速度,因此文本部分的大小为 6,070 字节,而 C 为 436 字节。

综上所述

可能还有调整的余地,但是当我正常执行 Rust 时(比 C 慢)。
不过,我不认为有没有希望的区别,而且我认为 Rust 会生活在中大型系统中,所以我不会悲观。
另外,在这个比较中,我没有写出利用 Rust 零成本抽象等优点的代码,所以我认为如果我写的代码考虑到安全性,比如错误检查(Rust 是在构建时可以进行错误检查)。
如果你没有足够的资源并且想要编写速度调整的代码,你应该使用 C,如果你有足够的资源并且想要编写高度安全的代码,你应该使用 Rust。
贪得无厌的人你甚至可以在 Rust 中编写程序集, 可能有一个选项可以使用汇编作为核心部分,其余部分使用 Rust。

后记

我尝试了@fujitanozomu 建议的代码。
变化是

C。
// rand内
int64_t     uint32_t

// montecarlo内
% 1000      % 1024U
<= 1000000  <= 1046529U
// rand内
i64              u32
X = (a * X) & b  X = a.wrapping_mul(X) & b // オーバーフロー対策

// montecarlo内
% 1000           % 1024u32
<= 1000000       <= 1_046_529u32

变成。

结果如下。

C。
813毫秒 690毫秒

Rust 击败 C!
似乎每个编译器都擅长优化而不擅长优化。

使用 gcc,后端不是 LLVM,而且不公平,所以请注意,这个结果并不是全部。
有时间我会试着用clang比较一下速度。

正如我在评论中所写,速度比较是语言与语言这不是一个简单的故事,而是一个依赖于实施者的知识和技能的世界。
我想小心不要编写廉价代码并说“我不能使用这种语言”。


原创声明:本文系作者授权爱码网发表,未经许可,不得转载;

原文地址:https://www.likecs.com/show-308629112.html

相关文章:

  • 2021-09-06
  • 2021-04-23
  • 2021-11-03
  • 2022-12-23
  • 2021-05-26
  • 2021-08-03
  • 2021-09-20
猜你喜欢
  • 2022-12-23
  • 2021-12-06
  • 2022-01-25
  • 2021-12-04
  • 2021-11-05
  • 2022-02-10
  • 2021-12-08
相关资源
相似解决方案