Skip to content

Commit 87c6246

Browse files
committed
Binary search in searchFirst/last and BoundSliceBench
1 parent c670c81 commit 87c6246

6 files changed

Lines changed: 77 additions & 34 deletions

File tree

bench.sh

Lines changed: 0 additions & 1 deletion
This file was deleted.

src-java/datascript/SortedSet.java

Lines changed: 23 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -119,19 +119,15 @@ public Seq rslice(Object from, Object to, Comparator cmp) {
119119

120120
while (true) {
121121
if (node instanceof Node) {
122-
int idx = node.searchFirstBigger(from, cmp);
123-
if (idx == node._len) --idx; // beyond last, clamp to last
124-
if (idx == -1) idx = 0; // smaller than 0.maxKey, still could be first
122+
int idx = node.searchLast(from, cmp) + 1;
123+
if (idx == node._len) --idx; // last or beyond, clamp to last
125124
seq = new Seq(null, seq, node, idx, null, null, false);
126125
node = seq.child();
127126
} else { // Leaf
128127
int idx = node.searchLast(from, cmp);
129128
if (idx == -1) { // not in this, so definitely in prev
130129
seq = new Seq(null, seq, node, 0, to, cmp, false);
131130
return seq.advance() ? seq : null;
132-
} else if (idx < 0) { // one before insertion point
133-
seq = new Seq(null, seq, node, (-idx-1)-1, to, cmp, false);
134-
return seq.over() ? null : seq;
135131
} else { // exact match
136132
seq = new Seq(null, seq, node, idx, to, cmp, false);
137133
return seq.over() ? null : seq;
@@ -296,6 +292,8 @@ Leaf newLeaf(int len, Edit edit) {
296292
}
297293

298294
int search(Object key, Comparator cmp) {
295+
// return Arrays.binarySearch(_keys, 0, _len, key, cmp);
296+
299297
int low = 0, high = _len;
300298
while (high - low > 16) {
301299
int mid = (high + low) >>> 1;
@@ -306,7 +304,7 @@ int search(Object key, Comparator cmp) {
306304
}
307305

308306
// linear search
309-
for(int i = low; i < high; ++i) {
307+
for (int i = low; i < high; ++i) {
310308
int d = cmp.compare(_keys[i], key);
311309
if (d == 0) return i;
312310
else if (d > 0) return -i-1; // i
@@ -315,28 +313,29 @@ int search(Object key, Comparator cmp) {
315313
}
316314

317315
int searchFirst(Object key, Comparator cmp) {
318-
for(int i = 0; i < _len; ++i) {
319-
int d = cmp.compare(_keys[i], key);
320-
if (d == 0) return i;
321-
else if (d > 0) return -i-1; // i
316+
int low = 0, high = _len;
317+
while (low < high) {
318+
int mid = (high + low) >>> 1;
319+
int d = cmp.compare(_keys[mid], key);
320+
if (d < 0)
321+
low = mid + 1;
322+
else
323+
high = mid;
322324
}
323-
return -_len-1; // _len
324-
}
325-
326-
int searchFirstBigger(Object key, Comparator cmp) {
327-
for(int i = _len-1; i >= 0; --i)
328-
if (cmp.compare(_keys[i], key) <= 0)
329-
return i+1;
330-
return -1;
325+
return low;
331326
}
332327

333328
int searchLast(Object key, Comparator cmp) {
334-
for(int i = _len-1; i >= 0; --i) {
335-
int d = cmp.compare(_keys[i], key);
336-
if (d == 0) return i;
337-
else if (d < 0) return -i-2; // i+1
329+
int low = 0, high = _len;
330+
while (low < high) {
331+
int mid = (high + low) >>> 1;
332+
int d = cmp.compare(_keys[mid], key);
333+
if (d <= 0)
334+
low = mid + 1;
335+
else
336+
high = mid;
338337
}
339-
return -1; // 0
338+
return low - 1;
340339
}
341340

342341
boolean contains(Object key, Comparator cmp) {

test-java/bench.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
test-java/javac.sh && java -ea -cp `clojure -A:datomic -Spath`:target/classes datascript.Bench

test-java/datascript/Bench.java

Lines changed: 51 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,29 @@ void run() {
105105
}
106106
}
107107

108+
class BoundSliceBench extends ABench {
109+
ISortedSet set;
110+
111+
BoundSliceBench(Class<? extends ISortedSet> setClass, Collection source, boolean asTransient, int maxLen) {
112+
super(setClass, source, asTransient, maxLen);
113+
set = newSet(source);
114+
}
115+
116+
void run() {
117+
int from = 1, to = 999999, expected = from;
118+
ISeq seq = set.slice(from, to);
119+
Integer value = -1;
120+
while (seq != null) {
121+
value = (Integer) seq.first();
122+
assert value.intValue() == expected : "Expected " + expected + ", got " + value;
123+
++expected;
124+
seq = seq.next();
125+
}
126+
assert value.intValue() == to : "Expected " + to + ", got " + value;
127+
}
128+
}
129+
130+
108131
class RemoveBench extends ABench {
109132
ISortedSet set;
110133
List<Integer> removes;
@@ -135,15 +158,15 @@ abstract class APersistentCollection<T> implements ISortedSet {
135158

136159
abstract APersistentCollection<T> create(T _impl);
137160

138-
public ISortedSet with(Object o) { return create((T) ((IPersistentCollection) _impl).cons(o)); }
139-
public ISortedSet without(Object o) { return create((T) ((IPersistentSet) _impl).disjoin(o)); }
140-
public int size() { return ((IPersistentCollection) _impl).count(); }
141-
public boolean contains(Object o) { return ((IPersistentSet) _impl).contains(o); }
161+
public ISortedSet with(Object o) { return create((T) ((IPersistentCollection) _impl).cons(o)); }
162+
public ISortedSet without(Object o) { return create((T) ((IPersistentSet) _impl).disjoin(o)); }
163+
public int size() { return ((IPersistentCollection) _impl).count(); }
164+
public boolean contains(Object o) { return ((IPersistentSet) _impl).contains(o); }
142165

143-
public Iterator iterator() { return ((Iterable) _impl).iterator(); }
144-
public ISortedSet toTransient() { return create((T) ((IEditableCollection) _impl).asTransient()); }
145-
public ISortedSet toPersistent() { return create((T) ((ITransientCollection) _impl).persistent()); }
146-
public ISeq seq() { return ((Seqable) _impl).seq(); }
166+
public Iterator iterator() { return ((Iterable) _impl).iterator(); }
167+
public ISortedSet toTransient() { return create((T) ((IEditableCollection) _impl).asTransient()); }
168+
public ISortedSet toPersistent() { return create((T) ((ITransientCollection) _impl).persistent()); }
169+
public ISeq seq() { return ((Seqable) _impl).seq(); }
147170
}
148171

149172
class DatomicSet extends APersistentCollection<BTSet> {
@@ -166,12 +189,27 @@ class DatascriptSet extends APersistentCollection<SortedSet> {
166189
_impl = new SortedSet();
167190
}
168191
DatascriptSet create(SortedSet impl) { return new DatascriptSet(impl); }
192+
public ISeq slice(Object from, Object to) { return _impl.slice(from, to); }
169193
}
170194

195+
@SuppressWarnings("unchecked")
171196
class ClojureSet extends APersistentCollection<PersistentTreeSet> {
197+
static IFn takeWhile;
198+
static {
199+
takeWhile = (IFn) Clojure.var("clojure.core", "take-while");
200+
}
201+
172202
ClojureSet(PersistentTreeSet impl) { _impl = impl; }
173203
ClojureSet(int ml) { _impl = PersistentTreeSet.create(RT.DEFAULT_COMPARATOR, null); }
174204
ClojureSet create(PersistentTreeSet impl) { return new ClojureSet(impl); }
205+
public ISeq slice(Object from, Object to) {
206+
IFn pred = new AFn() {
207+
public Object invoke(Object arg) {
208+
return _impl.comparator().compare(arg, to) <= 0;
209+
}
210+
};
211+
return (ISeq) takeWhile.invoke(pred, _impl.seqFrom(from, true));
212+
}
175213
}
176214

177215
public class Bench {
@@ -250,6 +288,10 @@ public static void bench() throws Exception {
250288
runBench(SeqIterateBench.class, bigSource, ClojureSet.class);
251289
runBench(SeqIterateBench.class, bigSource, DatomicSet.class);
252290
runBench(SeqIterateBench.class, bigSource, DatascriptSet.class);
291+
292+
System.out.println("\n === Bound slice over 1M ===");
293+
runBench(BoundSliceBench.class, bigSource, ClojureSet.class);
294+
runBench(BoundSliceBench.class, bigSource, DatascriptSet.class);
253295

254296
System.out.println("\n === 100K REMOVEs ===");
255297
runBench(RemoveBench.class, source, ClojureSet.class, false);
@@ -331,6 +373,6 @@ public static void test() throws Exception {
331373

332374
public static void main(String args[]) throws Exception {
333375
bench();
334-
// test();
376+
test();
335377
}
336378
}

test-java/datascript/ISortedSet.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,5 @@ public interface ISortedSet extends Iterable, Seqable {
1212
default ISortedSet toPersistent() { throw new UnsupportedOperationException(); }
1313
default Iterator iterator() { throw new UnsupportedOperationException(); }
1414
default ISeq seq() { throw new UnsupportedOperationException(); }
15+
default ISeq slice(Object from, Object to) { throw new UnsupportedOperationException(); }
1516
}

test-java/javac.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
javac -d target/classes -cp `clojure -A:datomic -Spath` -Xlint:unchecked src-java/datascript/*.java test-java/datascript/*.java

0 commit comments

Comments
 (0)