Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add benchmark for TransformKey and TransformValue
Two new benchmarks added for evaluating the possible outcome for bytecode generation for map transform key/value. The results shows a 10%-25% improvement. BenchmarkTransformKey is to transform a long type key x to x + 1. BenchmarkTransformValue is to transform a long type value y to a boolean type if y > 0. Benchmark (name) (type) Mode Cnt Score Error Units transform_keys DOUBLE avgt 20 143.077 ± 2.378 ns/op exact_transform_keys DOUBLE avgt 20 136.607 ± 7.131 ns/op transform_keys BIGINT avgt 20 107.877 ± 5.898 ns/op exact_transform_keys BIGINT avgt 20 92.876 ± 4.231 ns/op Benchmark (name) (type) Mode Cnt Score Error Units transform_values BIGINT avgt 20 67.534 ± 4.069 ns/op exact_transform_values BIGINT avgt 20 50.477 ± 2.556 ns/op transform_values DOUBLE avgt 20 62.217 ± 1.273 ns/op exact_transform_values DOUBLE avgt 20 50.615 ± 3.348 ns/op transform_values VARCHAR avgt 20 89.682 ± 3.663 ns/op exact_transform_values VARCHAR avgt 20 76.351 ± 3.868 ns/op
- Loading branch information
Showing
2 changed files
with
433 additions
and
0 deletions.
There are no files selected for viewing
207 changes: 207 additions & 0 deletions
207
presto-main/src/test/java/com/facebook/presto/operator/scalar/BenchmarkTransformKey.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,207 @@ | ||
/* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
package com.facebook.presto.operator.scalar; | ||
|
||
import com.facebook.presto.metadata.FunctionKind; | ||
import com.facebook.presto.metadata.MetadataManager; | ||
import com.facebook.presto.metadata.Signature; | ||
import com.facebook.presto.operator.PageProcessor; | ||
import com.facebook.presto.spi.Page; | ||
import com.facebook.presto.spi.PageBuilder; | ||
import com.facebook.presto.spi.block.Block; | ||
import com.facebook.presto.spi.block.BlockBuilder; | ||
import com.facebook.presto.spi.block.BlockBuilderStatus; | ||
import com.facebook.presto.spi.block.InterleavedBlockBuilder; | ||
import com.facebook.presto.spi.type.Type; | ||
import com.facebook.presto.sql.gen.ExpressionCompiler; | ||
import com.facebook.presto.sql.relational.CallExpression; | ||
import com.facebook.presto.sql.relational.ConstantExpression; | ||
import com.facebook.presto.sql.relational.InputReferenceExpression; | ||
import com.facebook.presto.sql.relational.LambdaDefinitionExpression; | ||
import com.facebook.presto.sql.relational.RowExpression; | ||
import com.facebook.presto.sql.relational.VariableReferenceExpression; | ||
import com.facebook.presto.type.MapType; | ||
import com.google.common.collect.ImmutableList; | ||
import org.openjdk.jmh.annotations.Benchmark; | ||
import org.openjdk.jmh.annotations.BenchmarkMode; | ||
import org.openjdk.jmh.annotations.Fork; | ||
import org.openjdk.jmh.annotations.Measurement; | ||
import org.openjdk.jmh.annotations.Mode; | ||
import org.openjdk.jmh.annotations.OperationsPerInvocation; | ||
import org.openjdk.jmh.annotations.OutputTimeUnit; | ||
import org.openjdk.jmh.annotations.Param; | ||
import org.openjdk.jmh.annotations.Scope; | ||
import org.openjdk.jmh.annotations.Setup; | ||
import org.openjdk.jmh.annotations.State; | ||
import org.openjdk.jmh.annotations.Warmup; | ||
import org.openjdk.jmh.runner.Runner; | ||
import org.openjdk.jmh.runner.options.Options; | ||
import org.openjdk.jmh.runner.options.OptionsBuilder; | ||
import org.openjdk.jmh.runner.options.VerboseMode; | ||
import org.openjdk.jmh.runner.options.WarmupMode; | ||
|
||
import java.util.ArrayList; | ||
import java.util.List; | ||
import java.util.concurrent.ThreadLocalRandom; | ||
import java.util.concurrent.TimeUnit; | ||
import java.util.stream.Collectors; | ||
|
||
import static com.facebook.presto.spi.function.OperatorType.ADD; | ||
import static com.facebook.presto.spi.type.BigintType.BIGINT; | ||
import static com.facebook.presto.spi.type.BooleanType.BOOLEAN; | ||
import static com.facebook.presto.spi.type.DoubleType.DOUBLE; | ||
import static com.facebook.presto.spi.type.TypeSignature.parseTypeSignature; | ||
import static com.facebook.presto.spi.type.TypeUtils.writeNativeValue; | ||
import static com.facebook.presto.testing.TestingConnectorSession.SESSION; | ||
import static java.lang.String.format; | ||
|
||
@SuppressWarnings("MethodMayBeStatic") | ||
@State(Scope.Thread) | ||
@OutputTimeUnit(TimeUnit.NANOSECONDS) | ||
@Fork(2) | ||
@Warmup(iterations = 10, time = 500, timeUnit = TimeUnit.MILLISECONDS) | ||
@Measurement(iterations = 10, time = 500, timeUnit = TimeUnit.MILLISECONDS) | ||
@BenchmarkMode(Mode.AverageTime) | ||
public class BenchmarkTransformKey | ||
{ | ||
private static final int POSITIONS = 100_000; | ||
private static final int NUM_TYPES = 2; | ||
|
||
@Benchmark | ||
@OperationsPerInvocation(POSITIONS * NUM_TYPES) | ||
public Object benchmark(BenchmarkData data) | ||
throws Throwable | ||
{ | ||
int position = 0; | ||
List<Page> pages = new ArrayList<>(); | ||
while (position < data.getPage().getPositionCount()) { | ||
position = data.getPageProcessor().process(SESSION, data.getPage(), position, data.getPage().getPositionCount(), data.getPageBuilder()); | ||
pages.add(data.getPageBuilder().build()); | ||
data.getPageBuilder().reset(); | ||
} | ||
return pages; | ||
} | ||
|
||
@SuppressWarnings("FieldMayBeFinal") | ||
@State(Scope.Thread) | ||
public static class BenchmarkData | ||
{ | ||
@Param({"DOUBLE", "BIGINT"}) | ||
private String type = "DOUBLE"; | ||
|
||
private String name = "transform_keys"; | ||
private PageBuilder pageBuilder; | ||
private Page page; | ||
private PageProcessor pageProcessor; | ||
|
||
@Setup | ||
public void setup() | ||
{ | ||
MetadataManager metadata = MetadataManager.createTestMetadataManager(); | ||
ExpressionCompiler compiler = new ExpressionCompiler(metadata); | ||
ImmutableList.Builder<RowExpression> projectionsBuilder = ImmutableList.builder(); | ||
Type elementType; | ||
Object increment; | ||
switch (type) { | ||
case "BIGINT": | ||
elementType = BIGINT; | ||
increment = 1L; | ||
break; | ||
case "DOUBLE": | ||
elementType = DOUBLE; | ||
increment = 1.0d; | ||
break; | ||
default: | ||
throw new UnsupportedOperationException(); | ||
} | ||
MapType mapType = new MapType(elementType, elementType); | ||
Signature signature = new Signature( | ||
name, | ||
FunctionKind.SCALAR, | ||
mapType.getTypeSignature(), | ||
mapType.getTypeSignature(), | ||
parseTypeSignature(format("function(%s, %s, %s)", type, type, type))); | ||
Signature add = new Signature("$operator$" + ADD.name(), FunctionKind.SCALAR, elementType.getTypeSignature(), elementType.getTypeSignature(), elementType.getTypeSignature()); | ||
projectionsBuilder.add(new CallExpression(signature, mapType, ImmutableList.of( | ||
new InputReferenceExpression(0, mapType), | ||
new LambdaDefinitionExpression( | ||
ImmutableList.of(elementType, elementType), | ||
ImmutableList.of("x", "y"), | ||
new CallExpression(add, elementType, ImmutableList.of( | ||
new VariableReferenceExpression("x", elementType), | ||
new ConstantExpression(increment, elementType))))))); | ||
Block block = createChannel(POSITIONS, mapType, elementType); | ||
|
||
ImmutableList<RowExpression> projections = projectionsBuilder.build(); | ||
pageProcessor = compiler.compilePageProcessor(new ConstantExpression(true, BOOLEAN), projections).get(); | ||
pageBuilder = new PageBuilder(projections.stream().map(RowExpression::getType).collect(Collectors.toList())); | ||
page = new Page(block); | ||
} | ||
|
||
private static Block createChannel(int positionCount, MapType mapType, Type elementType) | ||
{ | ||
BlockBuilder mapArrayBuilder = mapType.createBlockBuilder(new BlockBuilderStatus(), 1); | ||
BlockBuilder mapBuilder = new InterleavedBlockBuilder(ImmutableList.of(mapType.getKeyType(), mapType.getValueType()), new BlockBuilderStatus(), positionCount * 2); | ||
Object value; | ||
for (int position = 0; position < positionCount; position++) { | ||
if (elementType.equals(BIGINT)) { | ||
value = ThreadLocalRandom.current().nextLong(); | ||
} | ||
else if (elementType.equals(DOUBLE)) { | ||
value = ThreadLocalRandom.current().nextDouble(); | ||
} | ||
else { | ||
throw new UnsupportedOperationException(); | ||
} | ||
// Use position as the key to avoid collision | ||
writeNativeValue(elementType, mapBuilder, position); | ||
writeNativeValue(elementType, mapBuilder, value); | ||
} | ||
mapType.writeObject(mapArrayBuilder, mapBuilder.build()); | ||
return mapArrayBuilder.build(); | ||
} | ||
|
||
public PageProcessor getPageProcessor() | ||
{ | ||
return pageProcessor; | ||
} | ||
|
||
public Page getPage() | ||
{ | ||
return page; | ||
} | ||
|
||
public PageBuilder getPageBuilder() | ||
{ | ||
return pageBuilder; | ||
} | ||
} | ||
|
||
public static void main(String[] args) | ||
throws Throwable | ||
{ | ||
// assure the benchmarks are valid before running | ||
BenchmarkData data = new BenchmarkData(); | ||
data.setup(); | ||
new BenchmarkTransformKey().benchmark(data); | ||
|
||
Options options = new OptionsBuilder() | ||
.verbosity(VerboseMode.NORMAL) | ||
.warmupMode(WarmupMode.BULK) | ||
.include(".*" + BenchmarkTransformKey.class.getSimpleName() + ".*") | ||
.build(); | ||
new Runner(options).run(); | ||
} | ||
} |
Oops, something went wrong.