This commit is contained in:
SKTT1Ryze 2021-07-13 23:40:35 +08:00
parent 261dda73d7
commit 95ff08a1b7
4 changed files with 75 additions and 25 deletions

View File

@ -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"] }

View File

@ -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
}
}
}

View File

@ -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));

View File

@ -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]);
}