diff --git a/pom.xml b/pom.xml
index ced9c50b8..e763fe1fd 100644
--- a/pom.xml
+++ b/pom.xml
@@ -82,6 +82,7 @@
spring-cql
spring-data-cassandra
spring-data-cassandra-distribution
+ spring-data-cassandra-benchmarks
diff --git a/spring-data-cassandra-benchmarks/pom.xml b/spring-data-cassandra-benchmarks/pom.xml
new file mode 100644
index 000000000..6cb3bdf66
--- /dev/null
+++ b/spring-data-cassandra-benchmarks/pom.xml
@@ -0,0 +1,98 @@
+
+
+ 4.0.0
+
+
+ org.springframework.data
+ spring-data-cassandra-parent
+ 2.0.0.DATACASS-389-SNAPSHOT
+ ../pom.xml
+
+
+ spring-data-cassandra-benchmarks
+
+ Spring Data Cassandra - Benchmarks
+
+
+
+
+
+ org.springframework.data
+ spring-data-cassandra
+ ${project.version}
+
+
+
+ org.slf4j
+ slf4j-nop
+ 1.7.21
+
+
+
+ org.openjdk.jmh
+ jmh-core
+ ${jmh.version}
+
+
+
+ org.projectlombok
+ lombok
+ 1.16.12
+ provided
+
+
+
+ org.openjdk.jmh
+ jmh-generator-annprocess
+ ${jmh.version}
+ provided
+
+
+
+
+
+ 1.17.4
+ benchmarks
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-shade-plugin
+ 2.2
+
+
+ package
+
+ shade
+
+
+ ${uberjar.name}
+
+
+ org.openjdk.jmh.Main
+
+
+
+
+
+ *:*
+
+ META-INF/*.SF
+ META-INF/*.DSA
+ META-INF/*.RSA
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/spring-data-cassandra-benchmarks/src/main/java/org/springframework/data/cassandra/benchmarks/Address.java b/spring-data-cassandra-benchmarks/src/main/java/org/springframework/data/cassandra/benchmarks/Address.java
new file mode 100644
index 000000000..9811d5f8c
--- /dev/null
+++ b/spring-data-cassandra-benchmarks/src/main/java/org/springframework/data/cassandra/benchmarks/Address.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2017 the original author or authors.
+ *
+ * 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 org.springframework.data.cassandra.benchmarks;
+
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+
+import org.springframework.data.cassandra.mapping.UserDefinedType;
+
+/**
+ * @author Mark Paluch
+ */
+@Getter
+@RequiredArgsConstructor
+@UserDefinedType
+public class Address {
+
+ final String city, zip;
+}
diff --git a/spring-data-cassandra-benchmarks/src/main/java/org/springframework/data/cassandra/benchmarks/CassandraMappingContextBenchmark.java b/spring-data-cassandra-benchmarks/src/main/java/org/springframework/data/cassandra/benchmarks/CassandraMappingContextBenchmark.java
new file mode 100644
index 000000000..b193b4615
--- /dev/null
+++ b/spring-data-cassandra-benchmarks/src/main/java/org/springframework/data/cassandra/benchmarks/CassandraMappingContextBenchmark.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2017 the original author or authors.
+ *
+ * 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 org.springframework.data.cassandra.benchmarks;
+
+import java.util.concurrent.TimeUnit;
+
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.Mode;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.State;
+import org.openjdk.jmh.infra.Blackhole;
+import org.openjdk.jmh.runner.Runner;
+import org.openjdk.jmh.runner.RunnerException;
+import org.openjdk.jmh.runner.options.Options;
+import org.openjdk.jmh.runner.options.OptionsBuilder;
+import org.springframework.data.cassandra.mapping.BasicCassandraMappingContext;
+
+/**
+ * Benchmark for {@link CassandraMappingContext}.
+ *
+ * @author Mark Paluch
+ */
+@State(Scope.Benchmark)
+public class CassandraMappingContextBenchmark {
+
+ @Benchmark
+ public void measureGetPersistentEntity(Blackhole blackhole) {
+
+ BasicCassandraMappingContext mappingContext = new BasicCassandraMappingContext();
+ mappingContext.setUserTypeResolver(typeName -> null);
+ blackhole.consume(mappingContext.getPersistentEntity(Address.class));
+ }
+
+ @Benchmark
+ public void measureGetPersistentEntityWithUdtReference(Blackhole blackhole) {
+
+ BasicCassandraMappingContext mappingContext = new BasicCassandraMappingContext();
+ mappingContext.setUserTypeResolver(typeName -> null);
+ blackhole.consume(mappingContext.getRequiredPersistentEntity(Customer.class));
+ }
+
+ public static void main(String[] args) throws RunnerException {
+
+ Options opt = new OptionsBuilder() //
+ .include(CassandraMappingContextBenchmark.class.getSimpleName()) //
+ .forks(1) //
+ .warmupIterations(5) //
+ .measurementIterations(10) //
+ .mode(Mode.AverageTime) //
+ .timeUnit(TimeUnit.NANOSECONDS) //
+ .build();
+
+ new Runner(opt).run();
+ }
+}
diff --git a/spring-data-cassandra-benchmarks/src/main/java/org/springframework/data/cassandra/benchmarks/Customer.java b/spring-data-cassandra-benchmarks/src/main/java/org/springframework/data/cassandra/benchmarks/Customer.java
new file mode 100644
index 000000000..eaeeca220
--- /dev/null
+++ b/spring-data-cassandra-benchmarks/src/main/java/org/springframework/data/cassandra/benchmarks/Customer.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2017 the original author or authors.
+ *
+ * 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 org.springframework.data.cassandra.benchmarks;
+
+import lombok.Data;
+
+import org.springframework.data.annotation.Id;
+import org.springframework.data.cassandra.mapping.Table;
+
+/**
+ * @author Mark Paluch
+ */
+@Data
+@Table
+public class Customer {
+
+ private @Id String id;
+ private String firstname, lastname;
+ private Address address;
+}
diff --git a/spring-data-cassandra-benchmarks/src/main/java/org/springframework/data/cassandra/benchmarks/MappingCassandraConverterBenchmark.java b/spring-data-cassandra-benchmarks/src/main/java/org/springframework/data/cassandra/benchmarks/MappingCassandraConverterBenchmark.java
new file mode 100644
index 000000000..7376aeed8
--- /dev/null
+++ b/spring-data-cassandra-benchmarks/src/main/java/org/springframework/data/cassandra/benchmarks/MappingCassandraConverterBenchmark.java
@@ -0,0 +1,231 @@
+/*
+ * Copyright 2017 the original author or authors.
+ *
+ * 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 org.springframework.data.cassandra.benchmarks;
+
+import static java.util.Arrays.*;
+import static org.springframework.data.cassandra.benchmarks.MappingCassandraConverterBenchmark.BenchmarkDependencyFactory.*;
+
+import java.lang.reflect.Constructor;
+import java.nio.ByteBuffer;
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.Mode;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.Setup;
+import org.openjdk.jmh.annotations.State;
+import org.openjdk.jmh.runner.Runner;
+import org.openjdk.jmh.runner.options.Options;
+import org.openjdk.jmh.runner.options.OptionsBuilder;
+import org.springframework.cassandra.core.cql.CqlIdentifier;
+import org.springframework.data.cassandra.convert.MappingCassandraConverter;
+import org.springframework.data.cassandra.mapping.BasicCassandraMappingContext;
+import org.springframework.data.cassandra.mapping.UserTypeResolver;
+import org.springframework.util.ReflectionUtils;
+
+import com.datastax.driver.core.CodecRegistry;
+import com.datastax.driver.core.ColumnDefinitions;
+import com.datastax.driver.core.ColumnDefinitions.Definition;
+import com.datastax.driver.core.DataType;
+import com.datastax.driver.core.ProtocolVersion;
+import com.datastax.driver.core.Row;
+import com.datastax.driver.core.TypeCodec;
+import com.datastax.driver.core.UDTValue;
+import com.datastax.driver.core.UserType;
+import com.datastax.driver.core.UserType.Field;
+import com.datastax.driver.core.querybuilder.QueryBuilder;
+
+/**
+ * Benchmark for {@link MappingCassandraConverter}.
+ *
+ * @author Mark Paluch
+ */
+@State(Scope.Benchmark)
+public class MappingCassandraConverterBenchmark {
+
+ private final BasicCassandraMappingContext mappingContext = new BasicCassandraMappingContext();
+ private final MappingCassandraConverter converter = new MappingCassandraConverter(mappingContext);
+
+ private ColumnDefinitions customerColumns;
+ private UserType addressType;
+
+ private ByteBuffer id;
+ private ByteBuffer firstname;
+ private ByteBuffer lastname;
+ private ByteBuffer address;
+
+ private Customer customer;
+ private Customer customerWithMappedUdt;
+
+ @Setup
+ public void beforeBenchmark() throws ReflectiveOperationException {
+
+ this.mappingContext.setUserTypeResolver(new BenchmarkUserTypeResolver());
+
+ this.mappingContext.getPersistentEntity(Customer.class);
+ this.mappingContext.getPersistentEntity(Address.class);
+
+ this.addressType = createUserType("address",
+ asList(field("zip", DataType.varchar()), field("city", DataType.varchar())));
+
+ this.customerColumns = columnDefinitions(asList(definition("id", DataType.varchar()), //
+ definition("firstname", DataType.varchar()), //
+ definition("lastname", DataType.varchar()), //
+ definition("address", this.addressType) //
+ ));
+
+ this.id = encode("my-id", DataType.varchar());
+ this.firstname = encode("Walter", DataType.varchar());
+ this.lastname = encode("White", DataType.varchar());
+
+ UDTValue udtValue = this.addressType.newValue();
+ udtValue.setString("zip", "12345");
+ udtValue.setString("city", "Albuquerque");
+
+ this.address = encode(udtValue, this.addressType);
+
+ Address address = new Address("12345", "Albuquerque");
+
+ this.customer = createCustomer();
+
+ Customer customerWithMappedUdt = createCustomer();
+ customerWithMappedUdt.setAddress(address);
+
+ this.customerWithMappedUdt = customerWithMappedUdt;
+ }
+
+ private Customer createCustomer() {
+
+ Customer customer = new Customer();
+
+ customer.setId("my-id");
+ customer.setFirstname("Walter");
+ customer.setLastname("White");
+
+ return customer;
+ }
+
+ // Benchmark
+ public void measureReadRow() throws ReflectiveOperationException {
+
+ Row row = createRow(this.customerColumns,
+ asList(id.duplicate(), this.firstname.duplicate(), this.lastname.duplicate(), null));
+
+ this.converter.read(Customer.class, row);
+ }
+
+ // @Benchmark
+ public void measureReadRowWithUdt() throws ReflectiveOperationException {
+
+ Row row = createRow(this.customerColumns,
+ asList(this.id.duplicate(), this.firstname.duplicate(), this.lastname.duplicate(), this.address.duplicate()));
+
+ converter.read(Customer.class, row);
+ }
+
+ @Benchmark
+ public void measureWriteQuery() throws ReflectiveOperationException {
+ converter.write(this.customer, QueryBuilder.insertInto("table"));
+ }
+
+ @Benchmark
+ public void measureWriteRowWithUdt() throws ReflectiveOperationException {
+ converter.write(this.customerWithMappedUdt, QueryBuilder.insertInto("table"));
+ }
+
+ public static void main(String[] args) throws Exception {
+
+ Options opt = new OptionsBuilder() //
+ .include(MappingCassandraConverterBenchmark.class.getSimpleName()) //
+ .forks(1) //
+ .warmupIterations(5) //
+ .measurementIterations(10) //
+ .mode(Mode.AverageTime) //
+ .timeUnit(TimeUnit.NANOSECONDS) //
+ .build();
+
+ new Runner(opt).run();
+ }
+
+ class BenchmarkUserTypeResolver implements UserTypeResolver {
+
+ @Override
+ public UserType resolveType(CqlIdentifier typeName) {
+ return addressType;
+ }
+ }
+
+ /**
+ * Factory to create dependencies required for the benchmark.
+ */
+ static class BenchmarkDependencyFactory {
+
+ static Row createRow(ColumnDefinitions definitions, List data) throws ReflectiveOperationException {
+
+ Class rowClass = (Class) Class.forName("com.datastax.driver.core.ArrayBackedRow");
+ Class> tokenFactoryClass = Class.forName("com.datastax.driver.core.Token$Factory");
+
+ Constructor constructor = ReflectionUtils.accessibleConstructor(rowClass, ColumnDefinitions.class,
+ tokenFactoryClass, ProtocolVersion.class, List.class);
+
+ return constructor.newInstance(definitions, null, ProtocolVersion.NEWEST_SUPPORTED, data);
+ }
+
+ static Definition definition(String name, DataType type) throws ReflectiveOperationException {
+
+ Constructor constructor = ReflectionUtils.accessibleConstructor(Definition.class, String.class,
+ String.class, String.class, DataType.class);
+
+ return constructor.newInstance("keyspace", "table", name, type);
+ }
+
+ static ColumnDefinitions columnDefinitions(Collection definitions) throws ReflectiveOperationException {
+
+ Constructor constructor = ReflectionUtils.accessibleConstructor(ColumnDefinitions.class,
+ Definition[].class, CodecRegistry.class);
+
+ return constructor.newInstance(definitions.toArray(new ColumnDefinitions.Definition[0]),
+ CodecRegistry.DEFAULT_INSTANCE);
+ }
+
+ static Field field(String name, DataType type) throws ReflectiveOperationException {
+
+ Class fieldClass = (Class) Class.forName("com.datastax.driver.core.UserType$Field");
+
+ Constructor constructor = ReflectionUtils.accessibleConstructor(fieldClass, String.class, DataType.class);
+
+ return constructor.newInstance(name, type);
+ }
+
+ static UserType createUserType(String name, Collection fields) throws ReflectiveOperationException {
+
+ Constructor constructor = ReflectionUtils.accessibleConstructor(UserType.class, String.class,
+ String.class, Collection.class, ProtocolVersion.class, CodecRegistry.class);
+
+ return constructor.newInstance("keyspace", name, fields, ProtocolVersion.NEWEST_SUPPORTED,
+ CodecRegistry.DEFAULT_INSTANCE);
+ }
+
+ static ByteBuffer encode(Object data, DataType type) throws ReflectiveOperationException {
+
+ TypeCodec