From bd2816c5fafa3ebb8a8078e4c11f827717ebe627 Mon Sep 17 00:00:00 2001 From: Philip Woolford Date: Thu, 22 Feb 2024 18:16:58 +1030 Subject: [PATCH] Implement f64 method polyfills with intrinsics support (#21) --- Cargo.toml | 4 +++- benches/wtinylfu_cache.rs | 10 ++++++---- src/lfu/tinylfu/bloom.rs | 7 +++++-- src/lib.rs | 9 ++++++++- src/lru/two_queue.rs | 11 +++++++---- src/polyfill.rs | 37 +++++++++++++++++++++++++++++++++++++ 6 files changed, 66 insertions(+), 12 deletions(-) create mode 100644 src/polyfill.rs diff --git a/Cargo.toml b/Cargo.toml index ab3fb19..ce9f66c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "caches" -version = "0.2.8" +version = "0.2.9" authors = ["Al Liu "] description = "This is a Rust implementation for popular caches (support no_std)." homepage = "https://github.com/al8n/caches-rs" @@ -42,7 +42,9 @@ nightly = ["rand/nightly"] [dependencies] bitvec = { version = "1", default-features = false } +cfg-if = "1.0.0" hashbrown = { version = "0.14", optional = true } +libm = {version = "0.2.8", optional = true} rand = {version = "0.8", optional = true} [dev-dependencies] diff --git a/benches/wtinylfu_cache.rs b/benches/wtinylfu_cache.rs index 31bc17b..652a38e 100644 --- a/benches/wtinylfu_cache.rs +++ b/benches/wtinylfu_cache.rs @@ -1,4 +1,4 @@ -use caches::{Cache, WTinyLFUCache, WTinyLFUCacheBuilder}; +use caches::{lfu::DefaultKeyHasher, Cache, WTinyLFUCache, WTinyLFUCacheBuilder}; use criterion::{black_box, criterion_group, criterion_main, BatchSize, Criterion}; use fnv::FnvBuildHasher; use rand::{thread_rng, Rng}; @@ -59,10 +59,11 @@ fn bench_wtinylfu_cache_fx_hasher(c: &mut Criterion) { .collect(), ); - let builder = WTinyLFUCacheBuilder::new(82, 6488, 1622, 8192) + let builder = WTinyLFUCacheBuilder::, BuildHasherDefault, BuildHasherDefault, BuildHasherDefault>::new(82, 6488, 1622, 8192) .set_window_hasher(BuildHasherDefault::::default()) .set_protected_hasher(BuildHasherDefault::::default()) - .set_probationary_hasher(BuildHasherDefault::::default()); + .set_probationary_hasher(BuildHasherDefault::::default()) + .set_key_hasher(DefaultKeyHasher::default()); let l = WTinyLFUCache::from_builder(builder).unwrap(); (l, nums) }, @@ -99,7 +100,8 @@ fn bench_wtinylfu_cache_fnv_hasher(c: &mut Criterion) { }) .collect(), ); - let builder = WTinyLFUCacheBuilder::new(82, 6488, 1622, 8192) + let builder = WTinyLFUCacheBuilder::, BuildHasherDefault, BuildHasherDefault, BuildHasherDefault>::new(82, 6488, 1622, 8192) + .set_key_hasher(DefaultKeyHasher::default()) .set_window_hasher(FnvBuildHasher::default()) .set_protected_hasher(FnvBuildHasher::default()) .set_probationary_hasher(FnvBuildHasher::default()); diff --git a/src/lfu/tinylfu/bloom.rs b/src/lfu/tinylfu/bloom.rs index 19c62e2..455eef8 100644 --- a/src/lfu/tinylfu/bloom.rs +++ b/src/lfu/tinylfu/bloom.rs @@ -7,7 +7,10 @@ // use bitvec::vec::BitVec; use alloc::{vec, vec::Vec}; +use crate::polyfill::{ceil, ln}; + const LN_2: f64 = core::f64::consts::LN_2; +const LN_2_2: f64 = LN_2 * LN_2; fn get_size(ui64: u64) -> (u64, u64) { let ui64 = if ui64 < 512 { 512 } else { ui64 }; @@ -21,8 +24,8 @@ fn get_size(ui64: u64) -> (u64, u64) { } fn calc_size_by_wrong_positives(num_entries: f64, wrongs: f64) -> (u64, u64) { - let size = (-num_entries * wrongs.ln() / LN_2.powi(2)).ceil() as u64; - let locs = (LN_2 * size as f64 / num_entries).ceil() as u64; + let size = ceil(-num_entries * ln(wrongs) / LN_2_2) as u64; + let locs = ceil(LN_2 * size as f64 / num_entries) as u64; (size, locs) } diff --git a/src/lib.rs b/src/lib.rs index af6ca60..5f84d5a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -89,10 +89,17 @@ #![cfg_attr(docsrs, feature(doc_cfg))] #![cfg_attr(docsrs, allow(unused_attributes))] #![deny(missing_docs)] -#![allow(clippy::blocks_in_if_conditions, clippy::enum_variant_names)] +#![allow(unused_doc_comments)] +#![allow(clippy::blocks_in_conditions, clippy::enum_variant_names)] extern crate alloc; +#[macro_use] +extern crate cfg_if; + +#[doc(hidden)] +pub(crate) mod polyfill; + #[cfg(not(feature = "std"))] extern crate hashbrown; diff --git a/src/lru/two_queue.rs b/src/lru/two_queue.rs index 18a3ea4..9d1aefa 100644 --- a/src/lru/two_queue.rs +++ b/src/lru/two_queue.rs @@ -9,6 +9,9 @@ use alloc::fmt; use core::borrow::Borrow; use core::hash::{BuildHasher, Hash}; +// f64 function polyfill to support no_std contexts +use crate::polyfill::floor; + /// `DEFAULT_2Q_RECENT_RATIO` is the ratio of the [`TwoQueueCache`] dedicated /// to recently added entries that have only been accessed once. /// @@ -191,8 +194,8 @@ impl TwoQueueCacheBuilder TwoQueueCache { } // Determine the sub-sizes - let rs = ((size as f64) * rr).floor() as usize; - let es = ((size as f64) * gr).floor() as usize; + let rs = floor((size as f64) * rr) as usize; + let es = floor((size as f64) * gr) as usize; // allocate the lrus let recent = RawLRU::new(size).unwrap(); diff --git a/src/polyfill.rs b/src/polyfill.rs new file mode 100644 index 0000000..94797c8 --- /dev/null +++ b/src/polyfill.rs @@ -0,0 +1,37 @@ +/// Three level poliyfill dor the f64 `ceil`, `ln`, and `floor` functions. +/// Using these functions in a no_std context falls back to libm's manual +/// implementation from musl's libc. +/// Using the nightly feature allows the upgrade to using LLVM hints, and +/// allowing LLVM to provide a software fallback for target platforms +/// witout hardware f64 instructions. + +cfg_if! { + if #[cfg(feature = "std")] { + #[inline(always)] + pub(crate) fn ceil(val: f64) -> f64 { + val.ceil() + } + #[inline(always)] + pub(crate) fn ln(val: f64) -> f64 { + val.ln() + } + #[inline(always)] + pub(crate) fn floor(val: f64) -> f64 { + val.floor() + } + } else { + use libm; + #[inline(always)] + pub(crate) fn ceil(val: f64) -> f64 { + libm::ceil(val) + } + #[inline(always)] + pub(crate) fn ln(val: f64) -> f64 { + libm::log(val) + } + #[inline(always)] + pub(crate) fn floor(val: f64) -> f64 { + libm::floor(val) + } + } +}