Skip to content

Instantly share code, notes, and snippets.

@danielmitterdorfer
Created February 10, 2017 11:49
Show Gist options
  • Save danielmitterdorfer/6f78acbd5edc9e9371d94e4ae45fffb7 to your computer and use it in GitHub Desktop.
Save danielmitterdorfer/6f78acbd5edc9e9371d94e4ae45fffb7 to your computer and use it in GitHub Desktop.
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you 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 org.elasticsearch.benchmark.parser;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.search.suggest.completion.CompletionSuggestion;
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.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 java.io.IOException;
import java.net.URISyntaxException;
import java.security.NoSuchAlgorithmException;
import java.util.concurrent.TimeUnit;
import static org.elasticsearch.common.xcontent.XContentHelper.toXContent;
@Fork(3)
@Warmup(iterations = 10)
@Measurement(iterations = 10)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
@State(Scope.Benchmark)
@SuppressWarnings("unused") //invoked by benchmarking framework
public class ParserBenchmark {
private XContentType xContentType;
private BytesReference originalBytes;
private XContentParser parser;
@Param(value = {"false", "true"})
private String humanReadable;
@Param(value = {"JSON", "SMILE", "YAML", "CBOR"})
private String contentType;
@Setup
public void setUp() throws IOException, NoSuchAlgorithmException, URISyntaxException {
CompletionSuggestion.Entry.Option option = ParserBenchmarkHelper.createTestItem();
xContentType = XContentType.valueOf(contentType);
originalBytes = toXContent(option, xContentType, Boolean.valueOf(humanReadable));
}
@Benchmark
public CompletionSuggestion.Entry.Option parse() throws Exception {
try (XContentParser p = ParserBenchmarkHelper.parser(xContentType.xContent(), originalBytes)) {
return CompletionSuggestion.Entry.Option.fromXContent(p);
}
}
}
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you 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 org.elasticsearch.benchmark.parser;
import com.carrotsearch.randomizedtesting.generators.RandomNumbers;
import com.carrotsearch.randomizedtesting.generators.RandomPicks;
import com.carrotsearch.randomizedtesting.generators.RandomStrings;
import org.apache.lucene.search.Explanation;
import org.elasticsearch.cluster.ClusterModule;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.collect.Tuple;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.common.xcontent.NamedXContentRegistry;
import org.elasticsearch.common.xcontent.XContent;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.Index;
import org.elasticsearch.index.shard.ShardId;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHitField;
import org.elasticsearch.search.SearchShardTarget;
import org.elasticsearch.search.SearchSortValues;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.suggest.completion.CompletionSuggestion;
import org.elasticsearch.test.RandomObjects;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.function.Supplier;
public class ParserBenchmarkHelper {
private static List<String> META_FIELDS = Arrays.asList("_uid", "_all", "_parent", "_routing", "_size", "_timestamp", "_ttl");
private static NamedXContentRegistry XCONTENT_REGISTRY = new NamedXContentRegistry(ClusterModule.getNamedXWriteables());
public static boolean randomBoolean() {
return getRandom().nextBoolean();
}
public static byte randomByte() {
return (byte) getRandom().nextInt();
}
public static short randomShort() {
return (short) getRandom().nextInt();
}
public static int randomInt() {
return getRandom().nextInt();
}
public static int randomInt(int max) {
return RandomNumbers.randomIntBetween(getRandom(), 0, max);
}
public static float randomFloat() {
return getRandom().nextFloat();
}
public static double randomDouble() {
return getRandom().nextDouble();
}
public static long randomLong() {
return getRandom().nextLong();
}
public static String randomAsciiOfLength(int codeUnits) {
return RandomStrings.randomAsciiOfLength(getRandom(), codeUnits);
}
/**
* Pick a random object from the given array. The array must not be empty.
*/
public static <T> T randomFrom(T... array) {
return randomFrom(getRandom(), array);
}
/**
* Pick a random object from the given array. The array must not be empty.
*/
public static <T> T randomFrom(Random random, T... array) {
return RandomPicks.randomFrom(random, array);
}
/**
* Pick a random object from the given list.
*/
public static <T> T randomFrom(List<T> list) {
return RandomPicks.randomFrom(getRandom(), list);
}
// /** Pick a random object from the given collection. */
// public static <T> T randomFrom(Collection<T> collection) {
// return randomFrom(getRandom(), collection);
// }
public static boolean rarely() {
return randomInt(100) >= 90;
}
/**
* The exact opposite of {@link #rarely()}.
*/
public static boolean frequently() {
return !rarely();
}
public static int randomIntBetween(int min, int max) {
return RandomNumbers.randomIntBetween(getRandom(), min, max);
}
public static String randomAsciiOfLengthBetween(int minCodeUnits, int maxCodeUnits) {
return RandomStrings.randomAsciiOfLengthBetween(getRandom(), minCodeUnits, maxCodeUnits);
}
public static String randomRealisticUnicodeOfCodepointLengthBetween(int minCodePoints, int maxCodePoints) {
return RandomStrings.randomRealisticUnicodeOfCodepointLengthBetween(getRandom(), minCodePoints, maxCodePoints);
}
public static String randomRealisticUnicodeOfCodepointLength(int codePoints) {
return RandomStrings.randomRealisticUnicodeOfCodepointLength(getRandom(), codePoints);
}
public static HighlightField createHighlightField() {
String name = frequently() ? randomAsciiOfLengthBetween(5, 20) : randomRealisticUnicodeOfCodepointLengthBetween(5, 20);
Text[] fragments = null;
if (frequently()) {
int size = randomIntBetween(0, 5);
fragments = new Text[size];
for (int i = 0; i < size; i++) {
fragments[i] = new Text(frequently() ? randomAsciiOfLengthBetween(10, 30) : randomRealisticUnicodeOfCodepointLengthBetween(10, 30));
}
}
return new HighlightField(name, fragments);
}
public static SearchHit.NestedIdentity createNestedIdentity(int depth) {
String field = frequently() ? randomAsciiOfLengthBetween(1, 20) : randomRealisticUnicodeOfCodepointLengthBetween(1, 20);
int offset = randomInt(10);
SearchHit.NestedIdentity child = null;
if (depth > 0) {
child = createNestedIdentity(depth - 1);
}
return new SearchHit.NestedIdentity(field, offset, child);
}
public static SearchSortValues createSearchSortValues() {
List<Supplier<Object>> valueSuppliers = new ArrayList<>();
// this should reflect all values that are allowed to go through the transport layer
valueSuppliers.add(() -> null);
valueSuppliers.add(() -> randomInt());
valueSuppliers.add(() -> randomLong());
valueSuppliers.add(() -> randomDouble());
valueSuppliers.add(() -> randomFloat());
valueSuppliers.add(() -> randomByte());
valueSuppliers.add(() -> randomShort());
valueSuppliers.add(() -> randomBoolean());
valueSuppliers.add(() -> frequently() ? randomAsciiOfLengthBetween(1, 30) : randomRealisticUnicodeOfCodepointLength(30));
int size = randomIntBetween(1, 20);
Object[] values = new Object[size];
for (int i = 0; i < size; i++) {
Supplier<Object> supplier = randomFrom(valueSuppliers);
values[i] = supplier.get();
}
return new SearchSortValues(values);
}
private static Explanation createExplanation(int depth) {
String description = randomAsciiOfLengthBetween(5, 20);
float value = randomFloat();
List<Explanation> details = new ArrayList<>();
if (depth > 0) {
int numberOfDetails = randomIntBetween(1, 3);
for (int i = 0; i < numberOfDetails; i++) {
details.add(createExplanation(depth - 1));
}
}
return Explanation.match(value, description, details);
}
public static SearchHit createSearchHit(boolean withOptionalInnerHits) {
int internalId = randomInt();
String uid = randomAsciiOfLength(10);
Text type = new Text(randomAsciiOfLengthBetween(5, 10));
SearchHit.NestedIdentity nestedIdentity = null;
if (randomBoolean()) {
nestedIdentity = createNestedIdentity(randomIntBetween(0, 2));
}
Map<String, SearchHitField> fields = new HashMap<>();
if (randomBoolean()) {
int size = randomIntBetween(0, 10);
for (int i = 0; i < size; i++) {
Tuple<List<Object>, List<Object>> values = RandomObjects.randomStoredFieldValues(getRandom(), XContentType.JSON);
if (randomBoolean()) {
String metaField = randomFrom(META_FIELDS);
fields.put(metaField, new SearchHitField(metaField, values.v1()));
} else {
String fieldName = randomAsciiOfLengthBetween(5, 10);
fields.put(fieldName, new SearchHitField(fieldName, values.v1()));
}
}
}
SearchHit hit = new SearchHit(internalId, uid, type, nestedIdentity, fields);
if (frequently()) {
if (rarely()) {
hit.score(Float.NaN);
} else {
hit.score(randomFloat());
}
}
if (frequently()) {
hit.sourceRef(RandomObjects.randomSource(getRandom()));
}
if (randomBoolean()) {
hit.version(randomLong());
}
if (randomBoolean()) {
hit.sortValues(createSearchSortValues());
}
if (randomBoolean()) {
int size = randomIntBetween(0, 5);
Map<String, HighlightField> highlightFields = new HashMap<>(size);
for (int i = 0; i < size; i++) {
highlightFields.put(randomAsciiOfLength(5), createHighlightField());
}
hit.highlightFields(highlightFields);
}
if (randomBoolean()) {
int size = randomIntBetween(0, 5);
String[] matchedQueries = new String[size];
for (int i = 0; i < size; i++) {
matchedQueries[i] = randomAsciiOfLength(5);
}
hit.matchedQueries(matchedQueries);
}
if (randomBoolean()) {
hit.explanation(createExplanation(randomIntBetween(0, 5)));
}
if (randomBoolean()) {
hit.shard(new SearchShardTarget(randomAsciiOfLengthBetween(5, 10), new ShardId(new Index(randomAsciiOfLengthBetween(5, 10), randomAsciiOfLengthBetween(5, 10)), randomInt())));
}
return hit;
}
public static CompletionSuggestion.Entry.Option createTestItem() {
Text text = new Text(randomAsciiOfLengthBetween(5, 15));
int docId = randomInt();
int numberOfContexts = randomIntBetween(0, 3);
Map<String, Set<CharSequence>> contexts = new HashMap<>();
for (int i = 0; i < numberOfContexts; i++) {
int numberOfValues = randomIntBetween(0, 3);
Set<CharSequence> values = new HashSet<>();
for (int v = 0; v < numberOfValues; v++) {
values.add(randomAsciiOfLengthBetween(5, 15));
}
contexts.put(randomAsciiOfLengthBetween(5, 15), values);
}
SearchHit hit = null;
float score = randomFloat();
if (randomBoolean()) {
hit = createSearchHit(false);
score = hit.getScore();
}
CompletionSuggestion.Entry.Option option = new CompletionSuggestion.Entry.Option(docId, text, score, contexts);
option.setHit(hit);
return option;
}
public static XContentParser parser(XContent xContent, BytesReference originalBytes) throws IOException {
return xContent.createParser(XCONTENT_REGISTRY, originalBytes);
}
public static Random getRandom() {
// use a constant seed intentionally to ensure we have more control over the results!
return new Random(17L);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment