-
Type: Improvement
-
Resolution: Unresolved
-
Priority: Major - P3
-
None
-
Affects Version/s: None
-
Component/s: Performance
-
None
-
Java Drivers
Mongot is currently adding support for sorting on ObjectID fields, but the currently implementation of BsonObjectId#compareTo(BsonObjectId other) requires allocating two arrays, serializing the objectId, and then comparing the values one byte at a time. It should be equivalent and more-efficient to do Integer.compareUnsigned on the fields without allocating arrays.
I also want to propose changing the representation of ObjectId in memory from (int, int, short, int) to (int, long). This yields greater speedups because sort only requires two comparisons, and it's faster to serialize/deserialize.
Simplified Implementation (lightly tested):
private final int timestamp; private final long nonce; public FastObjectId(final byte[] bytes) { this( ByteBuffer.wrap( isTrueArgument("bytes has length of 12", bytes, notNull("bytes", bytes).length == 12))); } public FastObjectId(final ByteBuffer buffer) { notNull("buffer", buffer); isTrueArgument("buffer.remaining() >=12", buffer.remaining() >= OBJECT_ID_LENGTH); ByteOrder originalOrder = buffer.order(); buffer.order(ByteOrder.BIG_ENDIAN); timestamp = buffer.getInt(); nonce = buffer.getLong(); buffer.order(originalOrder); } public byte[] toByteArray() { return ByteBuffer.allocate(OBJECT_ID_LENGTH) .putInt(timestamp) .putLong(nonce) .array(); } public void putToByteBuffer(final ByteBuffer buffer) { notNull("buffer", buffer); isTrueArgument("buffer.remaining() >=12", buffer.remaining() >= OBJECT_ID_LENGTH); ByteOrder original = buffer.order(); buffer.order(ByteOrder.BIG_ENDIAN) .putInt(timestamp) .putLong(nonce) .order(original); } @Override public boolean equals(final Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } FastObjectId other = (FastObjectId) o; if (nonce != other.nonce) { return false; } return timestamp == other.timestamp; } @Override public int hashCode() { return 31 * timestamp + Long.hashCode(nonce); } @Override public int compareTo(final BaseObjectId other) { if (other == null) { throw new NullPointerException(); } int cmp = Integer.compareUnsigned(this.timestamp, other.timestamp); if (cmp != 0) { return cmp; } return Long.compareUnsigned(nonce, other.nonce); }
Benchmark | Mode | Cnt | Score | Error Units |
---|---|---|---|---|
ObjectIdBenchTest.constructor_baseline | thrpt | 15 | 195350828.155 | ± 350649.426 ops/s |
ObjectIdBenchTest.constructor_fast | thrpt | 15 | 315605848.191 | ± 2235589.879 ops/s |
ObjectIdBenchTest.sort_baseline | thrpt | 15 | 18.372 | ± 0.720 ops/s |
ObjectIdBenchTest.sort_fast | thrpt | 15 | 180.964 | ± 6.588 ops/s |
ObjectIdBenchTest.toByteArray_fast | thrpt | 15 | 261826671.855 | ± 2532613.428 ops/s |
ObjectIdBenchTest.toByteArray_baseline | thrpt | 15 | 186318524.055 | ± 2391487.317 ops/s |