Skip to content

Rebuild the marker spatial index less, and build it with quickselect - #19

Closed
wf9a5m75 wants to merge 1 commit into
developfrom
perf/marker-spatial-index
Closed

wf9a5m75 wants to merge 1 commit into
developfrom
perf/marker-spatial-index

Conversation

@wf9a5m75

@wf9a5m75 wf9a5m75 commented Nov 5, 2026

Copy link
Copy Markdown
Contributor

MapConductor/js-sdk-core#20 と同じ修正の iOS 版。

js-sdk-core#20 との対応

js-sdk-core#20 iOS
pointerdown でドラッグ可能なマーカーが無ければ引き当てない 対象外。iOS のドラッグ開始は長押し(DefaultMarkerEventController.handleLongPress)で、タッチのたびに空間インデックスを引かない
HexCellRegistry:セルの集合が変わったときだけ作り直しの印を付ける・既にあるセルは置き換えない このPR
KDTree:quickselect で構築 このPR

不具合

HexCellRegistry は setPoint / removePoint のたびに K-D 木の作り直しの印を付けていた。位置の変わらない更新や、既にあるセルへの追加でも同じで、次の空間検索(タイル方式のマーカーのタップ判定など)が全セルの再構築を待つ。MarkerManager.updateEntity は毎回 setPoint を呼ぶので、アイコンの差し替えなどでも当たる。

KDTree の構築は、階層ごとにセルの配列を並べ替えて複製していた。

修正

  • HexCellRegistry:K-D 木が持つのはセルなので、セルの集合が変わったときだけ作り直しの印を付ける。同じセルのままなら何もしない。既にあるセルは置き換えず、返すのもそのセル(木が持っているセルと揃える)。removePoint も、セルに他のマーカーが残っていれば印を付けない
  • KDTree:1 本の配列の中で中央値だけを選んで(Wirth の quickselect)、添字の範囲で再帰する。六角セルの中心は列ごとに同じ x を持ち、等しい値が大量に並ぶので、左右から寄せる分割にした

公開 API の変更は無い(api/MapConductorCore.api.swift は変わらない)。

確認

  • xcodebuild build-for-testing -destination 'generic/platform=iOS':通る
  • シミュレータではテストを流せていない(手元の CoreSimulator が Xcode より古く、更新に管理者権限が要る)。代わりに geocell/ のソースをそのまま macOS 向けの SwiftPM パッケージに入れ(MarkerEntity / MarkerState は id と position だけのスタブ)、以下を流した
    • 追加した MarkerSpatialIndexTests(8 件)と既存の KDTreeRadiusTests:すべて通る
      • nearest / withinRadiusWithDistance / nearestKWithDistance が総当たりと一致する(同じ座標が並ぶ 40×40 の格子・ばらばらの 3,000 点)
      • 位置の変わらない更新・同じセルへの追加・同じセル内の 1 件削除では作り直しを要求しない。移動・セルが空になる削除では要求する
      • 更新・移動・削除・同じ座標への追加を重ねたあとも、最初から作り直したレジストリと同じ答えを返す
    • develop のソースに戻すと、作り直しを要求しないことを見る 3 件が落ちる
  • K-D 木の構築(6 万セル、並びをシャッフル、Mac・リリースビルド、10 回平均):136.7 ms → 12.8 ms。実機では測っていない

HexCellRegistry marked the K-D tree dirty on every setPoint and removePoint,
including updates that left the marker in the same cell and additions to a
cell that already existed. The tree holds cells, not markers, so the next
spatial query rebuilt it over every cell for nothing. It is now marked dirty
only when the set of cells changes, and an existing cell is not replaced, so
the cell returned is the one the tree holds.

KDTree sorted a copy of the cells at every level. It now selects only the
median inside one array (Wirth's quickselect) and recurses on index ranges.
Hex cell centres share x per column, so the partition closes in from both
ends; a one-sided partition is O(n^2) on equal keys.

Same change as MapConductor/js-sdk-core#20. android-sdk-core already had
the registry part.
@wf9a5m75

wf9a5m75 commented Nov 5, 2026

Copy link
Copy Markdown
Contributor Author

K-D 木は f6d85c8 で MarkerGridIndex に置き換え済みで、HexCellRegistry/KDTree はマーカー検索で使われていないため、この最適化は不要。K-D 木そのものの削除は別 PR で行う。

@wf9a5m75 wf9a5m75 closed this Nov 5, 2026
@wf9a5m75
wf9a5m75 deleted the perf/marker-spatial-index branch November 5, 2026 02:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant