C代码转Zig与Rust的性能差异:Rust版本耗时过高问题排查
Rust代码性能落后C/Zig的问题分析
测试代码对比
C代码(耗时85秒)
unsigned int sorted_stepping(double seed, unsigned int* list, unsigned int max_val, unsigned int list_size) { unsigned int seat_number = round(seed * (max_val - list_size)); for (int i = 0; i < list_size; ++i) { if (list[i] > seat_number) { break; } else { seat_number++; } } return seat_number; } int main() { unsigned int max_val = 1000; unsigned int list[] = {1, 3, 4, 10, 15, 25, 100, 150, 214, 999, 1000}; double seed = 0.0; unsigned int random_number; // calling the function 1,000,000,000 times for (long i = 0; i < 1000000000; ++i) { seed += 0.000000001; random_number = sorted_stepping(seed, list, max_val, sizeof(list) / sizeof(unsigned int)); } fprintf(stdout, "Generated random number for seed %f is %d\n", seed, random_number); return 0; }
Zig代码(耗时85秒)
const std = @import("std"); pub fn sorted_stepping(seed: f64, list: []const u32, max_val: u32) u32 { const temp: f64 = @floatFromInt(@as(usize, max_val) - list.len); var seat_number: u32 = @intFromFloat(@round(seed * temp)); for (list) |list_item| { if (list_item > seat_number) { break; } else { seat_number = seat_number + 1; } } return seat_number; } pub fn main() void { const max_val = 1000; const list = [_]u32{ 1, 3, 4, 10, 15, 25, 100, 150, 214, 999, 1000 }; var seed: f64 = 0.0; var random_number: u32 = 0; // calling the function 1,000,000,000 times for (0..1_000_000_000) |_| { seed += 0.000000001; random_number = sorted_stepping(seed, &list, max_val); } std.debug.print("Generated random number for seed {} is {}\n", .{ seed, random_number }); }
Rust代码(耗时250秒)
fn sorted_stepping(seed: f64, list: &[u64], max_val: u64, list_size: usize) -> u64 { let mut seat_number = (seed * (max_val - list_size as u64) as f64) as u64; for item in list { if item > &seat_number { break; } else { seat_number = seat_number + 1; } } seat_number } fn main() { let max_val: u64 = 1000; let list = [1, 3, 4, 10, 15, 25, 100, 150, 214, 999, 1000]; let mut seed: f64 = 0.0; let mut random_number: u64 = 0; // calling the function 1,000,000,000 times let mut i: u32 = 0; while i < 1_000_000_000 { seed = seed + 0.000_000_001; random_number = sorted_stepping(seed, &list, max_val, list.len()); i = i + 1; } println!("Generated random number for seed {0} is {1}", seed, random_number); }
Rust代码的问题点
- 未使用release编译模式:这是核心原因——Rust默认编译为debug模式,会关闭所有优化并保留大量调试检查,性能比release模式差数倍。如果测试用的是
cargo run默认命令,换成cargo run --release后性能会大幅接近C/Zig水平。 - 不必要的64位整数使用:C和Zig用32位无符号整数(
unsigned int/u32),但Rust代码用了u64。64位整数运算在部分CPU上开销更高,频繁类型转换的损耗在10亿次调用下会被放大。 - 低效的循环写法:Rust用手动计数的
while循环,而C/Zig用范围for循环。编译器对范围for循环的优化支持更好,更容易做循环展开、向量化等操作,手动while循环可能让编译器难以识别优化机会。 - 冗余参数与不必要的引用操作:
sorted_stepping函数的list_size参数完全冗余,因为slice&[u64]本身包含长度,额外传参可能导致编译器做不必要的检查;循环里item > &seat_number可简化为*item > seat_number,减少不必要的引用比较操作。
为什么Zig更容易实现和C一致的性能
- 默认优化策略对齐C:Zig默认编译就是优化过的(类似C的
-O2),不需要手动指定编译模式,新手不会因忘记开优化得到糟糕性能。 - 类型系统贴近C:Zig的整数类型、内存布局默认和C完全一致,类型转换规则直白,无隐式转换或额外语法糖,编译器更容易生成和C等价的机器码。
- 显式控制安全检查:Zig的安全检查是可选的,可通过编译选项关闭所有额外检查,完全复刻C的运行时行为;而Rust即使在release模式下,仍会保留部分内存安全检查(本例无影响),Zig的“显式控制”理念让开发者更易掌控性能。
- 函数调用与内联规则更宽松:Zig的小函数默认更容易被编译器内联,和C的内联行为一致;Rust虽支持内联,但需显式标注或依赖编译器自动判断,极致性能场景下Zig规则更贴近C。
内容的提问来源于stack exchange,提问作者Gokul Raj V
相关产品推荐
相关产品推荐

