Skip to content

Commit 71256cc

Browse files
authored
Specialize DisiPriorityQueue for the 2-clauses case. (#14070)
Disjunctions with 2 clauses are rather common. Specializing this case enables some shortcuts.
1 parent cab88ca commit 71256cc

File tree

9 files changed

+448
-194
lines changed

9 files changed

+448
-194
lines changed

lucene/core/src/java/org/apache/lucene/search/DisiPriorityQueue.java

Lines changed: 30 additions & 186 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,6 @@
1616
*/
1717
package org.apache.lucene.search;
1818

19-
import java.util.Arrays;
20-
import java.util.Iterator;
2119
import org.apache.lucene.util.PriorityQueue;
2220

2321
/**
@@ -27,205 +25,51 @@
2725
*
2826
* @lucene.internal
2927
*/
30-
public final class DisiPriorityQueue implements Iterable<DisiWrapper> {
31-
32-
static int leftNode(int node) {
33-
return ((node + 1) << 1) - 1;
34-
}
35-
36-
static int rightNode(int leftNode) {
37-
return leftNode + 1;
38-
}
39-
40-
static int parentNode(int node) {
41-
return ((node + 1) >>> 1) - 1;
28+
public abstract sealed class DisiPriorityQueue implements Iterable<DisiWrapper>
29+
permits DisiPriorityQueue2, DisiPriorityQueueN {
30+
31+
/** Create a {@link DisiPriorityQueue} of the given maximum size. */
32+
public static DisiPriorityQueue ofMaxSize(int maxSize) {
33+
if (maxSize <= 2) {
34+
return new DisiPriorityQueue2();
35+
} else {
36+
return new DisiPriorityQueueN(maxSize);
37+
}
4238
}
4339

44-
private final DisiWrapper[] heap;
45-
private int size;
40+
/** Return the number of entries in this heap. */
41+
public abstract int size();
4642

47-
public DisiPriorityQueue(int maxSize) {
48-
heap = new DisiWrapper[maxSize];
49-
size = 0;
50-
}
51-
52-
public int size() {
53-
return size;
54-
}
55-
56-
public DisiWrapper top() {
57-
return heap[0];
58-
}
43+
/** Return top value in this heap, or null if the heap is empty. */
44+
public abstract DisiWrapper top();
5945

6046
/** Return the 2nd least value in this heap, or null if the heap contains less than 2 values. */
61-
public DisiWrapper top2() {
62-
switch (size()) {
63-
case 0:
64-
case 1:
65-
return null;
66-
case 2:
67-
return heap[1];
68-
default:
69-
if (heap[1].doc <= heap[2].doc) {
70-
return heap[1];
71-
} else {
72-
return heap[2];
73-
}
74-
}
75-
}
47+
public abstract DisiWrapper top2();
7648

7749
/** Get the list of scorers which are on the current doc. */
78-
public DisiWrapper topList() {
79-
final DisiWrapper[] heap = this.heap;
80-
final int size = this.size;
81-
DisiWrapper list = heap[0];
82-
list.next = null;
83-
if (size >= 3) {
84-
list = topList(list, heap, size, 1);
85-
list = topList(list, heap, size, 2);
86-
} else if (size == 2 && heap[1].doc == list.doc) {
87-
list = prepend(heap[1], list);
88-
}
89-
return list;
90-
}
91-
92-
// prepend w1 (iterator) to w2 (list)
93-
private DisiWrapper prepend(DisiWrapper w1, DisiWrapper w2) {
94-
w1.next = w2;
95-
return w1;
96-
}
97-
98-
private DisiWrapper topList(DisiWrapper list, DisiWrapper[] heap, int size, int i) {
99-
final DisiWrapper w = heap[i];
100-
if (w.doc == list.doc) {
101-
list = prepend(w, list);
102-
final int left = leftNode(i);
103-
final int right = left + 1;
104-
if (right < size) {
105-
list = topList(list, heap, size, left);
106-
list = topList(list, heap, size, right);
107-
} else if (left < size && heap[left].doc == list.doc) {
108-
list = prepend(heap[left], list);
109-
}
110-
}
111-
return list;
112-
}
50+
public abstract DisiWrapper topList();
11351

114-
public DisiWrapper add(DisiWrapper entry) {
115-
final DisiWrapper[] heap = this.heap;
116-
final int size = this.size;
117-
heap[size] = entry;
118-
upHeap(size);
119-
this.size = size + 1;
120-
return heap[0];
121-
}
52+
/** Add a {@link DisiWrapper} to this queue and return the top entry. */
53+
public abstract DisiWrapper add(DisiWrapper entry);
12254

55+
/** Bulk add. */
12356
public void addAll(DisiWrapper[] entries, int offset, int len) {
124-
// Nothing to do if empty:
125-
if (len == 0) {
126-
return;
127-
}
128-
129-
// Fail early if we're going to over-fill:
130-
if (size + len > heap.length) {
131-
throw new IndexOutOfBoundsException(
132-
"Cannot add "
133-
+ len
134-
+ " elements to a queue with remaining capacity "
135-
+ (heap.length - size));
136-
}
137-
138-
// Copy the entries over to our heap array:
139-
System.arraycopy(entries, offset, heap, size, len);
140-
size += len;
141-
142-
// Heapify in bulk:
143-
final int firstLeafIndex = size >>> 1;
144-
for (int rootIndex = firstLeafIndex - 1; rootIndex >= 0; rootIndex--) {
145-
int parentIndex = rootIndex;
146-
DisiWrapper parent = heap[parentIndex];
147-
while (parentIndex < firstLeafIndex) {
148-
int childIndex = leftNode(parentIndex);
149-
int rightChildIndex = rightNode(childIndex);
150-
DisiWrapper child = heap[childIndex];
151-
if (rightChildIndex < size && heap[rightChildIndex].doc < child.doc) {
152-
child = heap[rightChildIndex];
153-
childIndex = rightChildIndex;
154-
}
155-
if (child.doc >= parent.doc) {
156-
break;
157-
}
158-
heap[parentIndex] = child;
159-
parentIndex = childIndex;
160-
}
161-
heap[parentIndex] = parent;
57+
for (int i = 0; i < len; ++i) {
58+
add(entries[offset + i]);
16259
}
16360
}
16461

165-
public DisiWrapper pop() {
166-
final DisiWrapper[] heap = this.heap;
167-
final DisiWrapper result = heap[0];
168-
final int i = --size;
169-
heap[0] = heap[i];
170-
heap[i] = null;
171-
downHeap(i);
172-
return result;
173-
}
62+
/** Remove the top entry and return it. */
63+
public abstract DisiWrapper pop();
17464

175-
public DisiWrapper updateTop() {
176-
downHeap(size);
177-
return heap[0];
178-
}
65+
/** Rebalance this heap and return the top entry. */
66+
public abstract DisiWrapper updateTop();
17967

180-
DisiWrapper updateTop(DisiWrapper topReplacement) {
181-
heap[0] = topReplacement;
182-
return updateTop();
183-
}
68+
/**
69+
* Replace the top entry with the given entry, rebalance the heap, and return the new top entry.
70+
*/
71+
abstract DisiWrapper updateTop(DisiWrapper topReplacement);
18472

18573
/** Clear the heap. */
186-
public void clear() {
187-
Arrays.fill(heap, null);
188-
size = 0;
189-
}
190-
191-
void upHeap(int i) {
192-
final DisiWrapper node = heap[i];
193-
final int nodeDoc = node.doc;
194-
int j = parentNode(i);
195-
while (j >= 0 && nodeDoc < heap[j].doc) {
196-
heap[i] = heap[j];
197-
i = j;
198-
j = parentNode(j);
199-
}
200-
heap[i] = node;
201-
}
202-
203-
void downHeap(int size) {
204-
int i = 0;
205-
final DisiWrapper node = heap[0];
206-
int j = leftNode(i);
207-
if (j < size) {
208-
int k = rightNode(j);
209-
if (k < size && heap[k].doc < heap[j].doc) {
210-
j = k;
211-
}
212-
if (heap[j].doc < node.doc) {
213-
do {
214-
heap[i] = heap[j];
215-
i = j;
216-
j = leftNode(i);
217-
k = rightNode(j);
218-
if (k < size && heap[k].doc < heap[j].doc) {
219-
j = k;
220-
}
221-
} while (j < size && heap[j].doc < node.doc);
222-
heap[i] = node;
223-
}
224-
}
225-
}
226-
227-
@Override
228-
public Iterator<DisiWrapper> iterator() {
229-
return Arrays.asList(heap).subList(0, size).iterator();
230-
}
74+
public abstract void clear();
23175
}
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
package org.apache.lucene.search;
18+
19+
import java.util.Arrays;
20+
import java.util.Collections;
21+
import java.util.Iterator;
22+
23+
/** {@link DisiPriorityQueue} of two entries or less. */
24+
final class DisiPriorityQueue2 extends DisiPriorityQueue {
25+
26+
private DisiWrapper top, top2;
27+
28+
@Override
29+
public Iterator<DisiWrapper> iterator() {
30+
if (top2 != null) {
31+
return Arrays.asList(top, top2).iterator();
32+
} else if (top != null) {
33+
return Collections.singleton(top).iterator();
34+
} else {
35+
return Collections.emptyIterator();
36+
}
37+
}
38+
39+
@Override
40+
public int size() {
41+
return top2 == null ? (top == null ? 0 : 1) : 2;
42+
}
43+
44+
@Override
45+
public DisiWrapper top() {
46+
return top;
47+
}
48+
49+
@Override
50+
public DisiWrapper top2() {
51+
return top2;
52+
}
53+
54+
@Override
55+
public DisiWrapper topList() {
56+
DisiWrapper topList = null;
57+
if (top != null) {
58+
top.next = null;
59+
topList = top;
60+
if (top2 != null && top.doc == top2.doc) {
61+
top2.next = topList;
62+
topList = top2;
63+
}
64+
}
65+
return topList;
66+
}
67+
68+
@Override
69+
public DisiWrapper add(DisiWrapper entry) {
70+
if (top == null) {
71+
return top = entry;
72+
} else if (top2 == null) {
73+
top2 = entry;
74+
return updateTop();
75+
} else {
76+
throw new IllegalStateException(
77+
"Trying to add a 3rd element to a DisiPriorityQueue configured with a max size of 2");
78+
}
79+
}
80+
81+
@Override
82+
public DisiWrapper pop() {
83+
DisiWrapper ret = top;
84+
top = top2;
85+
top2 = null;
86+
return ret;
87+
}
88+
89+
@Override
90+
public DisiWrapper updateTop() {
91+
if (top2 != null && top2.doc < top.doc) {
92+
DisiWrapper tmp = top;
93+
top = top2;
94+
top2 = tmp;
95+
}
96+
return top;
97+
}
98+
99+
@Override
100+
DisiWrapper updateTop(DisiWrapper topReplacement) {
101+
top = topReplacement;
102+
return updateTop();
103+
}
104+
105+
@Override
106+
public void clear() {
107+
top = null;
108+
top2 = null;
109+
}
110+
}

0 commit comments

Comments
 (0)