More
This commit is contained in:
parent
cd34ee693f
commit
05a1f316e1
58 changed files with 3113 additions and 1277 deletions
|
|
@ -1138,12 +1138,12 @@ pub fn bounds_for(name: &str) -> Option<&'static Bounds> {
|
|||
/// The server will panic at startup if the data contains groups not in this list or vice versa.
|
||||
pub const POI_GROUP_ORDER: &[&str] = &[
|
||||
"Public Transport",
|
||||
"Groceries",
|
||||
"Leisure",
|
||||
"Education",
|
||||
"Health",
|
||||
"Emergency Services",
|
||||
"Other",
|
||||
"Groceries",
|
||||
"Local Businesses",
|
||||
"Culture",
|
||||
"Services",
|
||||
|
|
|
|||
|
|
@ -37,6 +37,38 @@ use tracing_subscriber::EnvFilter;
|
|||
|
||||
use state::{AppState, SharedState};
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
fn resident_memory_kib() -> Option<u64> {
|
||||
let status = std::fs::read_to_string("/proc/self/status").ok()?;
|
||||
status.lines().find_map(|line| {
|
||||
line.strip_prefix("VmRSS:")?
|
||||
.split_whitespace()
|
||||
.next()?
|
||||
.parse()
|
||||
.ok()
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
fn trim_allocator(label: &'static str) {
|
||||
let before = resident_memory_kib();
|
||||
let trimmed = unsafe { libc::malloc_trim(0) };
|
||||
let after = resident_memory_kib();
|
||||
if let (Some(before), Some(after)) = (before, after) {
|
||||
info!(
|
||||
label,
|
||||
trimmed = trimmed != 0,
|
||||
rss_before_mib = format_args!("{:.1}", before as f64 / 1024.0),
|
||||
rss_after_mib = format_args!("{:.1}", after as f64 / 1024.0),
|
||||
released_mib = format_args!("{:.1}", before.saturating_sub(after) as f64 / 1024.0),
|
||||
"Allocator trim"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
fn trim_allocator(_label: &'static str) {}
|
||||
|
||||
#[derive(Parser)]
|
||||
#[command(
|
||||
name = "perfect-postcode",
|
||||
|
|
@ -166,6 +198,7 @@ async fn main() -> anyhow::Result<()> {
|
|||
cli.postcode_features.display(),
|
||||
);
|
||||
let property_data = data::PropertyData::load(&cli.properties, &cli.postcode_features)?;
|
||||
trim_allocator("property data load");
|
||||
info!(
|
||||
rows = property_data.lat.len(),
|
||||
features = property_data.num_features,
|
||||
|
|
@ -194,6 +227,7 @@ async fn main() -> anyhow::Result<()> {
|
|||
|
||||
info!("Loading POI data from {}", poi_path.display());
|
||||
let poi_data = data::POIData::load(&poi_path)?;
|
||||
trim_allocator("poi data load");
|
||||
info!(pois = poi_data.lat.len(), "POI data loaded");
|
||||
|
||||
info!("Building POI spatial grid index");
|
||||
|
|
@ -206,6 +240,7 @@ async fn main() -> anyhow::Result<()> {
|
|||
}
|
||||
info!("Loading place data from {}", places_path.display());
|
||||
let place_data = data::PlaceData::load(places_path)?;
|
||||
trim_allocator("place data load");
|
||||
info!(places = place_data.name.len(), "Place data loaded");
|
||||
|
||||
// Load postcode boundaries
|
||||
|
|
@ -221,6 +256,7 @@ async fn main() -> anyhow::Result<()> {
|
|||
postcodes_path.display()
|
||||
);
|
||||
let postcode_data = data::PostcodeData::load(postcodes_path)?;
|
||||
trim_allocator("postcode boundary load");
|
||||
info!(
|
||||
postcodes = postcode_data.postcodes.len(),
|
||||
"Postcode boundaries loaded"
|
||||
|
|
|
|||
|
|
@ -85,79 +85,80 @@ pub async fn get_filter_counts(
|
|||
let has_travel = !travel_entries.is_empty();
|
||||
let (pc_interner, pc_keys) = state.data.postcode_parts();
|
||||
|
||||
let rows = state.grid.query(south, west, north, east);
|
||||
let row_count = rows.len();
|
||||
let row_count = state.grid.count_in_bounds(south, west, north, east);
|
||||
|
||||
let mut total_passing: u32 = 0;
|
||||
let mut impacts = vec![0u32; num_total_filters];
|
||||
|
||||
for row_idx in rows {
|
||||
let row = row_idx as usize;
|
||||
let base = row * num_features;
|
||||
let mut fail_count: u32 = 0;
|
||||
let mut fail_index: usize = 0;
|
||||
state
|
||||
.grid
|
||||
.for_each_in_bounds(south, west, north, east, |row_idx| {
|
||||
let row = row_idx as usize;
|
||||
let base = row * num_features;
|
||||
let mut fail_count: u32 = 0;
|
||||
let mut fail_index: usize = 0;
|
||||
|
||||
// Test numeric filters
|
||||
for (i, f) in parsed_filters.iter().enumerate() {
|
||||
let raw = feature_data[base + f.feat_idx];
|
||||
if raw == NAN_U16 || raw < f.min_u16 || raw > f.max_u16 {
|
||||
fail_count += 1;
|
||||
fail_index = i;
|
||||
if fail_count > 1 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Test enum filters
|
||||
if fail_count <= 1 {
|
||||
for (i, f) in parsed_enum_filters.iter().enumerate() {
|
||||
// Test numeric filters
|
||||
for (i, f) in parsed_filters.iter().enumerate() {
|
||||
let raw = feature_data[base + f.feat_idx];
|
||||
if raw == NAN_U16 || !f.allowed.contains(&raw) {
|
||||
if raw == NAN_U16 || raw < f.min_u16 || raw > f.max_u16 {
|
||||
fail_count += 1;
|
||||
fail_index = parsed_filters.len() + i;
|
||||
fail_index = i;
|
||||
if fail_count > 1 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Test travel time filters
|
||||
if fail_count <= 1 && has_travel {
|
||||
let postcode = pc_interner.resolve(&pc_keys[row]);
|
||||
for (slot, &ti) in travel_filter_indices.iter().enumerate() {
|
||||
let entry = &travel_entries[ti];
|
||||
let minutes = travel_data[ti].get(postcode).map(|r| {
|
||||
if entry.use_best {
|
||||
r.best_minutes.unwrap_or(r.minutes)
|
||||
} else {
|
||||
r.minutes
|
||||
}
|
||||
});
|
||||
let passes = match (minutes, entry.filter_min, entry.filter_max) {
|
||||
(Some(mins), Some(fmin), Some(fmax)) => {
|
||||
(mins as f32) >= fmin && (mins as f32) <= fmax
|
||||
}
|
||||
(None, Some(_), Some(_)) => false,
|
||||
_ => true,
|
||||
};
|
||||
if !passes {
|
||||
fail_count += 1;
|
||||
fail_index = num_regular + slot;
|
||||
if fail_count > 1 {
|
||||
break;
|
||||
// Test enum filters
|
||||
if fail_count <= 1 {
|
||||
for (i, f) in parsed_enum_filters.iter().enumerate() {
|
||||
let raw = feature_data[base + f.feat_idx];
|
||||
if raw == NAN_U16 || !f.allowed.contains(&raw) {
|
||||
fail_count += 1;
|
||||
fail_index = parsed_filters.len() + i;
|
||||
if fail_count > 1 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
match fail_count {
|
||||
0 => total_passing += 1,
|
||||
1 => impacts[fail_index] += 1,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
// Test travel time filters
|
||||
if fail_count <= 1 && has_travel {
|
||||
let postcode = pc_interner.resolve(&pc_keys[row]);
|
||||
for (slot, &ti) in travel_filter_indices.iter().enumerate() {
|
||||
let entry = &travel_entries[ti];
|
||||
let minutes = travel_data[ti].get(postcode).map(|r| {
|
||||
if entry.use_best {
|
||||
r.best_minutes.unwrap_or(r.minutes)
|
||||
} else {
|
||||
r.minutes
|
||||
}
|
||||
});
|
||||
let passes = match (minutes, entry.filter_min, entry.filter_max) {
|
||||
(Some(mins), Some(fmin), Some(fmax)) => {
|
||||
(mins as f32) >= fmin && (mins as f32) <= fmax
|
||||
}
|
||||
(None, Some(_), Some(_)) => false,
|
||||
_ => true,
|
||||
};
|
||||
if !passes {
|
||||
fail_count += 1;
|
||||
fail_index = num_regular + slot;
|
||||
if fail_count > 1 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
match fail_count {
|
||||
0 => total_passing += 1,
|
||||
1 => impacts[fail_index] += 1,
|
||||
_ => {}
|
||||
}
|
||||
});
|
||||
|
||||
// Map filter indices back to feature/travel names
|
||||
let mut impact_map: FxHashMap<String, u32> = FxHashMap::default();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue