You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Rust中函数返回Vec<f32>为何出现大幅性能损耗?

Rust分位数计算的性能差异问题

问题场景

内联逻辑的情况

将分位数计算逻辑内联在main函数中时,release模式下总耗时仅44.484µs,代码如下:

fn main() -> Result<(), ()> {
    // ... Some code for loading data, ends up with:
    let series: ChunkedArray<Float32Type>

    let start = Instant::now();
    let percentiles = [0.0, 0.2, 0.4, 0.6, 0.8, 1.0];
    let mut result = Vec::<f32>::with_capacity(percentiles.len());
    for p in percentiles {
        match series.quantile(p as f64, QuantileInterpolOptions::Lower) {
            Ok(v) => result.push(v.unwrap()),
            _ => {}
        }
    }
    let duration = start.elapsed();
    println!("Time elapsed is: {:?}", duration);
    println!("{}",result);
    Ok(())
}

提取为独立函数的情况

将逻辑提取为独立的percentile函数后,main中调用函数的总耗时骤升至约72ms,但函数内部逻辑耗时仅13µs左右;调试模式下总耗时更是达到约600ms,代码如下:

fn main() -> Result<(), ()> {
    // ... Some code for loading data, ends up with:
    let series: ChunkedArray<Float32Type>

    let start = Instant::now();
    let percentiles = percentile(series);
    let duration = start.elapsed();
    println!("Time elapsed is: {:?}", duration);
    println!("{}",result);
    Ok(())
}

pub fn percentile(data: Series) -> Vec<f32> {
    let data = data;

    let start = Instant::now();
    let percentiles = [0.0, 0.2, 0.4, 0.6, 0.8, 1.0];
    let mut result = Vec::<f32>::with_capacity(percentiles.len());
    for p in percentiles {
        match data.quantile(p as f64, QuantileInterpolOptions::Lower) {
            Ok(v) => result.push(v.unwrap()),
            _ => {}
        }
    }
    let duration = start.elapsed();
    println!("internal time: {:?}", duration);

    result
}

经测试,原代码中series的销毁耗时仅241µs,无法解释该差异。疑问:为何函数返回Vec<f32>会导致如此巨大的性能损耗?忽略了什么?


核心原因分析

不是返回Vec<f32>导致的性能问题,真正的元凶是**ChunkedArray<Float32Type>转Series的深克隆开销**:

  • 内联版本中,直接使用series(ChunkedArray类型)调用quantile,没有任何类型转换开销;
  • 提取函数时,你将ChunkedArray<Float32Type>传给了接受Series参数的percentile函数,这里触发了ChunkedArray到Series的转换——而polars库中的Series克隆属于深克隆,会复制底层所有数据,这部分开销发生在函数调用之前(参数传递阶段),不在你函数内部的计时范围内,所以会出现"函数内部耗时极短但总耗时暴增"的现象。

调试模式下性能极差是因为Rust默认关闭了所有优化,同时插入了大量调试信息,类型转换的开销被进一步放大,属于正常现象。


解决方案

修改percentile函数的参数类型,直接接受ChunkedArray<Float32Type>的引用,避免不必要的深克隆:

pub fn percentile(data: &ChunkedArray<Float32Type>) -> Vec<f32> {
    let start = Instant::now();
    let percentiles = [0.0, 0.2, 0.4, 0.6, 0.8, 1.0];
    let mut result = Vec::<f32>::with_capacity(percentiles.len());
    for p in percentiles {
        match data.quantile(p as f64, QuantileInterpolOptions::Lower) {
            Ok(v) => result.push(v.unwrap()),
            _ => {}
        }
    }
    let duration = start.elapsed();
    println!("internal time: {:?}", duration);

    result
}

在main中调用时传递引用:

let percentiles = percentile(&series);

这样既保留了代码的模块化,又完全消除了类型转换的开销,性能会和内联版本一致。


内容的提问来源于stack exchange,提问作者Crispy Holiday

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 23:17:40