continue
This commit is contained in:
parent
261dda73d7
commit
95ff08a1b7
|
@ -7,3 +7,6 @@ edition = "2018"
|
|||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
async-trait = "0.1.50"
|
||||
spin = "0.7"
|
||||
lazy_static = { version = "1.4", features = ["spin_no_std"] }
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
//! 块缓冲层实现
|
||||
use std::mem::MaybeUninit;
|
||||
|
||||
use alloc::sync::Arc;
|
||||
use crate::AsyncBlockDevive;
|
||||
use super::cache::{Cache, Node, LFUCache};
|
||||
use spin::RwLock;
|
||||
|
||||
// B: 一个块中的字节数
|
||||
// N: 块缓冲层的块数
|
||||
pub struct BlockCache<C: Cache<N, Key = usize, Value = [u8; B]>, const B: usize, const N: usize> {
|
||||
block_device: Arc<dyn AsyncBlockDevive + Send + Sync>,
|
||||
cache: C
|
||||
}
|
||||
|
||||
impl BlockCache<LFUCache<usize, [u8; 512], 100>, 512, 100> {
|
||||
pub fn init(device: Arc<dyn AsyncBlockDevive + Send + Sync>) -> Self {
|
||||
let mut data: [MaybeUninit<Node<usize, [u8; 512]>>; 100] = unsafe {
|
||||
MaybeUninit::uninit().assume_init()
|
||||
};
|
||||
for elem in &mut data[..] {
|
||||
*elem = MaybeUninit::new(Node::new(0, [0; 512]));
|
||||
}
|
||||
let nodes = unsafe { core::mem::transmute::<_, [Node<usize, [u8; 512]>; 100]>(data) };
|
||||
let lfu_cache = LFUCache::empty(nodes);
|
||||
Self {
|
||||
block_device: device,
|
||||
cache: lfu_cache
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,14 +1,15 @@
|
|||
//! Cache Trait
|
||||
|
||||
pub trait Cache {
|
||||
type Key: ?Sized;
|
||||
pub trait Cache<const N: usize> {
|
||||
type Key;
|
||||
type Value;
|
||||
fn get(&mut self, key: &Self::Key) -> Option<&Self::Value>;
|
||||
fn put(&mut self, key: &Self::Key, value: Self::Value, write_back: impl FnOnce(&Self::Key, &Self::Value));
|
||||
// 如果有需要写回的值,将它返回
|
||||
fn put(&mut self, key: &Self::Key, value: Self::Value) -> Option<(Self::Key, Self::Value)>;
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
struct Node<K: Eq + PartialEq + Copy, V> {
|
||||
pub struct Node<K: Eq + PartialEq + Copy, V: Clone> {
|
||||
key: K,
|
||||
value: V,
|
||||
cnt: usize,
|
||||
|
@ -16,7 +17,7 @@ struct Node<K: Eq + PartialEq + Copy, V> {
|
|||
dirty: bool
|
||||
}
|
||||
|
||||
impl<K: Eq + PartialEq + Copy, V> Node<K, V> {
|
||||
impl<K: Eq + PartialEq + Copy, V: Clone> Node<K, V> {
|
||||
pub fn new(key: K, value: V) -> Self {
|
||||
Self {
|
||||
key,
|
||||
|
@ -28,35 +29,35 @@ impl<K: Eq + PartialEq + Copy, V> Node<K, V> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<K: Eq + PartialEq + Copy, V> PartialEq for Node<K, V> {
|
||||
impl<K: Eq + PartialEq + Copy, V: Clone> PartialEq for Node<K, V> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.cnt == other.cnt
|
||||
}
|
||||
}
|
||||
|
||||
impl<K: Eq + PartialEq + Copy, V> Eq for Node<K, V> {}
|
||||
impl<K: Eq + PartialEq + Copy, V: Clone> Eq for Node<K, V> {}
|
||||
|
||||
impl<K: Eq + PartialEq + Copy, V> Ord for Node<K, V> {
|
||||
impl<K: Eq + PartialEq + Copy, V: Clone> Ord for Node<K, V> {
|
||||
fn cmp(&self, other: &Self) -> core::cmp::Ordering {
|
||||
self.cnt.cmp(&other.cnt).then_with(|| self.time.cmp(&other.time))
|
||||
}
|
||||
}
|
||||
|
||||
impl<K: Eq + PartialEq + Copy, V> PartialOrd for Node<K, V> {
|
||||
impl<K: Eq + PartialEq + Copy, V: Clone> PartialOrd for Node<K, V> {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
/// 这里采用比较保守的 LFU 算法实现,将来可能考虑采用性能更好的方案
|
||||
pub struct LFUCache<K: Eq + PartialEq + Copy, V, const N: usize> {
|
||||
pub struct LFUCache<K: Eq + PartialEq + Copy, V: Clone, const N: usize> {
|
||||
data: [Node<K, V>; N],
|
||||
size: usize,
|
||||
time: usize
|
||||
}
|
||||
|
||||
impl<K: Eq + PartialEq + Copy, V, const N: usize> LFUCache<K, V, N> {
|
||||
fn init(data: [Node<K, V>; N]) -> Self {
|
||||
impl<K: Eq + PartialEq + Copy, V: Clone, const N: usize> LFUCache<K, V, N> {
|
||||
pub fn init(data: [Node<K, V>; N]) -> Self {
|
||||
Self {
|
||||
data,
|
||||
size: N,
|
||||
|
@ -64,7 +65,7 @@ impl<K: Eq + PartialEq + Copy, V, const N: usize> LFUCache<K, V, N> {
|
|||
}
|
||||
}
|
||||
// todo: 用 `maybuninit`
|
||||
fn empty(data: [Node<K, V>; N]) -> Self {
|
||||
pub fn empty(data: [Node<K, V>; N]) -> Self {
|
||||
Self {
|
||||
data,
|
||||
size: 0,
|
||||
|
@ -73,7 +74,7 @@ impl<K: Eq + PartialEq + Copy, V, const N: usize> LFUCache<K, V, N> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<K: Eq + PartialEq + Copy, V, const N: usize> Cache for LFUCache<K, V, N> {
|
||||
impl<K: Eq + PartialEq + Copy, V: Clone, const N: usize> Cache<N> for LFUCache<K, V, N> {
|
||||
type Key = K;
|
||||
type Value = V;
|
||||
fn get(&mut self, key: &Self::Key) -> Option<&Self::Value> {
|
||||
|
@ -86,7 +87,7 @@ impl<K: Eq + PartialEq + Copy, V, const N: usize> Cache for LFUCache<K, V, N> {
|
|||
&node.value
|
||||
})
|
||||
}
|
||||
fn put(&mut self, key: &Self::Key, value: Self::Value, write_back: impl FnOnce(&Self::Key, &Self::Value)) {
|
||||
fn put(&mut self, key: &Self::Key, value: Self::Value) -> Option<(Self::Key, Self::Value)> {
|
||||
self.time += 1;
|
||||
if let Some(node) = self.data.iter_mut().find(|i| i.key == *key) {
|
||||
node.value = value;
|
||||
|
@ -94,6 +95,7 @@ impl<K: Eq + PartialEq + Copy, V, const N: usize> Cache for LFUCache<K, V, N> {
|
|||
node.time = self.time;
|
||||
// 写脏
|
||||
node.dirty = true;
|
||||
return None;
|
||||
} else {
|
||||
if self.size < N { // 缓存未满
|
||||
self.data[self.size].key = *key;
|
||||
|
@ -101,19 +103,22 @@ impl<K: Eq + PartialEq + Copy, V, const N: usize> Cache for LFUCache<K, V, N> {
|
|||
self.data[self.size].cnt = 1;
|
||||
self.data[self.size].time = self.time;
|
||||
self.size += 1;
|
||||
return None;
|
||||
} else { // 缓存已满
|
||||
// 顺序排序
|
||||
self.data[0..self.size].sort_by(|a, b| a.cmp(b));
|
||||
// 淘汰第一项
|
||||
let node = &mut self.data[0];
|
||||
// 如果数据已经被写脏,现在需要写回
|
||||
if node.dirty {
|
||||
write_back(&node.key, &node.value);
|
||||
}
|
||||
let write_back = (node.key, node.value.clone());
|
||||
node.key = *key;
|
||||
node.value = value;
|
||||
node.cnt = 1;
|
||||
node.time = self.time;
|
||||
// 如果数据已经被写脏,现在需要写回
|
||||
match node.dirty {
|
||||
true => Some(write_back),
|
||||
false => None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -128,13 +133,15 @@ fn lfu_cache_test() {
|
|||
assert!(lfu_cache.get(&0).is_none());
|
||||
assert!(lfu_cache.get(&1).is_none());
|
||||
assert!(lfu_cache.get(&2).is_none());
|
||||
lfu_cache.put(&1, 1, |_ , _| {});
|
||||
lfu_cache.put(&2, 2, |_ , _| {});
|
||||
assert_eq!(lfu_cache.get(&1).map(|v| *v), Some(1));
|
||||
lfu_cache.put(&3, 3, |_ , _| {});
|
||||
lfu_cache.put(&1, 1);
|
||||
lfu_cache.put(&1, 2);
|
||||
lfu_cache.put(&2, 2);
|
||||
assert_eq!(lfu_cache.get(&1).map(|v| *v), Some(2));
|
||||
lfu_cache.put(&3, 3);
|
||||
assert_eq!(lfu_cache.get(&2), None);
|
||||
assert_eq!(lfu_cache.get(&3).map(|v| *v), Some(3));
|
||||
lfu_cache.put(&4, 4, |_ , _| {});
|
||||
assert_eq!(lfu_cache.get(&3).map(|v| *v), Some(3));
|
||||
assert_eq!(lfu_cache.put(&4, 4), Some((1, 2)));
|
||||
assert_eq!(lfu_cache.get(&1), None);
|
||||
assert_eq!(lfu_cache.get(&3).map(|v| *v), Some(3));
|
||||
assert_eq!(lfu_cache.get(&4).map(|v| *v), Some(4));
|
||||
|
|
|
@ -2,5 +2,14 @@
|
|||
|
||||
mod bs_bpb;
|
||||
mod cache;
|
||||
mod block_cache;
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
use async_trait::async_trait;
|
||||
|
||||
#[async_trait]
|
||||
pub trait AsyncBlockDevive {
|
||||
async fn read(&self, block_id: usize, buf: &mut [u8]);
|
||||
async fn write(&self, block_id: usize, buf: &[u8]);
|
||||
}
|
Loading…
Reference in New Issue