Rustのlazystra静的!使用法ベンチマークとコード深い潜水
23513 ワード
lazy_static
さびの生態系の基礎的な木箱の一つです.これは、明示的な初期化コールなしで静的変数を使用することができます.
私はそれを多くの時間を使用してその性能の意味を与えることなく考えた.すべてのその怠惰な静的な魔法が若干の隠れたコストを持っているならば、私を心配させました.
The crate's docs 力学を説明する
lazy_static!
マクロとして:The Deref implementation uses a hidden static variable that is guarded by an atomic check on each access.
それは無邪気に聞こえるが、まだ質問がある.
lazy_static
サブモジュールで使用されますが、モジュールからの関数へのすべての呼び出しで再初期化されますか?lazy_static
私は、それを通して掘るより、それがベンチマークにより簡単であるとわかりましたsource code .ハウツー動画
git clone https://github.com/rimutaka/empirical.git
cargo +nightly bench
cargo +nightly test --benches
結果
$ cargo +nightly bench
test bad_rust_local ... bench: 40,608 ns/iter (+/- 9,239)
test lazy_static_backref ... bench: 27 ns/iter (+/- 1)
test lazy_static_external_mod ... bench: 27 ns/iter (+/- 0)
test lazy_static_inner ... bench: 27 ns/iter (+/- 1)
test lazy_static_local ... bench: 27 ns/iter (+/- 5)
test lazy_static_reinit ... bench: 26 ns/iter (+/- 1)
test once_cell_lazy ... bench: 26 ns/iter (+/- 2)
test vanilla_rust_local ... bench: 27 ns/iter (+/- 0)
結果はかなりきれいに見えた.唯一の外れは、私がベースラインをセットするために意図的にベンチに入れた悪いコードの部分でした.TL博士:lazystra静的!良いですが、OnceLIGNセルは、新しいプロジェクトの方が良いかもしれません。
ベンチマーク詳細
BadHeight Rustchen local ()
このベンチは明らかに愚かなことをします-それはループ内の正規表現を再コンパイルします.
b.iter(|| {
let compiled_regex = regex::Regex::new(LONG_REGEX).unwrap(); // <-- don't place this inside a loop
let is_match = compiled_regex.is_match(TEST_EMAIL);
test::black_box(is_match);
});
40608 ns/iterで、それは我々に再コンパイルコストのベースラインを与えます.Vanillaum rustchen local ()
各アクセスについてアトミックチェックによる顕著な性能コストはなかった.
vanilla_rust_local
ベンチは、一度正規表現をコンパイルし、正確に同じ27 ns/iterlazy_static
.let compiled_regex = regex::Regex::new(LONG_REGEX).unwrap(); // <-- compiled once only
b.iter(|| {
let is_match = compiled_regex.is_match(TEST_EMAIL);
test::black_box(is_match);
});
<高橋潤子>
これらのベンチは
lazy_static
宣言された場所の唯一の違いがあります.ローカルレベルで:ローカル
LazyHard StaticCherm :サブモジュールレベル(同じファイル)で
別のファイルに配置されたモジュールで
ルートレベルで、サブモジュールで使用されます
lazy_static
宣言は全てのケースで同一であった.lazy_static! {
pub(crate) static ref COMPILED_REGEX: regex::Regex = regex::Regex::new(LONG_REGEX).unwrap();
}
の配置lazy_static! { ... }
宣言は全く違いました.LazyHand StaticCharity ()
最初の使用の前または後に静的変数を初期化することが可能です
lazy_static::initialize(&STATIC_VAR_NAME);
呼び出しには追加のパフォーマンスコストはなかったinitialize
初めて、または何度もそれ以降.これは以下のような状態になります.
Takes a shared reference to a lazy static and initializes it if it has not been already.
オンセル細胞
once_cell
と同じくらいエレガントですlazy_static!
で、約20 %速くなります32-thread test . それは50 %と同等に行われたlazy_static!
私の基本テストで.静的宣言は1行のコードです.
static COMPILED_REGEX_ONCE_CELL: once_cell::sync::Lazy<regex::Regex> =
once_cell::sync::Lazy::new(|| regex::Regex::new(LONG_REGEX).unwrap());
と静的変数の使用法はlazy_static!
: b.iter(|| {
let is_match = COMPILED_REGEX_ONCE_CELL.is_match(TEST_EMAIL);
test::black_box(is_match);
});
があるRFC to merge once_cell
into std::lazy
標準ライブラリの一部を作る.あなたが新しいプロジェクトを始めているならば、それはより将来の証拠選択であるかもしれません.LazyRange静的代替
静的変数の宣言
pub(crate) static STATIC_REGEX: regex::Regex = regex::Regex::new(LONG_REGEX).unwrap();
ERROR: calls in statics are limited to constant functions, tuple structs and tuple variants rustc E0015
const関数の宣言
const fn static_regex() -> regex::Regex {
regex::Regex::new(LONG_REGEX).unwrap()
}
ERROR: calls in constant functions are limited to constant functions, tuple structs and tuple variants rustc E0015
深さの静的
このセクションは、lazy_static
実際にどのように動作を理解する.
このプロジェクトのデモコードはいくつかの部分に分割されます.
ベンチ:このポストのベンチマークのための源
例/拡張性ベース.RS:拡張コードを取得する最小の実装
lazy_static!
マクロ例/拡張.RS :生成された拡張コード
lazy_static!
マクロから拡張マクロ.RSsrc/mainRS:拡張コードに基づく自己完結型実装
rustup default nightly
rustup default stable
マクロコード
走る
cargo expand --example expansion_base
木枠のコードを出力します.あなたは完全な出力を見つけることができますexamples/expanded.rs ファイル.つまり、以下のようになります.
#![feature(prelude_import)]
#[prelude_import]
use std::prelude::rust_2021::*;
#[macro_use]
extern crate std;
extern crate lazy_static;
use lazy_static::lazy_static;
#[allow(missing_copy_implementations)]
#[allow(non_camel_case_types)]
#[allow(dead_code)]
struct COMPILED_REGEX {
__private_field: (),
}
#[doc(hidden)]
static COMPILED_REGEX: COMPILED_REGEX = COMPILED_REGEX {
__private_field: (),
};
impl ::lazy_static::__Deref for COMPILED_REGEX {
type Target = regex::Regex;
fn deref(&self) -> ®ex::Regex {
#[inline(always)]
fn __static_ref_initialize() -> regex::Regex {
regex::Regex::new(".*").unwrap()
}
#[inline(always)]
fn __stability() -> &'static regex::Regex {
static LAZY: ::lazy_static::lazy::Lazy<regex::Regex> = ::lazy_static::lazy::Lazy::INIT;
LAZY.get(__static_ref_initialize)
}
__stability()
}
}
impl ::lazy_static::LazyStatic for COMPILED_REGEX {
fn initialize(lazy: &Self) {
let _ = &**lazy;
}
}
fn main() {
let _x = COMPILED_REGEX.is_match("abc");
}
上のスニペットは、lazy_static
そして、それは魔法の大部分が起こるところです.私はそれを持っていない簡単なバージョンに蒸留
lazy_static
すべての依存関係として.それを通してスキムし、以下の説明に進みます.struct Lazy<T: Sync>(Cell<Option<T>>, Once);
unsafe impl<T: Sync> Sync for Lazy<T> {}
struct CompiledRegex {
__private_field: (),
}
static COMPILED_REGEX: CompiledRegex = CompiledRegex {
__private_field: (),
};
impl Deref for CompiledRegex {
type Target = regex::Regex;
fn deref(&self) -> ®ex::Regex {
static LAZY: Lazy<regex::Regex> = Lazy(Cell::new(None), Once::new());
LAZY.1.call_once(|| {
LAZY.0.set(Some(regex::Regex::new(LONG_REGEX).unwrap()));
});
unsafe {
match *LAZY.0.as_ptr() {
Some(ref x) => x,
None => {
panic!("attempted to dereference an uninitialized lazy static. This is a bug");
}
}
}
}
}
最後のスニペットでいくつかの重要な機能があります.struct Lazy
総称するstruct CompiledRegex
としてインスタンス化されるstatic COMPILED_REGEX
これは実際にコンパイルされた正規表現を保持します.impl Deref for CompiledRegex
静的変数としてコンパイルされた正規表現を与えるにはstd::sync::Once::call_once()
は正規表現を初期化するのに使われるmatch *LAZY.0.as_ptr()
構造体の鎖の中の深さから初期化された正規表現を取得するプログラムの実行
cargo run
このデモコードをsrc/main.rs 怠惰な静的な部分のために上記のスニペットを使用することfn main() {
println!("Program started");
println!("{TEST_EMAIL} is valid: {}", COMPILED_REGEX.is_match(TEST_EMAIL));
println!("{TEST_NOT_EMAIL} is valid: {}", COMPILED_REGEX.is_match(TEST_NOT_EMAIL));
}
を返します.Program started
Derefencing CompiledRegex
CompiledRegex initialized
[email protected] is valid: true
Derefencing CompiledRegex
Hello world! is valid: false
ご覧の通り.COMPILED_REGEX
は1回だけ初期化され、COMPILED_REGEX
変数が使用されます.Q . E . D .?)
Reference
この問題について(Rustのlazystra静的!使用法ベンチマークとコード深い潜水), 我々は、より多くの情報をここで見つけました https://dev.to/rimutaka/rusts-lazystatic-usage-benchmarks-and-code-deep-dive-1bicテキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol