Skip to content

Commit 85e87f5

Browse files
stefanvoditaStefan Vodita
authored andcommitted
Introduce growInRange to reduce array overallocation
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 da69346 commit 85e87f5

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)