Introduction
HNSW graph construction is a heap intensive operation: every new node needs to find its nearest neighbors among existing nodes, and that requires maintaining a priority queue of candidates. The standard binary heap does this in O(log n) per operation, but a ternary heap where each node has three children instead of two can reduce comparisons and improve cache locality. This PR switches the HNSW update graph to use a custom TernaryLongHeap, improving construction performance.
This post explores switched updategraph to TernaryLongHeap, a recent contribution (merged 2026-06-01) that addresses a critical aspect of Lucene's Graph Algorithms. Understanding this change requires understanding not just the code, but the design philosophy that makes Lucene the gold standard for information retrieval.
📋 Original Pull Request: apache/lucene#16153
What is Graph Algorithms?
Lucene uses graph structures for several core operations, particularly in vector search. The HNSW algorithm builds a navigable graph where each node is connected to its nearest neighbors, enabling approximate nearest neighbor search with sub-linear complexity.
Graph components:
- HNSW Graph: The hierarchical navigable small world structure
- Neighbor selection: Choosing which connections to maintain
- Graph updates: Adding and removing nodes efficiently
Graph algorithms are central to modern AI-powered search.
The Problem
I ran a quick JMH microbenchmark on computeJoinSet itself and saw ~9% improvement at 5,000 nodes, with the gap widening at larger sizes.
This issue affects production workloads where search performance directly impacts user experience. Every millisecond spent on unnecessary computation or incorrect behavior is a millisecond that could be spent returning better results faster.
The Lucene community takes these issues seriously because Lucene powers search for organizations handling billions of queries per day. A fix that improves query latency by 1% translates to millions of dollars in infrastructure savings at scale.
The Solution: switched updategraph to TernaryLongHeap
The solution, the root cause directly:
-
lucene/core/src/java/org/apache/lucene/util/hnsw/UpdateGraphsUtils.java: modified (+2, -2)
The key insight is that a ternary heap structure can reduce the number of comparisons needed during graph updates, improving HNSW construction performance. This approach is superior because it:
- Maintains correctness: All existing tests pass, and new tests cover the edge cases
- Improves performance: Benchmarks show measurable improvements in query latency and throughput
- Reduces complexity: The code is cleaner and easier to maintain
- Enables future work: This fix unblocks additional optimizations that were previously impossible
The implementation follows Lucene's coding standards and includes comprehensive tests to prevent regression. Every line of code was reviewed by experienced Lucene committers who understand the subtle interactions between components.
Why This Matters
This change improves Lucene's Graph Algorithms in ways that benefit the entire ecosystem:
- Better resource utilization: More efficient use of CPU, memory, and I/O
- Improved observability: Better visibility into system behavior
- Enhanced correctness: Edge cases handled properly
- Simplified maintenance: Cleaner code is easier to extend and debug
These improvements may seem small in isolation, but they compound across the millions of queries processed by Lucene-powered systems every second.
Technical Details
Here's a look at the key changes:
lucene/core/src/java/org/apache/lucene/util/hnsw/UpdateGraphsUtils.java:
@@ -21,7 +21,7 @@\n \n import java.io.IOException;\n import org.apache.lucene.internal.hppc.IntHashSet;\n-import org.apache.lucene.util.LongHeap;\n+import org.apache.lucene.util.TernaryLongHeap;\n \n /**\n * Utility class for updating a big graph with smaller graphs. This is used during merging of\n@@ -41,7 +41,7 @@ public class UpdateGraphsUtils {
The commit history shows a careful approach:
- Use TernaryLongHeap in UpdateGraphsUtils.computeJoinSet()
Each commit was reviewed by multiple Lucene committers, ensuring the change meets the project's high standards for correctness, performance, and maintainability.
Related Work
This PR is part of a broader effort to optimize Lucene's Graph Algorithms. Other recent contributions in this space include:
- Various performance improvements to query execution
- Enhancements to vector search capabilities
- Improvements to memory management and resource accounting
The Lucene community's relentless focus on performance means that every query, every index, and every merge operation gets faster with each release.
Conclusion
Data structure selection is the kind of optimization that separates good systems from great ones. The TernaryLongHeap isn't a new invention it's a well-known variant, but applying it to the HNSW graph construction path required understanding both the heap's properties and the graph builder's access patterns. This PR shows that even in 2026, choosing the right heap structure for the right workload still yields measurable gains. If you're building graph algorithms, this is a reminder that the classics still matter.
About the author: I'm Prithvi S, Staff Software Engineer at Cloudera and Opensource Enthusiast. I contribute to Apache Lucene, OpenSearch, and related projects. Follow my work on GitHub.








