Store $geoNear StoredGeometry by value to avoid per-geometry heap allocation in extractGeometries

XMLWordPrintableJSON

    • Type: Improvement
    • Resolution: Won't Fix
    • Priority: Minor - P4
    • None
    • Affects Version/s: None
    • Component/s: Query Execution
    • None
    • Query Execution
    • QE 2026-08-03, QE 2026-08-17
    • 200
    • None
    • None
    • None
    • None
    • None
    • None
    • None

      Overview

      On the classic $geoNear hot path, StoredGeometry::extractGeometries() (src/mongo/db/geo/geometry_container.cpp) allocates one StoredGeometry on the heap per extracted geometry (via std::unique_ptr) and collects them into a std::vector[std::unique\_ptr<StoredGeometry|std::unique_ptr<StoredGeometry]>. Both allocations happen once per candidate document in computeGeoNearDistance(). Holding StoredGeometry by value in a small inline vector removes both allocations.

      Background: headroom analysis

      Flamegraph analysis of the classic 2dsphere workload (on-CPU, drift-invariant inclusive %):
      * computeGeoNearDistance = 22.1% (2dsphere), 15.5% (2d)
      * extractGeometries = 6.78% (2dsphere), 6.53% (2d), of which:
      ** alloc/free = 2.15% / 2.58% (the avoidable part: unique_ptr + set node + vector)
      ** parseFromStorage = 2.99% (inherent S2 geometry parsing, not avoidable)
      ** allocation inside the BSONElementSet (std::set) itself = only 0.28%

      The std::set is mostly comparator/tree work, not allocation, so replacing it recovers only ~0.3% and requires an invasive rewrite of multikey path extraction; it is intentionally left alone. The real avoidable cost is the per-geometry unique_ptr heap allocation.

      Proposed Change

      * Add a defaulted move constructor and move assignment to GeometryContainer. It currently declares a copy constructor, which suppresses the implicit move constructor; without an explicit move, storing StoredGeometry by value would deep-clone the geometry via clonable_ptr on every vector growth (worse than the status quo). All members (clonable_ptr, unique_ptr, BSONElement) are movable, so = default is safe. This is a general improvement in its own right.
      * Change StoredGeometry::extractGeometries to collect into absl::InlinedVector by value (new StoredGeometry::Vector type alias), removing the per-geometry unique_ptr allocation and the vector heap allocation for the common single/double-geometry case.
      * Convert StoredGeometry::parseFrom from a static factory returning a heap pointer into a member bool parseFrom(...) that parses in place (emplace_back().parseFrom(), pop_back() on failure).
      * Update the two callers (src/mongo/db/exec/classic/geo_near.cpp and src/mongo/db/exec/agg/internal_compute_geo_near_distance_stage.cpp) to iterate by value.

      Prototype Data

      Prototyped and measured locally (classic engine forced, Standalone inMemory, ARM) on Aggregation.GeoNear2dSphere / Aggregation.GeoNear2d. Because the measured effect is small and the host exhibited 15-20% performance drift across the session, the flamegraph (drift-invariant proportions) is the reliable signal, not raw ops/sec.

      Flamegraph, baseline -> prototype:
      * 2dsphere: computeGeoNearDistance 22.10% -> 20.98%; extractGeometries 6.78% -> 5.42%; extractGeometries alloc/free 2.15% -> 1.48%
      * 2d: computeGeoNearDistance 15.49% -> 14.14%; extractGeometries 6.53% -> 5.43%; extractGeometries alloc/free 2.58% -> 1.68%

      Net: approximately 1.1-1.4 percentage points of total CPU removed (~0.7-0.9pp of it allocation). Interleaved ops/sec A/B was inconclusive (round 1 +5.2%, round 2 +0.2%), i.e. at/below the noise floor on the drifting host.

      Correctness: a custom harness verified an exact result signature (ids + distances) identical to baseline for both 2d and 2dsphere.

      Relationship to SERVER-132107

      This is a separate, much smaller optimization in the same function than SERVER-132107 (defer point-metadata Value construction, ~+20-24% on 2dsphere). This change targets the extraction allocations rather than the metadata Value. They are independent and can be reviewed/merged separately.

      Scope of Work

      * src/mongo/db/geo/geometry_container.h — add GeometryContainer move ctor/assignment; add StoredGeometry::Vector; make parseFrom a member.
      * src/mongo/db/geo/geometry_container.cpp — rewrite parseFrom/extractGeometries to parse in place by value.
      * src/mongo/db/exec/classic/geo_near.cpp, src/mongo/db/exec/agg/internal_compute_geo_near_distance_stage.cpp — iterate by value.

      Acceptance Criteria

      * $geoNear results and distance/point metadata are unchanged.
      * No per-geometry heap allocation in extractGeometries for the common (inline-capacity) case.
      * Existing $geoNear correctness tests pass.

      Technical Notes

      * The prototype patch is attached to this ticket.
      * Since this touches the query hot path, a sys-perf patch is recommended before merge.

            Assignee:
            Jan Steemann
            Reporter:
            Jan Steemann
            Votes:
            0 Vote for this issue
            Watchers:
            1 Start watching this issue

              Created:
              Updated:
              Resolved: