From 901921fc452630d33cc6c1547f56d6e455d89443 Mon Sep 17 00:00:00 2001 From: David Manthey Date: Tue, 10 Dec 2024 15:57:31 -0500 Subject: [PATCH] perf: Speed up hit tests for points and markers If the range tree returns multiple points or markers that might be within the search area, we were sorting them to maintain a stable response order. Javascript sort defaults to a lexical sort which casts all values to strings, so we had been doing `points.sort((a, b) => a - b)` to do a numerical sort, but this has a lot of function call overhead and probably is casting each value between strings and numeric datatypes. Instead, we do `points = Uint32Array.from(points).sort();` which uses native javascript functions and is much faster. --- src/markerFeature.js | 18 +++++++++++------- src/pointFeature.js | 4 ++-- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/markerFeature.js b/src/markerFeature.js index 8f943ab010..e87a0e48f2 100644 --- a/src/markerFeature.js +++ b/src/markerFeature.js @@ -228,7 +228,7 @@ var markerFeature = function (arg) { // Find markers inside the bounding box idx = m_rangeTree.range(min.x, min.y, max.x, max.y); - idx.sort((a, b) => a - b); + idx = Uint32Array.from(idx).sort(); // Filter by circular region idx.forEach(function (i) { var d = data[i], @@ -353,16 +353,20 @@ var markerFeature = function (arg) { }; // Find markers inside the bounding box. Only these could be in the polygon idx = m_rangeTree.range(min.x, min.y, max.x, max.y); - // sort by index - idx.sort((a, b) => a - b); + /* sort by index. This had been + * idx.sort((a, b) => a - b); + * but this requires continual casting from int to str and back, so using + * a Uint32Array is faster, though potentially limits the maximum number of + * markers. */ + idx = Uint32Array.from(idx).sort(); // filter markers within the polygon idx.forEach(function (i) { var d = data[i]; let p = m_this.position()(d, i); - let rad = radius(data[i], i), - swz = scaleWithZoom(data[i], i); - const so = strokeOffset(data[i], i), - s = swz ? strokeWidth(data[i], i) : 0; + let rad = radius(d, i), + swz = scaleWithZoom(d, i); + const so = strokeOffset(d, i), + s = swz ? strokeWidth(d, i) : 0; let ris = radiusIncludesStroke(d, i); ris = ris === undefined ? true : ris; const rwos = ris ? rad + s * (so - 1) / 2 : rad; // radius without stroke diff --git a/src/pointFeature.js b/src/pointFeature.js index 75cf2f46f6..45f5f4c092 100644 --- a/src/pointFeature.js +++ b/src/pointFeature.js @@ -329,7 +329,7 @@ var pointFeature = function (arg) { // Find points inside the bounding box idx = m_rangeTree.range(min.x, min.y, max.x, max.y); - idx.sort((a, b) => a - b); + idx = Uint32Array.from(idx).sort(); // Filter by circular region idx.forEach(function (i) { var d = data[i], @@ -434,7 +434,7 @@ var pointFeature = function (arg) { // Find points inside the bounding box. Only these could be in the polygon idx = m_rangeTree.range(min.x, min.y, max.x, max.y); // sort by index - idx.sort((a, b) => a - b); + idx = Uint32Array.from(idx).sort(); // filter points within the polygon idx.forEach(function (i) { var d = data[i],