refactor: large-scale performance improvement on Obvious

This commit is contained in:
arielherself 2023-12-27 17:16:47 +08:00
parent 9eb467077e
commit a8d89e7d6c
3 changed files with 76 additions and 77 deletions

View File

@ -23,20 +23,34 @@ const shortest_path = noexcept((nodes, ways, start_point, end_point) => {
// const clean_nodes = nodes; // const clean_nodes = nodes;
const count = {}; const count = {};
const aff = {}; const aff = {};
const location = {};
const clean_nodes = {}; const clean_nodes = {};
// TODO: delete this
let count1 = 0, tot = 0;
for (const way_id in ways) { for (const way_id in ways) {
// sill_unwrap(way_id); // sill_unwrap(way_id);
const st = new Set(); const st = new Set();
const [l, n] = get_row(ways, way_id); const [l, n] = get_row(ways, way_id);
for (let i = 0; i < n; ++i) { for (let i = 0; i < n; ++i) {
l[i] = l[i].toString(); // critical
const curr = l[i]; const curr = l[i];
if (st.has(curr)) continue; if (st.has(curr)) continue;
st.add(curr); st.add(curr);
if (location[curr]) {
location[curr][way_id] = i;
} else {
location[curr] = {[way_id]: i};
}
clean_nodes[curr] = nodes[curr]; clean_nodes[curr] = nodes[curr];
if (count[curr]) { if (count[curr]) {
if (count[curr] === 1) {
count1 -= 1;
}
++count[curr]; ++count[curr];
} else { } else {
count[curr] = 1; count[curr] = 1;
count1 += 1;
tot += 1;
} }
if (aff[curr]) { if (aff[curr]) {
aff[curr][way_id] = true; 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); // sill_unwrap(aff);
const actual_start_node_id = find_nearest_node_id(clean_nodes, start_point); 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); const actual_end_node_id = find_nearest_node_id(clean_nodes, end_point);
// sill_unwrap(typeof (actual_start_node_id)); // sill_unwrap(typeof (actual_start_node_id));
// sill(count); // sill(count);
const ch_dict = {}; const ch_dict = {};
const ch_dict_bench = {};
let f = 1; let f = 1;
for (const t in ways) { for (const t in ways) {
// if (t === ways[aff[actual_start_node_id]]) sill('yes'); // if (t === ways[aff[actual_start_node_id]]) sill('yes');
const [l, n] = get_row(ways, t); 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 prev = '';
let distance = 0;
for (let i = 0; i < n; ++i) { 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(true) {
if (count[curr] > 1 || curr === actual_end_node_id || curr === actual_start_node_id) { 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 (curr === actual_start_node_id) sill(curr === actual_start_node_id);
if (prev !== '') { if (prev !== '') {
if (ch_dict[curr]) { if (ch_dict[curr]) {
ch_dict[curr].push(prev); ch_dict[curr].push([prev, distance]);
} else { } else {
ch_dict[curr] = [prev]; ch_dict[curr] = [[prev, distance]];
} }
if (ch_dict[prev]) { if (ch_dict[prev]) {
ch_dict[prev].push(curr); ch_dict[prev].push([curr, distance]);
} else { } else {
ch_dict[prev] = [curr]; ch_dict[prev] = [[curr, distance]];
} }
} }
prev = curr; 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(`start distance: ${haversine_distance(nodes[actual_start_node_id], start_point)}`);
sill(`dest distance: ${haversine_distance(nodes[actual_end_node_id], end_point)}`); sill(`dest distance: ${haversine_distance(nodes[actual_end_node_id], end_point)}`);
sill("calling __spa..."); 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]; const res = [end_point];
seq.forEach((node_id) => { seq.forEach((node_id) => {
if (clean_nodes[node_id]) res.push(clean_nodes[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; maxlon = Math.max(...lonRange) + 0.01, maxlat = Math.max(...latRange) + 0.01;
if (haversine_distance([minlat, minlon], [maxlat, maxlon]) > 100) { if (haversine_distance([minlat, minlon], [maxlat, maxlon]) > 100) {
res.status(500); res.status(500);
sill('rejected');
return; return;
} }
const request_uri = `https://www.overpass-api.de/api/interpreter?data=[out:json];way[highway](${minlat},${minlon},${maxlat},${maxlon});(._;>;);out body;`; const request_uri = `https://www.overpass-api.de/api/interpreter?data=[out:json];way[highway](${minlat},${minlon},${maxlat},${maxlon});(._;>;);out body;`;

View File

@ -2,18 +2,18 @@ import * as SP from "./ShortestPath";
import {sill} from "./Debug"; import {sill} from "./Debug";
import {get_row} from "./Misc"; 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==========`); sill(`==========PathBench==========`);
let start_time, end_time; let start_time, end_time;
let res; let res;
//benchmark Obvious-Dijkstra //benchmark Obvious-Dijkstra
start_time = performance.now(); 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(); end_time = performance.now();
sill(`Obvious-Dijkstra run-time: ${end_time - start_time} ms`); sill(`Obvious-Dijkstra run-time: ${end_time - start_time} ms`);
// benchmark Dijkstra // benchmark Dijkstra
start_time = performance.now(); 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(); end_time = performance.now();
sill(`Dijkstra run-time: ${end_time - start_time} ms`); sill(`Dijkstra run-time: ${end_time - start_time} ms`);
sill(`==============================`); sill(`==============================`);

View File

@ -24,23 +24,15 @@ function __haversine_distance(p1, p2) {
* Use Dijkstra algorithm to find the shortest path. * Use Dijkstra algorithm to find the shortest path.
* @param nodes node index list * @param nodes node index list
* @param ways unused in this implementation * @param ways unused in this implementation
* @param loc unused in this implementation
* @param ch adjacent table * @param ch adjacent table
* @param count unused in this implementation * @param count unused in this implementation
* @param aff unused in this implementation * @param aff unused in this implementation
* @param u start node id * @param u start node id
* @param p destination 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}`); // 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 dis = {};
const fa = {}; const fa = {};
const vis = new Set(); const vis = new Set();
@ -56,9 +48,9 @@ function __dijkstra(nodes, ways, ch, count, aff, u, p) {
vis.add(v); vis.add(v);
const t = ch[v].length; const t = ch[v].length;
for (let j = 0; j < t; ++j) { for (let j = 0; j < t; ++j) {
const c = ch[v][j]; const [c, w] = ch[v][j];
if (!nodes[c]) continue; if (!nodes[c]) continue;
const w = get_weight(v, c); // const w = get_weight(v, c);
if (!dis[c] || d + w < dis[c]) { if (!dis[c] || d + w < dis[c]) {
dis[c] = d + w; dis[c] = d + w;
pq.push([dis[c], c]); 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. * Use Dijkstra algorithm with Obvious optimization to find the shortest path.
* @param nodes node index list * @param nodes node index list
* @param ways node list for each way * @param ways node list for each way
* @param loc relative location in the way
* @param ch adjacent table * @param ch adjacent table
* @param count determine isolated nodes * @param count determine isolated nodes
* @param aff affiliation of isolated nodes * @param aff affiliation of isolated nodes
* @param u start node id * @param u start node id
* @param p destination 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}`); // 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 dis = {};
const fa = {}; const fa = {};
const vis = new Map(); const vis = new Map();
@ -140,37 +105,15 @@ function __obvious_dijkstra(nodes, ways, ch, count, aff, u, p) {
} }
// sill('loop'); // sill('loop');
vis.set(v,true); vis.set(v,true);
dis[v] = d;
const t = ch[v].length; const t = ch[v].length;
for (let j = 0; j < t; ++j) { for (let j = 0; j < t; ++j) {
const c = ch[v][j]; const [c, w] = ch[v][j];
if (!nodes[c]) continue; 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); // sill(w);
if (w > 0 && (!dis[c] || d + w < dis[c])) { if ((!dis[c] || d + w < dis[c])) {
dis[c] = d + w; dis[c] = d + w;
pq.push([dis[c], c]); pq.push([dis[c], c]);
const v_oc = way.indexOf(parseInt(v)); fa[c] = 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;
}
}
} }
} }
} }
@ -179,14 +122,32 @@ function __obvious_dijkstra(nodes, ways, ch, count, aff, u, p) {
const res = [p]; const res = [p];
vis.clear(); vis.clear();
while (fa[curr]) { while (fa[curr]) {
curr = fa[curr].toString(); const prev = curr;
curr = fa[curr];
if (vis.has(curr)) { if (vis.has(curr)) {
sill(`Cycle at ${curr}`); sill(`Cycle at ${curr}`);
// sill(res); // sill(res);
break; break;
} }
vis.set(curr,true); 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."); // sill("finished Obvious Dijkstra.");
return res; return res;