Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions docs/changelog/132456.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
pr: 132456
summary: Some optimizations for constant blocks
area: ES|QL
type: enhancement
issues: []

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -154,9 +154,13 @@ private void addRawInput(int positionOffset, IntBigArrayBlock groups, Block valu
* This method is called for count all.
*/
private void addRawInput(IntVector groups) {
for (int groupPosition = 0; groupPosition < groups.getPositionCount(); groupPosition++) {
int groupId = groups.getInt(groupPosition);
state.increment(groupId, 1);
if (groups.isConstant()) {
state.increment(groups.getInt(0), groups.getPositionCount());
} else {
for (int groupPosition = 0; groupPosition < groups.getPositionCount(); groupPosition++) {
int groupId = groups.getInt(groupPosition);
state.increment(groupId, 1);
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -178,10 +178,21 @@ static void addOrdinalInputVector(
IntVector ordinalIds,
IntVector hashIds
) {
for (int p = 0; p < groupIds.getPositionCount(); p++) {
int groupId = groupIds.getInt(p);
int ord = ordinalIds.getInt(p + positionOffset);
state.addValueOrdinal(groupId, hashIds.getInt(ord));
if (groupIds.isConstant() && hashIds.isConstant()) {
state.addValueOrdinal(groupIds.getInt(0), hashIds.getInt(0));
return;
}
int lastGroup = groupIds.getInt(0);
int lastOrd = ordinalIds.getInt(positionOffset);
state.addValueOrdinal(lastGroup, hashIds.getInt(lastOrd));
for (int p = 1; p < groupIds.getPositionCount(); p++) {
final int nextGroup = groupIds.getInt(p);
final int nextOrd = ordinalIds.getInt(p + positionOffset);
if (nextGroup != lastGroup || nextOrd != lastOrd) {
lastGroup = nextGroup;
lastOrd = nextOrd;
state.addValueOrdinal(lastGroup, hashIds.getInt(lastOrd));
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,10 +100,40 @@ public void add(IntBlock bytesHashes, LongBlock longsBlock, GroupingAggregatorFu
public IntVector add(IntVector bytesHashes, LongVector longsVector) {
int positions = bytesHashes.getPositionCount();
final int[] ords = new int[positions];
for (int i = 0; i < positions; i++) {
ords[i] = Math.toIntExact(hashOrdToGroup(finalHash.add(bytesHashes.getInt(i), longsVector.getLong(i))));
int lastByte = bytesHashes.getInt(0);
long lastLong = longsVector.getLong(0);
ords[0] = Math.toIntExact(hashOrdToGroup(finalHash.add(lastByte, lastLong)));
boolean constant = true;
if (bytesHashes.isConstant()) {
for (int i = 1; i < positions; i++) {
final long nextLong = longsVector.getLong(i);
if (nextLong == lastLong) {
ords[i] = ords[i - 1];
} else {
ords[i] = Math.toIntExact(hashOrdToGroup(finalHash.add(lastByte, nextLong)));
lastLong = nextLong;
constant = false;
}
}
} else {
for (int i = 1; i < positions; i++) {
final int nextByte = bytesHashes.getInt(i);
final long nextLong = longsVector.getLong(i);
if (nextByte == lastByte && nextLong == lastLong) {
ords[i] = ords[i - 1];
} else {
ords[i] = Math.toIntExact(hashOrdToGroup(finalHash.add(nextByte, nextLong)));
lastByte = nextByte;
lastLong = nextLong;
constant = false;
}
}
}
if (constant) {
return blockFactory.newConstantIntVector(ords[0], positions);
} else {
return blockFactory.newIntArrayVector(ords, positions);
}
return blockFactory.newIntArrayVector(ords, positions);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,11 @@ final class $Type$BlockHash extends BlockHash {
*/
IntVector add($Type$Vector vector) {
$if(BytesRef)$
if (vector.isConstant()) {
BytesRef v = vector.getBytesRef(0, new BytesRef());
int groupId = Math.toIntExact(hashOrdToGroupNullReserved(hash.add(v)));
return blockFactory.newConstantIntVector(groupId, vector.getPositionCount());
}
var ordinals = vector.asOrdinals();
if (ordinals != null) {
return addOrdinalsVector(ordinals);
Expand Down Expand Up @@ -168,15 +173,18 @@ $endif$
$if(BytesRef)$
private IntVector addOrdinalsVector(OrdinalBytesRefVector inputBlock) {
IntVector inputOrds = inputBlock.getOrdinalsVector();
try (
var builder = blockFactory.newIntVectorBuilder(inputOrds.getPositionCount());
var hashOrds = add(inputBlock.getDictionaryVector())
) {
for (int p = 0; p < inputOrds.getPositionCount(); p++) {
int ord = hashOrds.getInt(inputOrds.getInt(p));
builder.appendInt(ord);
try (var hashOrds = add(inputBlock.getDictionaryVector())) {
if (inputOrds.isConstant()) {
int ord = hashOrds.getInt(inputOrds.getInt(0));
return blockFactory.newConstantIntVector(ord, inputOrds.getPositionCount());
}
try (var builder = blockFactory.newIntVectorBuilder(inputOrds.getPositionCount())) {
for (int p = 0; p < inputOrds.getPositionCount(); p++) {
int ord = hashOrds.getInt(inputOrds.getInt(p));
builder.appendInt(ord);
}
return builder.build();
}
return builder.build();
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import org.elasticsearch.compute.data.BytesRefBlock;
import org.elasticsearch.compute.data.BytesRefVector;
import org.elasticsearch.compute.data.IntBlock;
import org.elasticsearch.compute.data.IntVector;
import org.elasticsearch.compute.data.OrdinalBytesRefBlock;
import org.elasticsearch.compute.operator.BreakingBytesRefBuilder;
import org.elasticsearch.core.Releasable;
Expand Down Expand Up @@ -64,6 +65,37 @@ public SingletonOrdinalsBuilder endPositionEntry() {
throw new UnsupportedOperationException("should only have one value per doc");
}

private BytesRefBlock tryBuildConstantBlock() {
if (minOrd != maxOrd) {
return null;
}
for (int ord : ords) {
if (ord == -1) {
return null;
}
}
final BytesRef v;
try {
v = BytesRef.deepCopyOf(docValues.lookupOrd(minOrd));
} catch (IOException e) {
throw new UncheckedIOException("failed to lookup ordinals", e);
}
BytesRefVector bytes = null;
IntVector ordinals = null;
boolean success = false;
try {
bytes = blockFactory.newConstantBytesRefVector(v, 1);
ordinals = blockFactory.newConstantIntVector(0, ords.length);
final var result = new OrdinalBytesRefBlock(ordinals.asBlock(), bytes);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be a ConstantBytesRefBlock?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, it should be but here we need to return an ordinal constant block instead of a direct constant block to ensure that ordinal optimizations are applied if the constant optimization is not available: #132456 (comment)

success = true;
return result;
} finally {
if (success == false) {
Releasables.close(bytes, ordinals);
}
}
}

BytesRefBlock buildOrdinal() {
int valueCount = maxOrd - minOrd + 1;
long breakerSize = ordsSize(valueCount);
Expand Down Expand Up @@ -172,6 +204,10 @@ public long estimatedBytes() {

@Override
public BytesRefBlock build() {
var constantBlock = tryBuildConstantBlock();
if (constantBlock != null) {
return constantBlock;
}
return shouldBuildOrdinalsBlock() ? buildOrdinal() : buildRegularBlock();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1715,4 +1715,76 @@ private BlockHash buildBlockHash(int emitBatchSize, Block... values) {
? new PackedValuesBlockHash(specs, blockFactory, emitBatchSize)
: BlockHash.build(specs, blockFactory, emitBatchSize, true);
}

public void testConstant() {
try (
var hash = new BytesRefLongBlockHash(blockFactory, 0, 1, false, randomIntBetween(1, 1000));
var bytesHash = new BytesRefBlockHash(0, blockFactory)
) {
int iters = between(1, 20);
for (int i = 0; i < iters; i++) {
final BytesRefBlock bytes;
final LongBlock longs;
final boolean constantInput = randomBoolean();
final int positions = randomIntBetween(1, 100);
if (constantInput) {
bytes = blockFactory.newConstantBytesRefBlockWith(new BytesRef(randomAlphaOfLength(10)), positions);
if (randomBoolean()) {
try (IntVector hashIds = bytesHash.add(bytes.asVector())) {
assertTrue(hashIds.isConstant());
}
}
if (randomBoolean()) {
longs = blockFactory.newConstantLongBlockWith(randomNonNegativeLong(), positions);
} else {
long value = randomNonNegativeLong();
try (var builder = blockFactory.newLongVectorFixedBuilder(positions)) {
for (int p = 0; p < positions; p++) {
builder.appendLong(value);
}
longs = builder.build().asBlock();
}
}
} else {
try (var builder = blockFactory.newBytesRefBlockBuilder(positions)) {
for (int p = 0; p < positions; p++) {
builder.appendBytesRef(new BytesRef(randomAlphaOfLength(10)));
}
bytes = builder.build();
}
try (var builder = blockFactory.newLongVectorFixedBuilder(positions)) {
for (int p = 0; p < positions; p++) {
builder.appendLong(randomNonNegativeLong());
}
longs = builder.build().asBlock();
}
}
try (Page page = new Page(bytes, longs)) {
hash.add(page, new GroupingAggregatorFunction.AddInput() {
@Override
public void add(int positionOffset, IntArrayBlock groupIds) {
fail("should not call IntArrayBlock");
}

@Override
public void add(int positionOffset, IntBigArrayBlock groupIds) {
fail("should not call IntBigArrayBlock");
}

@Override
public void add(int positionOffset, IntVector groupIds) {
if (constantInput) {
assertTrue(groupIds.isConstant());
}
}

@Override
public void close() {

}
});
}
}
}
}
}
Loading