Я решил посмотреть скорость работы с файлами в Rust. Меня посетила простая идея: держать индекс до записи в памяти. На маленьких размерах записей (10 байт) всё шло очень даже хорошо. 
результат простого теста для 10 байт
insert took: 3.112033812s
get took: 1.433445888s
 
Затем 100 байт.
результат простого теста для 100 байт
insert took: 3.02511339s
get took: 1.395097325s
 
А затем 500.
результат простого теста для 500 байт
insert took: 11.586355301s
get took: 1.778387251s
 
Как мы можем заметить, скорость чтения незначительно изменилась, но записи - резко уменьшилась!
 Но затем я поставил 1000 байт и результаты вызвали у меня много вопросов.
результат простого теста для 1000 байт
insert took: 44.390536515s
get took: 132.18342292s
 
Я могу понять, почему падает скорость записи (но не в 4 раза, при изменении размера в 2), но что происходит с чтением? Почему скорость чтения падает в 78 раз?
Привожу код и буду только рад услышать его критику.
use positioned_io::ReadAt;
struct CustomStorage {
    infos: DashMap<Vec<u8>, (u32, u64)>,
    atomic_indexes: Vec<Arc<AtomicU64>>,
    files: Vec<Arc<RwLock<File>>>,
    read_files: Vec<Arc<RwLock<File>>>,
    mask: usize
}
impl CustomStorage {
    fn new(size: usize) -> Self {
        let size = {
            if size.is_power_of_two() {
                size
            } else {
                size.next_power_of_two()
            }
        };
        let lob = f64::log2(size as f64) as u32;
        let mask = (1 << lob) - 1;
        let mut files = Vec::with_capacity(size);
        let mut read_files = Vec::with_capacity(size);
        let mut atomic_indexes = Vec::with_capacity(size);
        std::fs::DirBuilder::new().create("data1").unwrap();
        for i in 0..size {
            files.push(Arc::new(RwLock::new(File::create(format!("data1/test{}.txt", i)).unwrap())));
            read_files.push(Arc::new(RwLock::new(File::open(format!("data1/test{}.txt", i)).unwrap())));
            atomic_indexes.push(Arc::new(AtomicU64::new(0)));
        }
        Self {
            infos: DashMap::new(),
            atomic_indexes,
            files,
            read_files,
            mask
        }
    }
    #[inline(always)]
    fn insert(&self, key: Vec<u8>, mut value: Vec<u8>) {
        let res = self.get_file(&key);
        if res.is_none() {
            return;
        }
        let kl = key.len();
        let vl = value.len();
        let size = (4 + kl + vl) as u64;
        let mut buf = Vec::with_capacity(size as usize);
        buf.push((kl >> 8) as u8);
        buf.push(kl as u8);
        buf.append(&mut key.clone());
        buf.push((vl >> 8) as u8);
        buf.push(vl as u8);
        buf.append(&mut value);
        let index;
        let (file, atomic_index) = unsafe { res.unwrap_unchecked() };
        {
            let mut file = file.write().unwrap();
            file.write_all(&buf).expect("failed to write");
            index = atomic_index.fetch_add(size, std::sync::atomic::Ordering::SeqCst);
        }
        self.infos.insert(key, (size as u32, index));
    }
    #[inline(always)]
    fn get(&self, key: &Vec<u8>) -> Option<Vec<u8>>{
        let res = self.get_index_and_file(key);
        if res.is_none() {
            return None;
        }
        let (file, info) = unsafe { res.unwrap_unchecked() };
        let file = file.read().unwrap();
        let mut buf = vec![0; info.0 as usize];
        file.read_at(info.1, &mut buf).expect("failed to read");
        return Some(buf);
    }
    #[inline(always)]
    fn get_file(&self, key: &Vec<u8>) -> Option<(Arc<RwLock<File>>, Arc<AtomicU64>)> {
        let mut hasher = DefaultHasher::new();
        key.hash(&mut hasher);
        let number = hasher.finish() as usize & self.mask;
        return Some((self.files[number].clone(), self.atomic_indexes[number].clone()));
    }
    #[inline(always)]
    fn get_index_and_file(&self, key: &Vec<u8>) -> Option<(Arc<RwLock<File>>, (u32, u64))> {
        let mut hasher = DefaultHasher::new();
        key.hash(&mut hasher);
        let number = hasher.finish() as usize & self.mask;
        let info;
        {
            let index_ = self.infos.get(key);
            if index_.is_none() {
                return None;
            }
            info = unsafe { *index_.unwrap_unchecked() };
        }
        return Some((self.read_files[number].clone(), info));
    }
    fn bench(keys: Arc<Vec<Vec<u8>>>, values: Arc<Vec<Vec<u8>>>) {
        let custom_storage = Arc::new(Self::new(128));
        let mut joins = Vec::with_capacity(PAR);
        let start = std::time::Instant::now();
        for i in 0..PAR {
            let space = custom_storage.clone();
            let keys = keys.clone();
            let values = values.clone();
            joins.push(std::thread::spawn(move || {
                for j in i * COUNT..(i + 1) * COUNT {
                    space.insert(keys[j].clone(), values[j].clone());
                }
            }))
        }
        for join in joins {
            join.join().unwrap();
        }
        println!("insert took: {:?}", start.elapsed());
        let mut joins = Vec::with_capacity(PAR);
        let start = std::time::Instant::now();
        for i in 0..PAR {
            let space = custom_storage.clone();
            let keys = keys.clone();
            joins.push(std::thread::spawn(move || {
                for j in i * COUNT..(i + 1) * COUNT {
                    space.get(&keys[j]);
                }
            }))
        }
        for join in joins {
            join.join().unwrap();
        }
        println!("get took: {:?}", start.elapsed());
    }
}
Константы для теста
const N: usize = 3_000_000;
const SIZE: usize = 1000;
const PAR: usize = 256;
const COUNT: usize = N/PAR;