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;
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 rstar::{PointDistance, RTree, RTreeObject, AABB, Envelope};
use serde::{Deserialize, Serialize};
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_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);
#[derive(Serialize, Deserialize)]
pub struct World { // TODO: make r-trees faster than this, for my sanity
pub struct World {
index: HashMap<Vec3, usize>,
data: Vec<Chunk>,
last: Option<usize>,
pallette: Pallette,
}
impl World {
@ -26,11 +27,12 @@ impl World {
index: HashMap::new(),
data: Vec::new(),
last: None,
pallette: Pallette::new(),
}
}
pub fn get(&self, block: Vec3) -> Option<Block> {
let chunk = self.get_chunk(block)?;
Some(chunk.get(block)?)
Some(chunk.get(block, &self.pallette)?)
}
pub fn set(&mut self, block: Block) {
@ -45,11 +47,11 @@ impl World {
match chunk {
Some(chunk) => {
self.data[chunk].set(block).unwrap();
self.data[chunk].set(block, &mut self.pallette).unwrap();
},
None => {
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.index.insert(chunk_coords, self.data.len() - 1);
},
@ -113,31 +115,33 @@ pub struct Block {
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct Chunk {
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 {
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 {
pos,
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);
if !self.contains(&pos.pos) {
return Err(anyhow!("out of bounds"));
}
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(())
}
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 local = pos - chunk;
if !self.contains(&pos) {
@ -145,8 +149,10 @@ impl Chunk {
}
let local = local.map(|n| n as usize);
let idx = self.data[local.x][local.y][local.z]?;
Some(Block {
name: self.data[local.x][local.y][local.z].clone()?,
name: pallette.name(idx)?,
pos,
})
}

View file

@ -30,6 +30,7 @@ use indoc::formatdoc;
use crate::blocks::Block;
mod blocks;
mod pallette;
mod names;
mod mine;
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())
}
}