1
Fork 0

probabilistic world. 32 bit keys instead of a string

This causes most of the big benchmarks to fail, and it makes the
positive_many_set three times faster. More integration is required to
create a actual performance benefit by adjusting the search code
This commit is contained in:
Andy Killorin 2024-06-12 16:04:50 -05:00
parent 0717f6f4f0
commit 191fee36e1
Signed by: ank
GPG key ID: 23F9463ECB67FE8C
3 changed files with 50 additions and 13 deletions

View file

@ -1,23 +1,24 @@
extern crate test; extern crate test;
use std::{sync::Arc, ops::Sub, collections::HashMap}; use std::{collections::HashMap, num::NonZero, ops::Sub, sync::Arc};
use anyhow::{Ok, anyhow}; use anyhow::{anyhow, Context, Ok};
use nalgebra::Vector3; use nalgebra::Vector3;
use rstar::{PointDistance, RTree, RTreeObject, AABB, Envelope}; use rstar::{PointDistance, RTree, RTreeObject, AABB, Envelope};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use tokio::sync::{RwLock, OwnedRwLockReadGuard, OwnedRwLockWriteGuard}; use tokio::sync::{RwLock, OwnedRwLockReadGuard, OwnedRwLockWriteGuard};
use crate::{turtle::TurtleCommand, paths::{self, TRANSPARENT}}; use crate::{pallette::{self, Pallette}, paths::{self, TRANSPARENT}, turtle::TurtleCommand};
const CHUNK_SIZE: usize = 8; const CHUNK_SIZE: usize = 8;
const CHUNK_VOLUME: usize = CHUNK_SIZE * CHUNK_SIZE * CHUNK_SIZE; const CHUNK_VOLUME: usize = CHUNK_SIZE * CHUNK_SIZE * CHUNK_SIZE;
const CHUNK_VEC: Vec3 = Vec3::new(CHUNK_SIZE as i32, CHUNK_SIZE as i32, CHUNK_SIZE as i32); const CHUNK_VEC: Vec3 = Vec3::new(CHUNK_SIZE as i32, CHUNK_SIZE as i32, CHUNK_SIZE as i32);
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
pub struct World { // TODO: make r-trees faster than this, for my sanity pub struct World {
index: HashMap<Vec3, usize>, index: HashMap<Vec3, usize>,
data: Vec<Chunk>, data: Vec<Chunk>,
last: Option<usize>, last: Option<usize>,
pallette: Pallette,
} }
impl World { impl World {
@ -26,11 +27,12 @@ impl World {
index: HashMap::new(), index: HashMap::new(),
data: Vec::new(), data: Vec::new(),
last: None, last: None,
pallette: Pallette::new(),
} }
} }
pub fn get(&self, block: Vec3) -> Option<Block> { pub fn get(&self, block: Vec3) -> Option<Block> {
let chunk = self.get_chunk(block)?; let chunk = self.get_chunk(block)?;
Some(chunk.get(block)?) Some(chunk.get(block, &self.pallette)?)
} }
pub fn set(&mut self, block: Block) { pub fn set(&mut self, block: Block) {
@ -45,11 +47,11 @@ impl World {
match chunk { match chunk {
Some(chunk) => { Some(chunk) => {
self.data[chunk].set(block).unwrap(); self.data[chunk].set(block, &mut self.pallette).unwrap();
}, },
None => { None => {
let mut new_chunk = Chunk::new(chunk_coords); let mut new_chunk = Chunk::new(chunk_coords);
new_chunk.set(block).unwrap(); new_chunk.set(block, &mut self.pallette).unwrap();
self.data.push(new_chunk); self.data.push(new_chunk);
self.index.insert(chunk_coords, self.data.len() - 1); self.index.insert(chunk_coords, self.data.len() - 1);
}, },
@ -113,31 +115,33 @@ pub struct Block {
#[derive(Serialize, Deserialize, Clone, Debug)] #[derive(Serialize, Deserialize, Clone, Debug)]
pub struct Chunk { pub struct Chunk {
pos: Vec3, /// position in chunk coordinates (world/16) pos: Vec3, /// position in chunk coordinates (world/16)
data: [[[Option<String>;CHUNK_SIZE];CHUNK_SIZE];CHUNK_SIZE] data: [[[Option<NonZero<u32>>;CHUNK_SIZE];CHUNK_SIZE];CHUNK_SIZE]
} }
impl Chunk { impl Chunk {
fn new(pos: Vec3) -> Self { fn new(pos: Vec3) -> Self {
let data :[[[Option<String>;CHUNK_SIZE];CHUNK_SIZE];CHUNK_SIZE]= Default::default(); let data :[[[Option<NonZero<u32>>;CHUNK_SIZE];CHUNK_SIZE];CHUNK_SIZE]= Default::default();
Self { Self {
pos, pos,
data data
} }
} }
fn set(&mut self, pos: Block) -> anyhow::Result<()> { fn set(&mut self, pos: Block, pallette: &mut Pallette) -> anyhow::Result<()> {
let chunk = self.pos.component_mul(&CHUNK_VEC); let chunk = self.pos.component_mul(&CHUNK_VEC);
if !self.contains(&pos.pos) { if !self.contains(&pos.pos) {
return Err(anyhow!("out of bounds")); return Err(anyhow!("out of bounds"));
} }
let local: Vector3<usize> = (pos.pos - chunk).map(|n| n as usize); let local: Vector3<usize> = (pos.pos - chunk).map(|n| n as usize);
self.data[local.x][local.y][local.z] = Some(pos.name); let idx = pallette.number(&pos.name);
self.data[local.x][local.y][local.z] = Some(idx);
Ok(()) Ok(())
} }
fn get(&self, pos: Vec3) -> Option<Block> { fn get(&self, pos: Vec3, pallette: &Pallette) -> Option<Block> {
let chunk = self.pos.component_mul(&CHUNK_VEC); let chunk = self.pos.component_mul(&CHUNK_VEC);
let local = pos - chunk; let local = pos - chunk;
if !self.contains(&pos) { if !self.contains(&pos) {
@ -145,8 +149,10 @@ impl Chunk {
} }
let local = local.map(|n| n as usize); let local = local.map(|n| n as usize);
let idx = self.data[local.x][local.y][local.z]?;
Some(Block { Some(Block {
name: self.data[local.x][local.y][local.z].clone()?, name: pallette.name(idx)?,
pos, pos,
}) })
} }

View file

@ -30,6 +30,7 @@ use indoc::formatdoc;
use crate::blocks::Block; use crate::blocks::Block;
mod blocks; mod blocks;
mod pallette;
mod names; mod names;
mod mine; mod mine;
mod fell; mod fell;

30
server/src/pallette.rs Normal file
View file

@ -0,0 +1,30 @@
use std::{collections::HashMap, hash::{DefaultHasher, Hasher, SipHasher}, num::NonZero};
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize)]
pub struct Pallette {
names: HashMap<u32, String>,
}
impl Pallette {
pub fn new() -> Self {
let names = HashMap::new();
Self { names }
}
pub fn number(&mut self, name: &str) -> NonZero<u32> {
let mut hasher = DefaultHasher::new();
hasher.write(name.as_bytes());
let mut key = hasher.finish() as u32;
if key == 0 {key += 1}; // double the collisions!
self.names.insert(key,name.to_owned());
let key = unsafe { NonZero::new_unchecked(key) }; // SAFETY: see
// two lines above
key
}
pub fn name(&self, number: NonZero<u32>) -> Option<String> {
self.names.get(&number.get()).map(|s| s.to_owned())
}
}