gpt4 book ai didi

rust - 如何在 Rust 中使用非常量初始化程序初始化不可变的全局变量?

转载 作者:行者123 更新时间:2023-12-03 11:28:28 24 4
gpt4 key购买 nike

我试图获得一个在运行时只初始化一次的变量。
在 C/C++ 中 static将是我要寻找的关键字,但在 Rust 中,它必须由常量初始化。static mut是不安全的,我可以理解为什么,但它在概念上没有捕捉到我想要的东西,我想要一个不可变的变量。
以这个 tribonacci 函数的简单示例为例:

static sqrt_33_mul_3: f64 = 3.0 * 33.0f64.powf(0.5);

static tribonacci_constant: f64 = 1.0
+ (19.0 - sqrt_33_mul_3).powf(1.0 / 3.0)
+ (19.0 + sqrt_33_mul_3).powf(1.0 / 3.0);

fn tribonacci(n: f64) -> f64 {
return (
(tribonacci_constant / 3.0).powf(n)
/ (
(4.0 / 3.0)
* tribonacci_constant
- (1.0 / 9.0)
* tribonacci_constant.powf(2.0) - 1.0
)
).round();
}
我希望函数外部的两个静态变量只初始化一次,并且每次运行函数时都不调用 powf
我对 Rust 非常陌生,不知道对于普通的、有经验的用户来说可能是什么常识。
这是可能的,如果是这样,怎么做?

最佳答案

f64::powf是一个 const 函数,那么编译器应该转换类似 3.0 * 33.0f64.powf(0.5) 的东西降低到一个固定值。
虽然 lazy_static可以用来解决这个问题,使用lazy_statics 是有代价的,因为它们的设计目的不仅仅是支持简单的浮点常量。
您可以通过使用 Criterion 对两个实现进行基准测试来查看此成本:

pub mod ls {
use lazy_static::lazy_static; // 1.4.0

lazy_static! {
//TODO: Should this be a pow(1.0/3.0)?
pub static ref cbrt_33_mul_3: f64 = 3.0 * 33.0f64.powf(0.5);

pub static ref tribonacci_constant: f64 = 1.0
+ (19.0 - *cbrt_33_mul_3).powf(1.0 / 3.0)
+ (19.0 + *cbrt_33_mul_3).powf(1.0 / 3.0);
}

pub fn tribonacci(n: f64) -> f64 {
return (
(*tribonacci_constant / 3.0).powf(n)
/ (
(4.0 / 3.0)
* *tribonacci_constant
- (1.0 / 9.0)
* tribonacci_constant.powf(2.0) - 1.0
)
).round();
}
}

pub mod hc {
pub fn tribonacci(n: f64) -> f64 {
let p = 1.839286755214161;
let s = 0.3362281169949411;
return (s * p.powf(n)).round();
}
}

fn criterion_benchmark(c: &mut Criterion) {
c.bench_function("trib 5.1 ls", |b| b.iter(|| ls::tribonacci(black_box(5.1))));
c.bench_function("trib 5.1 hc", |b| b.iter(|| hc::tribonacci(black_box(5.1))));
}

criterion_group!(benches, criterion_benchmark);
criterion_main!(benches);
成本很小,但如果这是在您的核心循环中,则可能会很大。
在我的机器上,我得到(删除不相关的行后)
trib 5.1 ls             time:   [47.946 ns 48.832 ns 49.796 ns]                         
trib 5.1 hc time: [38.828 ns 39.898 ns 41.266 ns]
这大约有 20% 的差异。
如果您不喜欢在代码中使用硬编码常量,实际上可以在构建时使用 build.rs 生成这些常量。脚本。
我的基准测试完整示例如下所示:
build.rs
use std::env;
use std::fs;
use std::path::Path;

fn main() {
let out_dir = env::var_os("OUT_DIR").unwrap();
let dest_path = Path::new(&out_dir).join("constants.rs");

//TODO: Should this be a pow(1.0/3.0)?
let cbrt_33_mul_3: f64 = 3.0 * 33.0f64.powf(0.5);

let tribonacci_constant: f64 = 1.0
+ (19.0 - cbrt_33_mul_3).powf(1.0 / 3.0)
+ (19.0 + cbrt_33_mul_3).powf(1.0 / 3.0);

let p = tribonacci_constant / 3.0;
let s = 1.0 / (
(4.0 / 3.0)
* tribonacci_constant
- (1.0 / 9.0)
* tribonacci_constant.powf(2.0) - 1.0
);

fs::write(
&dest_path,
format!("\
pub mod tribonacci {{\n\
pub const P: f64 = {:.32};\n\
pub const S: f64 = {:.32};\n\
}}\n", p, s)
).unwrap();
println!("cargo:rerun-if-changed=build.rs");
}
src/lib.rs
pub mod constants {
include!(concat!(env!("OUT_DIR"), "/constants.rs"));
}

