//! Performance benchmarks for tile decoding and rendering //! //! These benchmarks measure the performance of critical paths in the map rendering pipeline. use criterion::{black_box, criterion_group, criterion_main, Criterion, BenchmarkId}; use nigig_map::tile_decode::build_tile_buffers_from_body; use nigig_map::geometry::TileKey; use nigig_map::style::CompiledMapTheme; /// Benchmark decoding a simple Overpass JSON response fn bench_decode_simple_json(c: &mut Criterion) { let json = r#"{ "version": 0.6, "elements": [ { "type": "node", "id": 1, "lat": 52.3676, "lon": 4.9041, "tags": {"name": "Test"} }, { "type": "way", "id": 2, "nodes": [10, 11, 12, 10], "tags": {"building": "yes"} }, {"type": "node", "id": 10, "lat": 52.367, "lon": 4.904}, {"type": "node", "id": 11, "lat": 52.368, "lon": 4.905}, {"type": "node", "id": 12, "lat": 52.367, "lon": 4.905} ] }"#; let tile_key = TileKey { z: 14, x: 8536, y: 5534 }; let theme = CompiledMapTheme::default(); c.bench_function("decode_simple_json", |b| { b.iter(|| { build_tile_buffers_from_body(black_box(tile_key), black_box(json), black_box(&theme)) }) }); } /// Benchmark decoding with varying numbers of features fn bench_decode_scaling_features(c: &mut Criterion) { let mut group = c.benchmark_group("decode_scaling"); for feature_count in [10, 50, 100, 500, 1000] { let json = generate_json_with_features(feature_count); let tile_key = TileKey { z: 14, x: 8536, y: 5534 }; let theme = CompiledMapTheme::default(); group.bench_with_input( BenchmarkId::new("features", feature_count), &feature_count, |b, _| { b.iter(|| { build_tile_buffers_from_body( black_box(tile_key), black_box(&json), black_box(&theme) ) }) } ); } group.finish(); } /// Benchmark MVT parsing (if we had test data) fn bench_mvt_parsing(c: &mut Criterion) { // This would require actual MVT binary test data // For now, we'll create a placeholder c.bench_function("mvt_parsing_placeholder", |b| { b.iter(|| { // Placeholder - would decode actual MVT data black_box(()) }) }); } /// Benchmark geometry tessellation fn bench_tessellation(c: &mut Criterion) { let json = generate_complex_building_json(); let tile_key = TileKey { z: 14, x: 8536, y: 5534 }; let theme = CompiledMapTheme::default(); c.bench_function("tessellate_complex_geometry", |b| { b.iter(|| { build_tile_buffers_from_body(black_box(tile_key), black_box(&json), black_box(&theme)) }) }); } /// Benchmark POI extraction fn bench_poi_extraction(c: &mut Criterion) { let json = generate_json_with_pois(100); let tile_key = TileKey { z: 14, x: 8536, y: 5534 }; let theme = CompiledMapTheme::default(); c.bench_function("extract_pois_100", |b| { b.iter(|| { build_tile_buffers_from_body(black_box(tile_key), black_box(&json), black_box(&theme)) }) }); } /// Benchmark label extraction and placement fn bench_label_extraction(c: &mut Criterion) { let json = generate_json_with_labels(50); let tile_key = TileKey { z: 14, x: 8536, y: 5534 }; let theme = CompiledMapTheme::default(); c.bench_function("extract_labels_50", |b| { b.iter(|| { build_tile_buffers_from_body(black_box(tile_key), black_box(&json), black_box(&theme)) }) }); } // Helper functions to generate test data fn generate_json_with_features(count: usize) -> String { let mut elements = Vec::new(); let mut node_id = 1000; for i in 0..count { // Create a simple building let base_lat = 52.367 + (i as f64 * 0.0001); let base_lon = 4.904 + (i as f64 * 0.0001); elements.push(format!( r#"{{"type": "way", "id": {}, "nodes": [{}, {}, {}, {}], "tags": {{"building": "yes"}}}}"#, i, node_id, node_id + 1, node_id + 2, node_id )); elements.push(format!( r#"{{"type": "node", "id": {}, "lat": {}, "lon": {}}}"#, node_id, base_lat, base_lon )); elements.push(format!( r#"{{"type": "node", "id": {}, "lat": {}, "lon": {}}}"#, node_id + 1, base_lat + 0.0001, base_lon )); elements.push(format!( r#"{{"type": "node", "id": {}, "lat": {}, "lon": {}}}"#, node_id + 2, base_lat + 0.0001, base_lon + 0.0001 )); node_id += 3; } format!( r#"{{"version": 0.6, "elements": [{}]}}"#, elements.join(",") ) } fn generate_json_with_pois(count: usize) -> String { let mut elements = Vec::new(); let amenities = ["restaurant", "cafe", "bar", "pub", "fast_food"]; for i in 0..count { let lat = 52.367 + (i as f64 * 0.0001); let lon = 4.904 + (i as f64 * 0.0001); let amenity = amenities[i % amenities.len()]; elements.push(format!( r#"{{"type": "node", "id": {}, "lat": {}, "lon": {}, "tags": {{"amenity": "{}", "name": "POI {}"}}}}"#, i, lat, lon, amenity, i )); } format!( r#"{{"version": 0.6, "elements": [{}]}}"#, elements.join(",") ) } fn generate_json_with_labels(count: usize) -> String { let mut elements = Vec::new(); let mut node_id = 1000; for i in 0..count { let base_lat = 52.367 + (i as f64 * 0.0001); let base_lon = 4.904 + (i as f64 * 0.0001); // Create a road with a name elements.push(format!( r#"{{"type": "way", "id": {}, "nodes": [{}, {}], "tags": {{"highway": "residential", "name": "Street {}"}}}}"#, i, node_id, node_id + 1, i )); elements.push(format!( r#"{{"type": "node", "id": {}, "lat": {}, "lon": {}}}"#, node_id, base_lat, base_lon )); elements.push(format!( r#"{{"type": "node", "id": {}, "lat": {}, "lon": {}}}"#, node_id + 1, base_lat + 0.0001, base_lon + 0.0001 )); node_id += 2; } format!( r#"{{"version": 0.6, "elements": [{}]}}"#, elements.join(",") ) } fn generate_complex_building_json() -> String { // Create a complex building with many vertices let mut nodes = Vec::new(); let mut node_ids = Vec::new(); let num_vertices = 20; for i in 0..num_vertices { let angle = (i as f64 / num_vertices as f64) * 2.0 * std::f64::consts::PI; let radius = 0.0002 + (i as f64 * 0.00001); let lat = 52.3676 + radius * angle.sin(); let lon = 4.9041 + radius * angle.cos(); let node_id = 1000 + i; node_ids.push(node_id); nodes.push(format!( r#"{{"type": "node", "id": {}, "lat": {}, "lon": {}}}"#, node_id, lat, lon )); } // Close the polygon node_ids.push(node_ids[0]); let way = format!( r#"{{"type": "way", "id": 1, "nodes": [{}], "tags": {{"building": "yes"}}}}"#, node_ids.iter().map(|id| id.to_string()).collect::>().join(",") ); let mut elements = vec![way]; elements.extend(nodes); format!( r#"{{"version": 0.6, "elements": [{}]}}"#, elements.join(",") ) } criterion_group!( benches, bench_decode_simple_json, bench_decode_scaling_features, bench_mvt_parsing, bench_tessellation, bench_poi_extraction, bench_label_extraction ); criterion_main!(benches);