From a8d89e7d6c5aaa4c5ac20000a1100122b35e8e73 Mon Sep 17 00:00:00 2001 From: arielherself Date: Wed, 27 Dec 2023 17:16:47 +0800 Subject: [PATCH] refactor: large-scale performance improvement on Obvious --- api/click.js | 50 +++++++++++++++++++--- tools/PathBench.js | 6 +-- tools/ShortestPath.js | 97 +++++++++++++------------------------------ 3 files changed, 76 insertions(+), 77 deletions(-) diff --git a/api/click.js b/api/click.js index 25fcee3..3d35706 100644 --- a/api/click.js +++ b/api/click.js @@ -23,20 +23,34 @@ const shortest_path = noexcept((nodes, ways, start_point, end_point) => { // const clean_nodes = nodes; const count = {}; const aff = {}; + const location = {}; const clean_nodes = {}; + // TODO: delete this + let count1 = 0, tot = 0; for (const way_id in ways) { // sill_unwrap(way_id); const st = new Set(); const [l, n] = get_row(ways, way_id); for (let i = 0; i < n; ++i) { + l[i] = l[i].toString(); // critical const curr = l[i]; if (st.has(curr)) continue; st.add(curr); + if (location[curr]) { + location[curr][way_id] = i; + } else { + location[curr] = {[way_id]: i}; + } clean_nodes[curr] = nodes[curr]; if (count[curr]) { + if (count[curr] === 1) { + count1 -= 1; + } ++count[curr]; } else { count[curr] = 1; + count1 += 1; + tot += 1; } if (aff[curr]) { aff[curr][way_id] = true; @@ -45,35 +59,56 @@ const shortest_path = noexcept((nodes, ways, start_point, end_point) => { } } } + sill(`One ratio: ${count1} / ${tot}`); // sill_unwrap(aff); const actual_start_node_id = find_nearest_node_id(clean_nodes, start_point); const actual_end_node_id = find_nearest_node_id(clean_nodes, end_point); // sill_unwrap(typeof (actual_start_node_id)); // sill(count); const ch_dict = {}; + const ch_dict_bench = {}; let f = 1; for (const t in ways) { // if (t === ways[aff[actual_start_node_id]]) sill('yes'); const [l, n] = get_row(ways, t); + // // TODO: delete this + // for (let i = 1; i < n; ++i) { + // const curr = l[i]; + // const prev = l[i-1]; + // const distance = haversine_distance(nodes[curr], nodes[prev]); + // if (ch_dict_bench[curr]) { + // ch_dict_bench[curr].push([prev, distance]); + // } else { + // ch_dict_bench[curr] = [[prev, distance]]; + // } + // if (ch_dict_bench[prev]) { + // ch_dict_bench[prev].push([curr, distance]); + // } else { + // ch_dict_bench[prev] = [[curr, distance]]; + // } + // } let prev = ''; + let distance = 0; for (let i = 0; i < n; ++i) { - const curr = l[i].toString(); + const curr = l[i]; + if (i) distance += haversine_distance(nodes[curr], nodes[l[i-1]]); // if(true) { if (count[curr] > 1 || curr === actual_end_node_id || curr === actual_start_node_id) { // if (curr === actual_start_node_id) sill(curr === actual_start_node_id); if (prev !== '') { if (ch_dict[curr]) { - ch_dict[curr].push(prev); + ch_dict[curr].push([prev, distance]); } else { - ch_dict[curr] = [prev]; + ch_dict[curr] = [[prev, distance]]; } if (ch_dict[prev]) { - ch_dict[prev].push(curr); + ch_dict[prev].push([curr, distance]); } else { - ch_dict[prev] = [curr]; + ch_dict[prev] = [[curr, distance]]; } } prev = curr; + distance = 0; } } } @@ -84,7 +119,9 @@ const shortest_path = noexcept((nodes, ways, start_point, end_point) => { sill(`start distance: ${haversine_distance(nodes[actual_start_node_id], start_point)}`); sill(`dest distance: ${haversine_distance(nodes[actual_end_node_id], end_point)}`); sill("calling __spa..."); - const seq = __spa(clean_nodes, ways, ch_dict, count, aff, actual_start_node_id, actual_end_node_id); + // // TODO: delete this + // const seq = __spa(nodes, clean_nodes, ways, location, ch_dict, ch_dict_bench, count, aff, actual_start_node_id, actual_end_node_id); + const seq = __spa(clean_nodes, ways, location, ch_dict, count, aff, actual_start_node_id, actual_end_node_id); const res = [end_point]; seq.forEach((node_id) => { if (clean_nodes[node_id]) res.push(clean_nodes[node_id]); @@ -102,6 +139,7 @@ export default function handler(req, res) { maxlon = Math.max(...lonRange) + 0.01, maxlat = Math.max(...latRange) + 0.01; if (haversine_distance([minlat, minlon], [maxlat, maxlon]) > 100) { res.status(500); + sill('rejected'); return; } const request_uri = `https://www.overpass-api.de/api/interpreter?data=[out:json];way[highway](${minlat},${minlon},${maxlat},${maxlon});(._;>;);out body;`; diff --git a/tools/PathBench.js b/tools/PathBench.js index c5fcd93..cc2e2b2 100644 --- a/tools/PathBench.js +++ b/tools/PathBench.js @@ -2,18 +2,18 @@ import * as SP from "./ShortestPath"; import {sill} from "./Debug"; import {get_row} from "./Misc"; -export default function benchmark(nodes, clean_nodes, ways, ch_dict, count, aff, actual_start_node_id, actual_end_node_id) { +export default function benchmark(nodes, clean_nodes, ways, location, ch_dict, ch_dict_bench, count, aff, actual_start_node_id, actual_end_node_id) { sill(`==========PathBench==========`); let start_time, end_time; let res; //benchmark Obvious-Dijkstra start_time = performance.now(); - res = SP.obvious_dijkstra(clean_nodes, ways, ch_dict, count, aff, actual_start_node_id, actual_end_node_id); + res = SP.obvious_dijkstra(clean_nodes, ways, location, ch_dict, count, aff, actual_start_node_id, actual_end_node_id); end_time = performance.now(); sill(`Obvious-Dijkstra run-time: ${end_time - start_time} ms`); // benchmark Dijkstra start_time = performance.now(); - res = SP.dijkstra(nodes, ways, ch_dict, count, aff, actual_start_node_id, actual_end_node_id); + const _ = SP.dijkstra(nodes, ways, location, ch_dict_bench, count, aff, actual_start_node_id, actual_end_node_id); end_time = performance.now(); sill(`Dijkstra run-time: ${end_time - start_time} ms`); sill(`==============================`); diff --git a/tools/ShortestPath.js b/tools/ShortestPath.js index b20f638..94078a1 100644 --- a/tools/ShortestPath.js +++ b/tools/ShortestPath.js @@ -24,23 +24,15 @@ function __haversine_distance(p1, p2) { * Use Dijkstra algorithm to find the shortest path. * @param nodes node index list * @param ways unused in this implementation + * @param loc unused in this implementation * @param ch adjacent table * @param count unused in this implementation * @param aff unused in this implementation * @param u start node id * @param p destination node id */ -function __dijkstra(nodes, ways, ch, count, aff, u, p) { +function __dijkstra(nodes, ways, loc, ch, count, aff, u, p) { // sill(`node count: ${Object.keys(nodes).length}`); - const weight_cache = {}; - const get_weight = (n1, n2) => { - const tup = [n1, n2]; - if (weight_cache[tup]) { - return weight_cache[tup]; - } - weight_cache[tup] = __haversine_distance(nodes[n1], nodes[n2]); - return weight_cache[tup]; - }; const dis = {}; const fa = {}; const vis = new Set(); @@ -56,9 +48,9 @@ function __dijkstra(nodes, ways, ch, count, aff, u, p) { vis.add(v); const t = ch[v].length; for (let j = 0; j < t; ++j) { - const c = ch[v][j]; + const [c, w] = ch[v][j]; if (!nodes[c]) continue; - const w = get_weight(v, c); + // const w = get_weight(v, c); if (!dis[c] || d + w < dis[c]) { dis[c] = d + w; pq.push([dis[c], c]); @@ -86,42 +78,15 @@ function __dijkstra(nodes, ways, ch, count, aff, u, p) { * Use Dijkstra algorithm with Obvious optimization to find the shortest path. * @param nodes node index list * @param ways node list for each way + * @param loc relative location in the way * @param ch adjacent table * @param count determine isolated nodes * @param aff affiliation of isolated nodes * @param u start node id * @param p destination node id */ -function __obvious_dijkstra(nodes, ways, ch, count, aff, u, p) { +function __obvious_dijkstra(nodes, ways, loc, ch, count, aff, u, p) { // sill(`node count: ${Object.keys(nodes).length}`); - const weight_cache = {}; - const get_weight_along = (way, n1, n2) => { - const tup = [n1, n2]; - if (weight_cache[tup]) { - return weight_cache[tup]; - } - let res = 0; - const n = way.length; - let prev = ''; - let state = 0; - for (let i = 0; i < n; ++i) { - const curr = way[i].toString(); - if (curr === n1 || curr === n2) { - if (state) { - res += (__haversine_distance(nodes[prev], nodes[curr])); - break; - } else { - state = 1; - prev = curr; - } - } else if (state) { - res += (__haversine_distance(nodes[prev], nodes[curr])); - prev = curr; - } - } - weight_cache[tup] = res; - return res; - }; const dis = {}; const fa = {}; const vis = new Map(); @@ -140,37 +105,15 @@ function __obvious_dijkstra(nodes, ways, ch, count, aff, u, p) { } // sill('loop'); vis.set(v,true); - dis[v] = d; const t = ch[v].length; for (let j = 0; j < t; ++j) { - const c = ch[v][j]; + const [c, w] = ch[v][j]; if (!nodes[c]) continue; - const way_id = find_common(aff[c], aff[v]); - if (!way_id) sill("FATAL: NO COMMON WAY"); - const way = ways[way_id]; - const w = get_weight_along(way, v, c); // sill(w); - if (w > 0 && (!dis[c] || d + w < dis[c])) { + if ((!dis[c] || d + w < dis[c])) { dis[c] = d + w; pq.push([dis[c], c]); - const v_oc = way.indexOf(parseInt(v)); - const c_oc = way.indexOf(parseInt(c)); - let prev = c; - if (v_oc < c_oc) { - for (let _p = c_oc - 1; _p >= v_oc; --_p) { - const node = way[_p].toString(); - fa[prev] = node; - if (vis.has(node)) break; - prev = node; - } - } else { - for (let _p = c_oc + 1; _p <= v_oc; ++_p) { - const node = way[_p].toString(); - fa[prev] = node; - if (vis.has(node)) break; - prev = node; - } - } + fa[c] = v; } } } @@ -179,14 +122,32 @@ function __obvious_dijkstra(nodes, ways, ch, count, aff, u, p) { const res = [p]; vis.clear(); while (fa[curr]) { - curr = fa[curr].toString(); + const prev = curr; + curr = fa[curr]; if (vis.has(curr)) { sill(`Cycle at ${curr}`); // sill(res); break; } vis.set(curr,true); - res.push(curr); + + // res.push(curr); + const way_id = find_common(aff[prev], aff[curr]); + const way = ways[way_id]; + if(!way) { + continue; + } + const p_oc = loc[prev][way_id]; + const c_oc = loc[curr][way_id]; + if (p_oc < c_oc) { + for (let _p = p_oc + 1; _p <= c_oc; ++_p) { + res.push(way[_p]); + } + } else { + for (let _p = p_oc - 1; _p >= c_oc; --_p) { + res.push(way[_p]); + } + } } // sill("finished Obvious Dijkstra."); return res;