|
32 | 32 | */ |
33 | 33 | public final class OnHeapHnswGraph extends HnswGraph implements Accountable { |
34 | 34 |
|
| 35 | + // shallow estimate of the statically used on-heap memory. |
| 36 | + private static final long RAM_BYTES_USED = |
| 37 | + RamUsageEstimator.shallowSizeOfInstance(OnHeapHnswGraph.class); |
| 38 | + |
35 | 39 | private static final int INIT_SIZE = 128; |
36 | 40 |
|
37 | 41 | private final AtomicReference<EntryNode> entryNode; |
@@ -83,6 +87,7 @@ public final class OnHeapHnswGraph extends HnswGraph implements Accountable { |
83 | 87 | numNodes = INIT_SIZE; |
84 | 88 | } |
85 | 89 | this.graph = new NeighborArray[numNodes][]; |
| 90 | + this.graphRamBytesUsed = RAM_BYTES_USED + RamUsageEstimator.shallowSizeOf(graph); |
86 | 91 | } |
87 | 92 |
|
88 | 93 | /** |
@@ -151,21 +156,28 @@ public void addNode(int level, int node) { |
151 | 156 | size.incrementAndGet(); |
152 | 157 | } |
153 | 158 | if (level == 0) { |
154 | | - graph[node][level] = new NeighborArray(nsize0, true); |
| 159 | + graph[node][level] = |
| 160 | + new NeighborArray( |
| 161 | + nsize0, |
| 162 | + true, |
| 163 | + l -> { |
| 164 | + assert l > 0; |
| 165 | + long bytesUsed = graphRamBytesUsed; |
| 166 | + graphRamBytesUsed = bytesUsed + l; |
| 167 | + }); |
155 | 168 | } else { |
156 | | - graph[node][level] = new NeighborArray(nsize, true); |
| 169 | + graph[node][level] = |
| 170 | + new NeighborArray( |
| 171 | + nsize, |
| 172 | + true, |
| 173 | + l -> { |
| 174 | + assert l > 0; |
| 175 | + long bytesUsed = graphRamBytesUsed; |
| 176 | + graphRamBytesUsed = bytesUsed + l; |
| 177 | + }); |
157 | 178 | nonZeroLevelSize.incrementAndGet(); |
158 | 179 | } |
159 | 180 | maxNodeId.accumulateAndGet(node, Math::max); |
160 | | - // update graphRamBytesUsed every 1000 nodes |
161 | | - if (level == 0 && node % 1000 == 0) { |
162 | | - updateGraphRamBytesUsed(); |
163 | | - } |
164 | | - } |
165 | | - |
166 | | - /** Finish building the graph. */ |
167 | | - public void finishBuild() { |
168 | | - updateGraphRamBytesUsed(); |
169 | 181 | } |
170 | 182 |
|
171 | 183 | @Override |
@@ -296,48 +308,14 @@ private void generateLevelToNodes() { |
296 | 308 | lastFreezeSize = size(); |
297 | 309 | } |
298 | 310 |
|
299 | | - /** Update the estimated ram bytes used for the neighbor array. */ |
300 | | - public void updateGraphRamBytesUsed() { |
301 | | - long currentRamBytesUsedEstimate = RamUsageEstimator.NUM_BYTES_ARRAY_HEADER; |
302 | | - for (int node = 0; node < graph.length; node++) { |
303 | | - if (graph[node] == null) { |
304 | | - continue; |
305 | | - } |
306 | | - |
307 | | - for (int i = 0; i < graph[node].length; i++) { |
308 | | - if (graph[node][i] == null) { |
309 | | - continue; |
310 | | - } |
311 | | - currentRamBytesUsedEstimate += graph[node][i].ramBytesUsed(); |
312 | | - } |
313 | | - |
314 | | - currentRamBytesUsedEstimate += RamUsageEstimator.NUM_BYTES_OBJECT_HEADER; |
315 | | - } |
316 | | - graphRamBytesUsed = currentRamBytesUsedEstimate; |
317 | | - } |
318 | | - |
| 311 | + /** |
| 312 | + * Provides an estimate of the current on-heap memory usage of the graph. This is not threadsafe, |
| 313 | + * meaning the heap utilization if building the graph concurrently may be inaccurate. The main |
| 314 | + * purpose of this method is during initial document indexing and flush. |
| 315 | + */ |
319 | 316 | @Override |
320 | 317 | public long ramBytesUsed() { |
321 | | - long total = graphRamBytesUsed; // all NeighborArray |
322 | | - total += 4 * Integer.BYTES; // all int fields |
323 | | - total += 1; // field: noGrowth |
324 | | - total += |
325 | | - RamUsageEstimator.NUM_BYTES_OBJECT_REF |
326 | | - + RamUsageEstimator.NUM_BYTES_OBJECT_HEADER |
327 | | - + 2 * Integer.BYTES; // field: entryNode |
328 | | - total += 3L * (Integer.BYTES + RamUsageEstimator.NUM_BYTES_OBJECT_HEADER); // 3 AtomicInteger |
329 | | - total += RamUsageEstimator.NUM_BYTES_OBJECT_REF; // field: cur |
330 | | - total += RamUsageEstimator.NUM_BYTES_ARRAY_HEADER; // field: levelToNodes |
331 | | - if (levelToNodes != null) { |
332 | | - total += |
333 | | - (long) (numLevels() - 1) * RamUsageEstimator.NUM_BYTES_OBJECT_REF; // no cost for level 0 |
334 | | - total += |
335 | | - (long) nonZeroLevelSize.get() |
336 | | - * (RamUsageEstimator.NUM_BYTES_OBJECT_HEADER |
337 | | - + RamUsageEstimator.NUM_BYTES_OBJECT_HEADER |
338 | | - + Integer.BYTES); |
339 | | - } |
340 | | - return total; |
| 318 | + return graphRamBytesUsed; |
341 | 319 | } |
342 | 320 |
|
343 | 321 | @Override |
|
0 commit comments