pub mod ls {
use lazy_static::lazy_static; // 1.4.0

lazy_static! {
//TODO: Should this be a pow(1.0/3.0)?
pub static ref cbrt_33_mul_3: f64 = 3.0 * 33.0f64.powf(0.5);

pub static ref tribonacci_constant: f64 = 1.0
+ (19.0 - *cbrt_33_mul_3).powf(1.0 / 3.0)
+ (19.0 + *cbrt_33_mul_3).powf(1.0 / 3.0);
}

pub fn tribonacci(n: f64) -> f64 {
return (
(*tribonacci_constant / 3.0).powf(n)
/ (
(4.0 / 3.0)
* *tribonacci_constant
- (1.0 / 9.0)
* tribonacci_constant.powf(2.0) - 1.0
)
).round();
}

}

pub mod hc {
pub fn tribonacci(n: f64) -> f64 {
let p = super::constants::tribonacci::P;
let s = super::constants::tribonacci::S;
return (s * p.powf(n)).round();
}
}
长凳/my_benchmark.rs
use criterion::{black_box, criterion_group, criterion_main, Criterion};
use rust_gen_const_vs_lazy_static::ls;
use rust_gen_const_vs_lazy_static::hc;

fn criterion_benchmark(c: &mut Criterion) {
c.bench_function("trib 5.1 ls", |b| b.iter(|| ls::tribonacci(black_box(5.1))));
c.bench_function("trib 5.1 hc", |b| b.iter(|| hc::tribonacci(black_box(5.1))));
}

criterion_group!(benches, criterion_benchmark);
criterion_main!(benches);
Cargo.toml
[package]
name = "rust_gen_const_vs_lazy_static"
version = "0.1.0"
edition = "2018"

[dependencies]
"lazy_static" = "1.4.0"

[dev-dependencies]
criterion = "0.3"

[[bench]]
name = "my_benchmark"
harness = false
$OUTDIR/constants.rs (生成)
pub mod tribonacci {
pub const P: f64 = 1.83928675521416096216853475198150;
pub const S: f64 = 0.33622811699494109527464047459944;
}

正如 Dilshod Tadjibaev 所建议的,使用 proc-macros 可以获得类似的结果,尽管在这种情况下需要做更多的工作。这提供了与构建时生成完全相同的速度。
为了设置它,我为宏创建了一个新的 crate trib_macros ,因为 proc-macros 需要在他们自己的箱子里。这个新箱子只包含两个文件 Cargo.tomlsrc/lib.rs Cargo.toml
[package]
name = "trib_macros"
version = "0.1.0"
edition = "2018"


[lib]
proc-macro = true
src/lib.rs
extern crate proc_macro;
use proc_macro::TokenStream;

#[proc_macro]
pub fn tp(_item: TokenStream) -> TokenStream {
let cbrt_33_mul_3: f64 = 3.0 * 33.0f64.powf(0.5);

let tribonacci_constant: f64 = 1.0
+ (19.0 - cbrt_33_mul_3).powf(1.0 / 3.0)
+ (19.0 + cbrt_33_mul_3).powf(1.0 / 3.0);

let p = tribonacci_constant / 3.0;
format!("{}f64",p).parse().unwrap()
}

#[proc_macro]
pub fn ts(_item: TokenStream) -> TokenStream {
let cbrt_33_mul_3: f64 = 3.0 * 33.0f64.powf(0.5);

let tribonacci_constant: f64 = 1.0
+ (19.0 - cbrt_33_mul_3).powf(1.0 / 3.0)
+ (19.0 + cbrt_33_mul_3).powf(1.0 / 3.0);

let s = 1.0 / (
(4.0 / 3.0)
* tribonacci_constant
- (1.0 / 9.0)
* tribonacci_constant.powf(2.0) - 1.0
);
format!("{}f64",s).parse().unwrap()
}
然后我们需要调整 Cargo.toml把它拉进来的原始 crate 。
[dependencies]
...
trib_macros = { path = "path/to/trib_macros" }
最后使用起来还是比较干净的:
pub mod mc {
use trib_macros::{ts,tp};

pub fn tribonacci(n: f64) -> f64 {
return (ts!() * tp!().powf(n)).round();
}
}
肯定有一种更简洁的方法来输出浮点文字标记,但我找不到它。

您可以在 https://github.com/mikeando/rust_code_gen_example 找到这些测试的完整存储库。

关于rust - 如何在 Rust 中使用非常量初始化程序初始化不可变的全局变量?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65120456/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com