LUCENE-6325: use array for number -> FieldInfo lookup, except in very sparse cases

git-svn-id: https://svn.apache.org/repos/asf/lucene/dev/trunk@1687789 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Michael McCandless 2015-06-26 15:54:03 +00:00
parent 75c60be660
commit 42fdbbeb95
2 changed files with 36 additions and 4 deletions

View File

@ -225,6 +225,10 @@ Optimizations
with a filter of "baz" will internally leapfrog foo,bar,baz as one with a filter of "baz" will internally leapfrog foo,bar,baz as one
conjunction. (Ryan Ernst, Robert Muir, Adrien Grande) conjunction. (Ryan Ernst, Robert Muir, Adrien Grande)
* LUCENE-6325: Reduce RAM usage of FieldInfos, and speed up lookup by
number, by using an array instead of TreeMap except in very sparse
cases (Robert Muir, Mike McCandless)
Build Build
* LUCENE-6518: Don't report false thread leaks from IBM J9 * LUCENE-6518: Don't report false thread leaks from IBM J9

View File

@ -25,6 +25,8 @@ import java.util.Map;
import java.util.SortedMap; import java.util.SortedMap;
import java.util.TreeMap; import java.util.TreeMap;
import org.apache.lucene.util.ArrayUtil;
/** /**
* Collection of {@link FieldInfo}s (accessible by number or by name). * Collection of {@link FieldInfo}s (accessible by number or by name).
* @lucene.experimental * @lucene.experimental
@ -38,7 +40,10 @@ public class FieldInfos implements Iterable<FieldInfo> {
private final boolean hasNorms; private final boolean hasNorms;
private final boolean hasDocValues; private final boolean hasDocValues;
private final SortedMap<Integer,FieldInfo> byNumber = new TreeMap<>(); // used only by fieldInfo(int)
private final FieldInfo[] byNumberTable; // contiguous
private final SortedMap<Integer,FieldInfo> byNumberMap; // sparse
private final HashMap<String,FieldInfo> byName = new HashMap<>(); private final HashMap<String,FieldInfo> byName = new HashMap<>();
private final Collection<FieldInfo> values; // for an unmodifiable iterator private final Collection<FieldInfo> values; // for an unmodifiable iterator
@ -54,6 +59,7 @@ public class FieldInfos implements Iterable<FieldInfo> {
boolean hasNorms = false; boolean hasNorms = false;
boolean hasDocValues = false; boolean hasDocValues = false;
TreeMap<Integer, FieldInfo> byNumber = new TreeMap<>();
for (FieldInfo info : infos) { for (FieldInfo info : infos) {
if (info.number < 0) { if (info.number < 0) {
throw new IllegalArgumentException("illegal field number: " + info.number + " for field " + info.name); throw new IllegalArgumentException("illegal field number: " + info.number + " for field " + info.name);
@ -84,6 +90,22 @@ public class FieldInfos implements Iterable<FieldInfo> {
this.hasNorms = hasNorms; this.hasNorms = hasNorms;
this.hasDocValues = hasDocValues; this.hasDocValues = hasDocValues;
this.values = Collections.unmodifiableCollection(byNumber.values()); this.values = Collections.unmodifiableCollection(byNumber.values());
Integer max = byNumber.isEmpty() ? null : Collections.max(byNumber.keySet());
// Only usee TreeMap in the very sparse case (< 1/16th of the numbers are used),
// because TreeMap uses ~ 64 (32 bit JVM) or 120 (64 bit JVM w/o compressed oops)
// overall bytes per entry, but array uses 4 (32 bit JMV) or 8
// (64 bit JVM w/o compressed oops):
if (max != null && max < ArrayUtil.MAX_ARRAY_LENGTH && max < 16L*byNumber.size()) {
byNumberMap = null;
byNumberTable = new FieldInfo[max+1];
for (Map.Entry<Integer,FieldInfo> entry : byNumber.entrySet()) {
byNumberTable[entry.getKey()] = entry.getValue();
}
} else {
byNumberMap = byNumber;
byNumberTable = null;
}
} }
/** Returns true if any fields have freqs */ /** Returns true if any fields have freqs */
@ -123,8 +145,7 @@ public class FieldInfos implements Iterable<FieldInfo> {
/** Returns the number of fields */ /** Returns the number of fields */
public int size() { public int size() {
assert byNumber.size() == byName.size(); return byName.size();
return byNumber.size();
} }
/** /**
@ -157,7 +178,14 @@ public class FieldInfos implements Iterable<FieldInfo> {
if (fieldNumber < 0) { if (fieldNumber < 0) {
throw new IllegalArgumentException("Illegal field number: " + fieldNumber); throw new IllegalArgumentException("Illegal field number: " + fieldNumber);
} }
return byNumber.get(fieldNumber); if (byNumberTable != null) {
if (fieldNumber >= byNumberTable.length) {
return null;
}
return byNumberTable[fieldNumber];
} else {
return byNumberMap.get(fieldNumber);
}
} }
static final class FieldNumbers { static final class FieldNumbers {