Merge branch 'master' into feature/UIMA-5823-Add-basic-benchmarking-module
diff --git a/pom.xml b/pom.xml
index 672cfd9..11735f8 100644
--- a/pom.xml
+++ b/pom.xml
@@ -115,6 +115,7 @@
 		<module>uimafit-legacy-support</module>
 		<module>uimafit-docbook</module>
 		<module>uimafit-cpe</module>
+    <module>uimafit-benchmark</module>
 		<module>uimafit-parent</module>
 	</modules>
 
diff --git a/uimafit-benchmark/pom.xml b/uimafit-benchmark/pom.xml
new file mode 100644
index 0000000..d39fc5e
--- /dev/null
+++ b/uimafit-benchmark/pom.xml
@@ -0,0 +1,128 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements. See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership. The ASF 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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <artifactId>uimafit-benchmark</artifactId>
+  <packaging>jar</packaging>
+  <name>Apache UIMA uimaFIT - Benchmark</name>
+  <description>Factories, Injection, and Testing library for UIMA</description>
+  <url>${uimaWebsiteUrl}</url>
+  <parent>
+    <groupId>org.apache.uima</groupId>
+    <artifactId>uimafit-parent</artifactId>
+    <version>2.5.0-SNAPSHOT</version>
+    <relativePath>../uimafit-parent</relativePath>
+  </parent>
+  <properties>
+    <maven.deploy.skip>true</maven.deploy.skip>
+    <maven.compiler.source>1.8</maven.compiler.source>
+    <maven.compiler.target>1.8</maven.compiler.target>
+  </properties>
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.uima</groupId>
+      <artifactId>uimafit-core</artifactId>
+      <version>2.5.0-SNAPSHOT</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.commons</groupId>
+      <artifactId>commons-math3</artifactId>
+      <version>3.6.1</version>
+    </dependency>
+    <dependency>
+      <groupId>org.assertj</groupId>
+      <artifactId>assertj-core</artifactId>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+  <licenses>
+    <license>
+      <name>Apache License, Version 2.0</name>
+      <url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
+      <distribution>repo</distribution>
+    </license>
+  </licenses>
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.uima</groupId>
+        <artifactId>jcasgen-maven-plugin</artifactId>
+        <executions>
+          <execution>
+            <phase>generate-resources</phase>
+            <goals>
+              <goal>generate</goal>
+            </goals>
+            <configuration>
+              <typeSystemIncludes>
+                <typeSystemInclude>src/main/resources/org/apache/uima/fit/type/**/*.xml</typeSystemInclude>
+              </typeSystemIncludes>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+      <plugin>
+        <!--
+          This plug-in adds the jcasgen generated source code folder as a project
+          source folder
+        -->
+        <groupId>org.codehaus.mojo</groupId>
+        <artifactId>build-helper-maven-plugin</artifactId>
+        <executions>
+          <execution>
+            <id>add-test-source</id>
+            <phase>process-resources</phase>
+            <goals>
+              <goal>add-test-source</goal>
+            </goals>
+            <configuration>
+              <sources>
+                <source>${project.build.directory}/generated-sources/jcasgen</source>
+              </sources>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+    <pluginManagement>
+      <plugins>
+        <plugin>
+          <groupId>org.apache.rat</groupId>
+          <artifactId>apache-rat-plugin</artifactId>
+          <executions>
+            <execution>
+              <id>default-cli</id>
+              <configuration>
+                <excludes combine.children="append">
+                  <!-- These test files are unreasonable to bear a license header -->
+                  <exclude>src/test/resources/log4j.properties</exclude>
+                  <!-- These configuration files cannot bear a license header -->
+                  <exclude>src/test/resources/META-INF/org.apache.uima.fit/fsindexes.txt</exclude>
+                  <exclude>src/test/resources/META-INF/org.apache.uima.fit/typepriorities.txt</exclude>
+                  <exclude>src/test/resources/META-INF/org.apache.uima.fit/types.txt</exclude>
+                </excludes>
+              </configuration>
+            </execution>
+          </executions>
+        </plugin>
+      </plugins>
+    </pluginManagement>
+  </build>
+</project>
\ No newline at end of file
diff --git a/uimafit-benchmark/src/main/java/org/apache/uima/fit/benchmark/Batch.java b/uimafit-benchmark/src/main/java/org/apache/uima/fit/benchmark/Batch.java
new file mode 100644
index 0000000..a4fd351
--- /dev/null
+++ b/uimafit-benchmark/src/main/java/org/apache/uima/fit/benchmark/Batch.java
@@ -0,0 +1,70 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF 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.apache.uima.fit.benchmark;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.commons.math3.stat.descriptive.DescriptiveStatistics;
+
+public class Batch {
+    private List<Measurement> measurements = new ArrayList<>();
+    
+    private final int magnitude;
+    
+    public Batch(int aMagnitude) {
+      magnitude = aMagnitude;
+    }
+    
+    public int getMagnitude() {
+      return magnitude;
+    }
+    
+    public void addMeasurement(Measurement aMeasurement) {
+      measurements.add(aMeasurement);
+    }
+    
+    public List<Measurement> getMeasurements() {
+      return measurements;
+    }
+    
+    @Override
+    public String toString()
+    {
+      DescriptiveStatistics stats = new DescriptiveStatistics();
+      
+      StringBuilder sb = new StringBuilder();
+      sb.append("[").append(String.format("%7d/%7d", magnitude, measurements.size())).append(": ");
+      int failures = 0;
+      for (Measurement m : measurements) {
+        if (m.failed()) {
+          failures++;
+        }
+        else {
+          stats.addValue(m.getDuration());
+        }
+      }
+      sb.append(String.format("min: %4.0f ", stats.getMin()));
+      sb.append(String.format("max: %4.0f ", stats.getMax()));
+      sb.append(String.format("median: %4.0f ", stats.getPercentile(50)));
+      sb.append(String.format("fail: %4d ", failures));
+      sb.append("]");
+      return sb.toString();
+    }
+  }
\ No newline at end of file
diff --git a/uimafit-benchmark/src/main/java/org/apache/uima/fit/benchmark/Benchmark.java b/uimafit-benchmark/src/main/java/org/apache/uima/fit/benchmark/Benchmark.java
new file mode 100644
index 0000000..1b998e3
--- /dev/null
+++ b/uimafit-benchmark/src/main/java/org/apache/uima/fit/benchmark/Benchmark.java
@@ -0,0 +1,149 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF 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.apache.uima.fit.benchmark;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.IntConsumer;
+import java.util.function.IntFunction;
+import java.util.function.LongSupplier;
+
+import org.apache.commons.lang.StringUtils;
+
+public class Benchmark {
+    private IntConsumer initializer = t -> {};
+    private RunnableWithExceptions subject;
+    
+    private String name;
+    private int baseRepeat = 20;
+    private int repeatIncrementTimes;
+    
+    private int baseMagnitude = 1;
+    private int incrementTimes;
+    private IntFunction<Integer> magnitudeIncrement = t -> t;
+    private LongSupplier timer = () -> System.currentTimeMillis();
+    
+    private List<Batch> batches = new ArrayList<>();
+
+    public Benchmark(String aName, Benchmark aTemplate) {
+      name = aName;
+      
+      initializer = aTemplate.initializer;
+      subject = aTemplate.subject;
+      
+      baseRepeat = aTemplate.baseRepeat;
+      repeatIncrementTimes = aTemplate.repeatIncrementTimes;
+      
+      baseMagnitude = aTemplate.baseMagnitude;
+      incrementTimes = aTemplate.incrementTimes;
+      magnitudeIncrement = aTemplate.magnitudeIncrement;
+      timer = aTemplate.timer;
+    }
+
+    public Benchmark(String aName) {
+      name = aName;
+    }
+
+    public Benchmark timer(LongSupplier aTimer)
+    {
+      timer = aTimer;
+      return this;
+    }
+
+    public Benchmark repeat(int aRepeat)
+    {
+      baseRepeat = aRepeat;
+      return this;
+    }
+
+    public Benchmark magnitude(int aMagnitude)
+    {
+      baseMagnitude = aMagnitude;
+      return this;
+    }
+
+    public Benchmark magnitudeIncrement(IntFunction<Integer> aIncrement)
+    {
+      magnitudeIncrement = aIncrement;
+      return this;
+    }
+
+    public Benchmark incrementTimes(int aTimes)
+    {
+      incrementTimes = aTimes;
+      return this;
+    }
+
+    public Benchmark initialize(IntConsumer aPieceOfCode)
+    {
+      initializer = aPieceOfCode;
+      return this;
+    }
+    
+    public Benchmark measure(RunnableWithExceptions aPieceOfCode)
+    {
+      subject = aPieceOfCode;
+      return this;
+    }
+    
+    private Batch runBatch(int aMagnitude)
+    {
+      Batch batch = new Batch(aMagnitude);
+      
+      initializer.accept(aMagnitude);
+      for (int i = 0; i < baseRepeat; i++) {
+        
+        long startTime = timer.getAsLong();
+        try {
+          subject.run();
+          batch.addMeasurement(new Measurement(i, timer.getAsLong() - startTime));
+        }
+        catch (Exception e) {
+          batch.addMeasurement(new Measurement(i, timer.getAsLong() - startTime, e));
+        }
+      }
+      
+      return batch;
+    }
+    
+    public void run()
+    {
+      System.out.printf("%n%s%n", StringUtils.repeat("=", name.length()));
+      System.out.printf("%s%n", name);
+      System.out.printf("%s%n", StringUtils.repeat("=", name.length()));
+      
+      int magnitude = baseMagnitude;
+      int n = 0;
+      
+      System.out.print("Running benchmark... ");
+      do {
+        if (magnitude > 0) {
+          System.out.printf("%d ", magnitude);
+        }
+        batches.add(runBatch(magnitude));
+        magnitude = magnitudeIncrement.apply(magnitude);
+        n++;
+      } while (n < incrementTimes);
+      System.out.printf("%n%n");
+      
+      for (Batch b : batches) {
+        System.out.printf("%s%n", b);
+      }
+    }
+  }
\ No newline at end of file
diff --git a/uimafit-benchmark/src/main/java/org/apache/uima/fit/benchmark/CasInitializationUtils.java b/uimafit-benchmark/src/main/java/org/apache/uima/fit/benchmark/CasInitializationUtils.java
new file mode 100644
index 0000000..e9e53c2
--- /dev/null
+++ b/uimafit-benchmark/src/main/java/org/apache/uima/fit/benchmark/CasInitializationUtils.java
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF 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.apache.uima.fit.benchmark;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+
+import org.apache.uima.cas.CAS;
+import org.apache.uima.cas.CASException;
+import org.apache.uima.cas.Type;
+import org.apache.uima.fit.type.Sentence;
+import org.apache.uima.fit.type.Token;
+
+public final class CasInitializationUtils
+{
+    private static final long RANDOM_SEED = 12345l;
+
+    private CasInitializationUtils()
+    {
+        // No instances
+    }
+
+    public static void initRandomCas(CAS cas, int size)
+    {
+        cas.reset();
+        Random rnd = new Random(RANDOM_SEED);
+        List<Type> types = new ArrayList<Type>();
+        types.add(cas.getTypeSystem().getType(Token.class.getName()));
+        types.add(cas.getTypeSystem().getType(Sentence.class.getName()));
+
+        // Shuffle the types
+        for (int n = 0; n < 10; n++) {
+            Type t = types.remove(rnd.nextInt(types.size()));
+            types.add(t);
+        }
+
+        // Randomly generate annotations
+        for (int n = 0; n < size; n++) {
+            for (Type t : types) {
+                int begin = rnd.nextInt(100);
+                int end = begin + rnd.nextInt(30);
+                cas.addFsToIndexes(cas.createAnnotation(t, begin, end));
+            }
+        }
+
+        try {
+            cas.getJCas();
+        }
+        catch (CASException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+}
diff --git a/uimafit-benchmark/src/main/java/org/apache/uima/fit/benchmark/Measurement.java b/uimafit-benchmark/src/main/java/org/apache/uima/fit/benchmark/Measurement.java
new file mode 100644
index 0000000..ace262e
--- /dev/null
+++ b/uimafit-benchmark/src/main/java/org/apache/uima/fit/benchmark/Measurement.java
@@ -0,0 +1,64 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF 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.apache.uima.fit.benchmark;
+
+public class Measurement {
+    private final int run;
+    private final long duration;
+    private final Exception exception;
+    
+    public Measurement(int aRun, long aDuration) {
+      run = aRun;
+      duration = aDuration;
+      exception = null;
+    }
+    
+    public Measurement(int aRun, long aDuration, Exception aException) {
+      exception = aException;
+      run = aRun;
+      duration = aDuration;
+    }
+
+    public int getRun() {
+      return run;
+    }
+    
+    public long getDuration() {
+      return duration;
+    }
+    
+    public Exception getException() {
+      return exception;
+    }
+    
+    public boolean failed() {
+      return exception != null;
+    }
+    
+    @Override
+    public String toString()
+    {
+      if (failed()) {
+        return "[" + run + ": FAIL]";
+      }
+      else {
+        return "[" + run + ": " + duration + "]";
+      }
+    }
+  }
\ No newline at end of file
diff --git a/uimafit-benchmark/src/main/java/org/apache/uima/fit/benchmark/RunnableWithExceptions.java b/uimafit-benchmark/src/main/java/org/apache/uima/fit/benchmark/RunnableWithExceptions.java
new file mode 100644
index 0000000..262c454
--- /dev/null
+++ b/uimafit-benchmark/src/main/java/org/apache/uima/fit/benchmark/RunnableWithExceptions.java
@@ -0,0 +1,5 @@
+package org.apache.uima.fit.benchmark;
+
+public interface RunnableWithExceptions {
+    void run() throws Exception;
+  }
\ No newline at end of file
diff --git a/uimafit-benchmark/src/main/resources/META-INF/org.apache.uima.fit/types.txt b/uimafit-benchmark/src/main/resources/META-INF/org.apache.uima.fit/types.txt
new file mode 100644
index 0000000..1c96dc9
--- /dev/null
+++ b/uimafit-benchmark/src/main/resources/META-INF/org.apache.uima.fit/types.txt
@@ -0,0 +1 @@
+classpath*:org/apache/uima/fit/type/**/*.xml
diff --git a/uimafit-benchmark/src/main/resources/org/apache/uima/fit/type/Sentence.xml b/uimafit-benchmark/src/main/resources/org/apache/uima/fit/type/Sentence.xml
new file mode 100644
index 0000000..a69681d
--- /dev/null
+++ b/uimafit-benchmark/src/main/resources/org/apache/uima/fit/type/Sentence.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+	Licensed to the Apache Software Foundation (ASF) under one
+	or more contributor license agreements. See the NOTICE file
+	distributed with this work for additional information
+	regarding copyright ownership. The ASF 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.
+-->
+<typeSystemDescription xmlns="http://uima.apache.org/resourceSpecifier">
+  <name>Sentence</name>
+  <description></description>
+  <version>1.0</version>
+  <vendor/>
+  <types>
+    <typeDescription>
+      <name>org.apache.uima.fit.type.Sentence</name>
+      <description/>
+      <supertypeName>uima.tcas.Annotation</supertypeName>
+    </typeDescription>
+  </types>
+</typeSystemDescription>
diff --git a/uimafit-benchmark/src/main/resources/org/apache/uima/fit/type/Token.xml b/uimafit-benchmark/src/main/resources/org/apache/uima/fit/type/Token.xml
new file mode 100644
index 0000000..629ae03
--- /dev/null
+++ b/uimafit-benchmark/src/main/resources/org/apache/uima/fit/type/Token.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+	Licensed to the Apache Software Foundation (ASF) under one
+	or more contributor license agreements. See the NOTICE file
+	distributed with this work for additional information
+	regarding copyright ownership. The ASF 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.
+-->
+<typeSystemDescription xmlns="http://uima.apache.org/resourceSpecifier">
+  <name>Token</name>
+  <description></description>
+  <version>1.0</version>
+  <vendor/>
+  <types>
+    <typeDescription>
+      <name>org.apache.uima.fit.type.Token</name>
+      <description/>
+      <supertypeName>uima.tcas.Annotation</supertypeName>
+      <features>
+        <featureDescription>
+          <name>pos</name>
+          <description/>
+          <rangeTypeName>uima.cas.String</rangeTypeName>
+        </featureDescription>
+        <featureDescription>
+          <name>stem</name>
+          <description/>
+          <rangeTypeName>uima.cas.String</rangeTypeName>
+        </featureDescription>
+      </features>
+    </typeDescription>
+  </types>
+</typeSystemDescription>
diff --git a/uimafit-benchmark/src/test/java/org/apache/uima/fit/benchmark/CasUtilBenchmark.java b/uimafit-benchmark/src/test/java/org/apache/uima/fit/benchmark/CasUtilBenchmark.java
new file mode 100644
index 0000000..673491b
--- /dev/null
+++ b/uimafit-benchmark/src/test/java/org/apache/uima/fit/benchmark/CasUtilBenchmark.java
@@ -0,0 +1,146 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF 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.apache.uima.fit.benchmark;
+
+import static org.apache.uima.fit.benchmark.CasInitializationUtils.initRandomCas;
+import static org.apache.uima.fit.factory.TypeSystemDescriptionFactory.createTypeSystemDescription;
+import static org.apache.uima.fit.util.CasUtil.getType;
+import static org.apache.uima.fit.util.CasUtil.indexCovered;
+import static org.apache.uima.fit.util.CasUtil.indexCovering;
+import static org.apache.uima.fit.util.CasUtil.select;
+import static org.apache.uima.fit.util.CasUtil.selectAll;
+import static org.apache.uima.fit.util.CasUtil.selectCovered;
+import static org.apache.uima.fit.util.CasUtil.selectCovering;
+import static org.apache.uima.fit.util.CasUtil.selectFS;
+
+import org.apache.uima.cas.CAS;
+import org.apache.uima.cas.Type;
+import org.apache.uima.util.CasCreationUtils;
+import org.junit.Before;
+import org.junit.Test;
+
+public class CasUtilBenchmark {
+  private CAS cas;
+  
+  private static final String TYPE_NAME_TOKEN = "org.apache.uima.fit.type.Token";
+  private static final String TYPE_NAME_SENTENCE = "org.apache.uima.fit.type.Sentence";
+  
+  @Before
+  public void setup() throws Exception {
+    if (cas == null) {
+      cas = CasCreationUtils.createCas(createTypeSystemDescription(), null, null);
+    }
+    else {
+      cas.reset();
+    }
+  }
+
+  @Test
+  public void benchmarkSelect() {
+    Benchmark template = new Benchmark("TEMPLATE")
+      .initialize(n -> initRandomCas(cas, n))
+      .magnitude(10)
+      .magnitudeIncrement(count -> count * 10)
+      .incrementTimes(5);
+    
+    new Benchmark("CAS select Token", template)
+      .measure(() -> select(cas, getType(cas, TYPE_NAME_TOKEN)))
+      .run();
+
+    new Benchmark("CAS select Token and iterate", template)
+      .measure(() -> select(cas, getType(cas, TYPE_NAME_TOKEN)).forEach(v -> {}))
+      .run();
+
+    new Benchmark("CAS select Sentence", template)
+      .measure(() -> select(cas, getType(cas, TYPE_NAME_SENTENCE)))
+      .run();
+
+    new Benchmark("CAS select Sentence and iterate", template)
+      .measure(() -> select(cas, getType(cas, TYPE_NAME_SENTENCE)).forEach(v -> {}))
+      .run();
+    
+    new Benchmark("CAS select TOP", template)
+      .measure(() -> selectFS(cas, getType(cas, CAS.TYPE_NAME_TOP)))
+      .run();
+
+    new Benchmark("CAS select TOP and iterate", template)
+      .measure(() -> selectFS(cas, getType(cas, CAS.TYPE_NAME_TOP)).forEach(v -> {}))
+      .run();
+
+    new Benchmark("CAS select ALL", template)
+      .measure(() -> selectAll(cas))
+      .run();
+    
+    new Benchmark("CAS select ALL and iterate", template)
+      .measure(() -> selectAll(cas).forEach(v -> {}))
+      .run();
+  }
+  
+  @Test
+  public void benchmarkSelectCovered() {
+    Benchmark template = new Benchmark("TEMPLATE")
+        .initialize(n -> initRandomCas(cas, n))
+        .magnitude(10)
+        .magnitudeIncrement(count -> count * 10)
+        .incrementTimes(4);
+    
+    new Benchmark("CAS selectCovered", template)
+      .measure(() -> {
+        Type sentenceType = getType(cas, TYPE_NAME_SENTENCE);
+        Type tokenType = getType(cas, TYPE_NAME_TOKEN);
+        select(cas, sentenceType).forEach(s -> selectCovered(tokenType, s).forEach(t -> {}));
+      })
+      .run();
+
+    new Benchmark("CAS indexCovered", template)
+      .measure(() -> indexCovered(cas, getType(cas, TYPE_NAME_SENTENCE), getType(cas, TYPE_NAME_TOKEN))
+          .forEach((s, l) -> l.forEach(t -> {})))
+      .run();
+  }
+  
+  @Test
+  public void benchmarkSelectCovering() {
+    Benchmark template = new Benchmark("TEMPLATE")
+      .initialize(n -> initRandomCas(cas, n))
+      .magnitude(10)
+      .magnitudeIncrement(count -> count * 10)
+      .incrementTimes(3);
+    
+    new Benchmark("CAS selectCovering", template)
+      .measure(() -> {
+        Type sentenceType = getType(cas, TYPE_NAME_SENTENCE);
+        Type tokenType = getType(cas, TYPE_NAME_TOKEN);
+        select(cas, tokenType).forEach(t -> selectCovering(sentenceType, t));
+      })
+      .run();
+
+    new Benchmark("CAS selectCovering", template)
+      .measure(() -> {
+        Type sentenceType = getType(cas, TYPE_NAME_SENTENCE);
+        Type tokenType = getType(cas, TYPE_NAME_TOKEN);
+        select(cas, tokenType).forEach(s -> selectCovering(sentenceType, s));
+      })
+      .run();
+
+    new Benchmark("CAS indexCovering", template)
+      .measure(() -> indexCovering(cas, getType(cas, TYPE_NAME_TOKEN), getType(cas, TYPE_NAME_SENTENCE))
+          .forEach((t, l) -> l.forEach(s -> {})))
+      .run();
+  }
+}
diff --git a/uimafit-benchmark/src/test/java/org/apache/uima/fit/benchmark/FSUtilBenchmark.java b/uimafit-benchmark/src/test/java/org/apache/uima/fit/benchmark/FSUtilBenchmark.java
new file mode 100644
index 0000000..536e234
--- /dev/null
+++ b/uimafit-benchmark/src/test/java/org/apache/uima/fit/benchmark/FSUtilBenchmark.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF 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.apache.uima.fit.benchmark;
+
+import static org.apache.uima.fit.util.FSUtil.setFeature;
+
+import org.apache.uima.fit.factory.JCasFactory;
+import org.apache.uima.fit.type.Token;
+import org.apache.uima.jcas.JCas;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+public class FSUtilBenchmark {
+	private static JCas jcas;
+	private static Token fs;
+
+	@BeforeClass
+  public static void setupOnce() throws Exception {
+  	jcas = JCasFactory.createText("test");
+  	fs = new Token(jcas, 0, 1);
+  	fs.addToIndexes();
+  }
+
+	@Test
+  public void benchmarkSetFeature() {
+    Benchmark template = new Benchmark("TEMPLATE")
+      .timer(System::nanoTime)
+      .repeat(1_000_000);
+
+    new Benchmark("set feature string JCas", template)
+      .measure(() -> fs.setPos("NN"))
+      .run();
+
+    new Benchmark("set feature string", template)
+      .measure(() -> setFeature(fs, "pos", "NN"))
+      .run();
+  }
+}
diff --git a/uimafit-benchmark/src/test/java/org/apache/uima/fit/benchmark/JCasFactoryBenchmark.java b/uimafit-benchmark/src/test/java/org/apache/uima/fit/benchmark/JCasFactoryBenchmark.java
new file mode 100644
index 0000000..3ca8157
--- /dev/null
+++ b/uimafit-benchmark/src/test/java/org/apache/uima/fit/benchmark/JCasFactoryBenchmark.java
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF 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.apache.uima.fit.benchmark;
+
+import static org.apache.uima.fit.factory.TypeSystemDescriptionFactory.createTypeSystemDescription;
+
+import org.apache.uima.resource.metadata.TypeSystemDescription;
+import org.apache.uima.util.CasCreationUtils;
+import org.junit.Test;
+
+public class JCasFactoryBenchmark
+{
+  @Test
+  public void benchmarkCreateTypeSystemDescription() throws Exception {
+    Benchmark template = new Benchmark("TEMPLATE")
+      .timer(System::currentTimeMillis)
+      .repeat(1000);
+    
+    new Benchmark("createTypeSystemDescription", template)
+      .measure(() -> createTypeSystemDescription())
+      .run();
+  }
+	
+  @Test
+  public void benchmarkCreateJCas() throws Exception {
+    Benchmark template = new Benchmark("TEMPLATE")
+      .timer(System::currentTimeMillis)
+      .repeat(1000);
+    
+    TypeSystemDescription tsd = createTypeSystemDescription();
+    
+    new Benchmark("create CAS", template)
+      .measure(() -> CasCreationUtils.createCas(tsd, null, null))
+      .run();
+
+    new Benchmark("create JCas (fresh TSD)", template)
+      .measure(() -> CasCreationUtils.createCas(createTypeSystemDescription(), null, null).getJCas())
+      .run();
+    
+    new Benchmark("create JCas (re-use TSD)", template)
+      .measure(() -> CasCreationUtils.createCas(tsd, null, null).getJCas())
+      .run();
+  }
+}
diff --git a/uimafit-benchmark/src/test/java/org/apache/uima/fit/benchmark/JCasUtilBenchmark.java b/uimafit-benchmark/src/test/java/org/apache/uima/fit/benchmark/JCasUtilBenchmark.java
new file mode 100644
index 0000000..591dfb8
--- /dev/null
+++ b/uimafit-benchmark/src/test/java/org/apache/uima/fit/benchmark/JCasUtilBenchmark.java
@@ -0,0 +1,124 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF 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.apache.uima.fit.benchmark;
+
+import static org.apache.uima.fit.benchmark.CasInitializationUtils.initRandomCas;
+import static org.apache.uima.fit.util.JCasUtil.indexCovered;
+import static org.apache.uima.fit.util.JCasUtil.indexCovering;
+import static org.apache.uima.fit.util.JCasUtil.select;
+import static org.apache.uima.fit.util.JCasUtil.selectAll;
+import static org.apache.uima.fit.util.JCasUtil.selectCovered;
+import static org.apache.uima.fit.util.JCasUtil.selectCovering;
+
+import org.apache.uima.fit.factory.JCasFactory;
+import org.apache.uima.fit.type.Sentence;
+import org.apache.uima.fit.type.Token;
+import org.apache.uima.jcas.JCas;
+import org.apache.uima.jcas.cas.TOP;
+import org.junit.Before;
+import org.junit.Test;
+
+public class JCasUtilBenchmark {
+  private JCas jcas;
+  
+  @Before
+  public void setup() throws Exception {
+    if (jcas == null) {
+      jcas = JCasFactory.createJCas();
+    }
+    else {
+      jcas.reset();
+    }
+  }
+
+  @Test
+  public void benchmarkSelect() {
+    Benchmark template = new Benchmark("TEMPLATE")
+      .initialize(n -> initRandomCas(jcas.getCas(), n))
+      .magnitude(10)
+      .magnitudeIncrement(count -> count * 10)
+      .incrementTimes(5);
+    
+    new Benchmark("JCas select Token", template)
+      .measure(() -> select(jcas, Token.class))
+      .run();
+
+    new Benchmark("JCas select Token and iterate", template)
+      .measure(() -> select(jcas, Token.class).forEach(v -> {}))
+      .run();
+
+    new Benchmark("JCas select Sentence", template)
+      .measure(() -> select(jcas, Sentence.class))
+      .run();
+
+    new Benchmark("JCas select Sentence and iterate", template)
+      .measure(() -> select(jcas, Sentence.class).forEach(v -> {}))
+      .run();
+    
+    new Benchmark("JCas select TOP", template)
+      .measure(() -> select(jcas, TOP.class))
+      .run();
+
+    new Benchmark("JCas select TOP and iterate", template)
+      .measure(() -> select(jcas, TOP.class).forEach(v -> {}))
+      .run();
+    
+    new Benchmark("JCas select ALL", template)
+      .measure(() -> selectAll(jcas))
+      .run();
+    
+    new Benchmark("JCas select ALL and iterate", template)
+      .measure(() -> selectAll(jcas).forEach(v -> {}))
+      .run();
+  }
+  
+  @Test
+  public void benchmarkSelectCovered() {
+    Benchmark template = new Benchmark("TEMPLATE")
+      .initialize(n -> initRandomCas(jcas.getCas(), n))
+      .magnitude(10)
+      .magnitudeIncrement(count -> count * 10)
+      .incrementTimes(4);
+    
+    new Benchmark("JCas selectCovered", template)
+      .measure(() -> select(jcas, Sentence.class).forEach(s -> selectCovered(Token.class, s)))
+      .run();
+
+    new Benchmark("JCas indexCovered", template)
+      .measure(() -> indexCovered(jcas, Sentence.class, Token.class).forEach((s, l) -> l.forEach(t -> {})))
+      .run();
+  }
+  
+  @Test
+  public void benchmarkSelectCovering() {
+    Benchmark template = new Benchmark("TEMPLATE")
+      .initialize(n -> initRandomCas(jcas.getCas(), n))
+      .magnitude(10)
+      .magnitudeIncrement(count -> count * 10)
+      .incrementTimes(3);
+    
+    new Benchmark("JCas selectCovering", template)
+      .measure(() -> select(jcas, Token.class).forEach(t -> selectCovering(Sentence.class, t)))
+      .run();
+
+    new Benchmark("JCas indexCovering", template)
+      .measure(() -> indexCovering(jcas, Token.class, Sentence.class).forEach((t, l) -> l.forEach(s -> {})))
+      .run();
+  }
+}