@@ -58,6 +58,11 @@ class PointValuesWriter {
5858 + (PointValues .MAX_NUM_BYTES * BKDConfig .MAX_DIMS );
5959 }
6060
61+ /** Minimum number of values to process per chunk in the dense N-D bulk path. */
62+ private static final int MIN_VALUES_PER_CHUNK = 64 ;
63+
64+ private byte [] densePointsBuffer ;
65+
6166 PointValuesWriter (Counter bytesUsed , FieldInfo fieldInfo , SharedIndexingScratch sharedScratch ) {
6267 this .fieldInfo = fieldInfo ;
6368 this .iwBytesUsed = bytesUsed ;
@@ -137,15 +142,30 @@ void addDense1DLongValues(int firstDocID, LongValuesCursor cursor) throws IOExce
137142 commitDenseRange (firstDocID , size , ramBefore );
138143 }
139144
145+ /**
146+ * Bulk-adds dense N-dimensional packed point values from a {@link BytesRefValuesCursor}. Each value
147+ * is a pre-encoded packed byte array of {@code packedBytesLength} bytes.
148+ */
140149 void addDenseNDValues (int firstDocID , BytesRefValuesCursor cursor ) throws IOException {
141150 final int size = cursor .size ();
142151 if (size == 0 ) {
143152 return ;
144153 }
145154 final long ramBefore = reserveDenseRange (firstDocID , size );
146155 final int width = packedBytesLength ;
147- final int perChunk = SharedIndexingScratch .BYTES_SCRATCH_SIZE / width ;
148- final byte [] buffer = sharedScratch .bytesScratch ();
156+ final byte [] buffer ;
157+ final int perChunk ;
158+ if (width * MIN_VALUES_PER_CHUNK <= SharedIndexingScratch .BYTES_SCRATCH_SIZE ) {
159+ // Common case (packed <= ~64 bytes): reuse the shared 4 KiB scratch which already gives
160+ // at least MIN_VALUES_PER_CHUNK values per chunk. Matches the 1D dense path behavior.
161+ buffer = sharedScratch .bytesScratch ();
162+ perChunk = SharedIndexingScratch .BYTES_SCRATCH_SIZE / width ;
163+ } else {
164+ // Wide points: shared 4 KiB would yield < MIN_VALUES_PER_CHUNK; allocate a dedicated
165+ // larger buffer (sized for at least 64 values) and keep it for the lifetime of this writer.
166+ buffer = pointsDenseBuffer (width );
167+ perChunk = buffer .length / width ;
168+ }
149169 int remaining = size ;
150170 while (remaining > 0 ) {
151171 int chunk = Math .min (perChunk , remaining );
@@ -156,6 +176,26 @@ void addDenseNDValues(int firstDocID, BytesRefValuesCursor cursor) throws IOExce
156176 commitDenseRange (firstDocID , size , ramBefore );
157177 }
158178
179+ /**
180+ * Returns (and caches) a dedicated buffer for wide packed point values, sized to hold at least
181+ * {@code MIN_VALUES_PER_CHUNK} values. Only called when the shared scratch would result in fewer
182+ * than {@link #MIN_VALUES_PER_CHUNK} values per chunk (i.e. packed length > ~64 bytes).
183+ *
184+ * <p>The allocated size is charged to {@code iwBytesUsed}.
185+ */
186+ private byte [] pointsDenseBuffer (int packedLength ) {
187+ final int minBytes = packedLength * MIN_VALUES_PER_CHUNK ;
188+ if (densePointsBuffer == null ) {
189+ densePointsBuffer = new byte [minBytes ];
190+ iwBytesUsed .addAndGet (minBytes );
191+ } else if (densePointsBuffer .length < minBytes ) {
192+ final int old = densePointsBuffer .length ;
193+ densePointsBuffer = new byte [minBytes ];
194+ iwBytesUsed .addAndGet (minBytes - (long ) old );
195+ }
196+ return densePointsBuffer ;
197+ }
198+
159199 private void validate1DPacked (int byteWidth ) {
160200 if (fieldInfo .getPointDimensionCount () != 1 || fieldInfo .getPointNumBytes () != byteWidth ) {
161201 throw new IllegalArgumentException (
0 commit comments