Skip to content

Commit b0ebb84

Browse files
authored
Introduce growInRange to reduce array overallocation (#12844)
In cases where we know there is an upper limit to the potential size of an array, we can use `growInRange` to avoid allocating beyond that limit.
1 parent ebf9e29 commit b0ebb84

File tree

4 files changed

+64
-7
lines changed

4 files changed

+64
-7
lines changed

lucene/CHANGES.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,9 @@ Improvements
188188

189189
Optimizations
190190
---------------------
191-
(No changes)
191+
192+
* GITHUB#12839: Introduce method to grow arrays up to a given upper limit and use it to reduce overallocation for
193+
DirectoryTaxonomyReader#getBulkOrdinals. (Stefan Vodita)
192194

193195
Bug Fixes
194196
---------------------

lucene/core/src/java/org/apache/lucene/util/ArrayUtil.java

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -330,15 +330,36 @@ public static int[] growExact(int[] array, int newLength) {
330330
return copy;
331331
}
332332

333+
/**
334+
* Returns an array whose size is at least {@code minLength}, generally over-allocating
335+
* exponentially, but never allocating more than {@code maxLength} elements.
336+
*/
337+
public static int[] growInRange(int[] array, int minLength, int maxLength) {
338+
assert minLength >= 0
339+
: "length must be positive (got " + minLength + "): likely integer overflow?";
340+
341+
if (minLength > maxLength) {
342+
throw new IllegalArgumentException(
343+
"requested minimum array length "
344+
+ minLength
345+
+ " is larger than requested maximum array length "
346+
+ maxLength);
347+
}
348+
349+
if (array.length >= minLength) {
350+
return array;
351+
}
352+
353+
int potentialLength = oversize(minLength, Integer.BYTES);
354+
return growExact(array, Math.min(maxLength, potentialLength));
355+
}
356+
333357
/**
334358
* Returns an array whose size is at least {@code minSize}, generally over-allocating
335359
* exponentially
336360
*/
337361
public static int[] grow(int[] array, int minSize) {
338-
assert minSize >= 0 : "size must be positive (got " + minSize + "): likely integer overflow?";
339-
if (array.length < minSize) {
340-
return growExact(array, oversize(minSize, Integer.BYTES));
341-
} else return array;
362+
return growInRange(array, minSize, Integer.MAX_VALUE);
342363
}
343364

344365
/**

lucene/core/src/test/org/apache/lucene/util/TestArrayUtil.java

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818

1919
import static org.apache.lucene.util.ArrayUtil.copyOfSubArray;
2020
import static org.apache.lucene.util.ArrayUtil.growExact;
21+
import static org.apache.lucene.util.ArrayUtil.growInRange;
22+
import static org.apache.lucene.util.ArrayUtil.oversize;
2123

2224
import java.util.Arrays;
2325
import java.util.Collections;
@@ -371,6 +373,36 @@ public void testGrowExact() {
371373
() -> growExact(new String[] {"a", "b", "c"}, random().nextInt(3)));
372374
}
373375

376+
public void testGrowInRange() {
377+
int[] array = new int[] {1, 2, 3};
378+
379+
// If minLength is negative, maxLength does not matter
380+
expectThrows(AssertionError.class, () -> growInRange(array, -1, 4));
381+
expectThrows(AssertionError.class, () -> growInRange(array, -1, 0));
382+
expectThrows(AssertionError.class, () -> growInRange(array, -1, -1));
383+
384+
// If minLength > maxLength, we throw an exception
385+
expectThrows(IllegalArgumentException.class, () -> growInRange(array, 1, 0));
386+
expectThrows(IllegalArgumentException.class, () -> growInRange(array, 4, 3));
387+
expectThrows(IllegalArgumentException.class, () -> growInRange(array, 5, 4));
388+
389+
// If minLength is sufficient, we return the array
390+
assertSame(array, growInRange(array, 1, 4));
391+
assertSame(array, growInRange(array, 1, 2));
392+
assertSame(array, growInRange(array, 1, 1));
393+
394+
int minLength = 4;
395+
int maxLength = Integer.MAX_VALUE;
396+
397+
// The array grows normally if maxLength permits
398+
assertEquals(
399+
oversize(minLength, Integer.BYTES),
400+
growInRange(new int[] {1, 2, 3}, minLength, maxLength).length);
401+
402+
// The array grows to maxLength if maxLength is limiting
403+
assertEquals(minLength, growInRange(new int[] {1, 2, 3}, minLength, minLength).length);
404+
}
405+
374406
public void testCopyOfSubArray() {
375407
short[] shortArray = {1, 2, 3};
376408
assertArrayEquals(new short[] {1}, copyOfSubArray(shortArray, 0, 1));

lucene/facet/src/java/org/apache/lucene/facet/taxonomy/directory/DirectoryTaxonomyReader.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -328,7 +328,8 @@ public int[] getBulkOrdinals(FacetLabel... categoryPaths) throws IOException {
328328
}
329329
// First try to find results in the cache:
330330
int[] result = new int[categoryPaths.length];
331-
int[] indexesMissingFromCache = new int[10]; // initial size, will grow when required
331+
// Will grow when required, but never beyond categoryPaths.length
332+
int[] indexesMissingFromCache = new int[Math.min(10, categoryPaths.length)];
332333
int numberOfMissingFromCache = 0;
333334
FacetLabel cp;
334335
Integer res;
@@ -352,7 +353,8 @@ public int[] getBulkOrdinals(FacetLabel... categoryPaths) throws IOException {
352353
}
353354
} else {
354355
indexesMissingFromCache =
355-
ArrayUtil.grow(indexesMissingFromCache, numberOfMissingFromCache + 1);
356+
ArrayUtil.growInRange(
357+
indexesMissingFromCache, numberOfMissingFromCache + 1, categoryPaths.length);
356358
indexesMissingFromCache[numberOfMissingFromCache++] = i;
357359
}
358360
}

0 commit comments

Comments
 (0)