diff options
author | Paul Duffin <paulduffin@google.com> | 2015-12-07 23:10:29 +0000 |
---|---|---|
committer | android-build-merger <android-build-merger@google.com> | 2015-12-07 23:10:29 +0000 |
commit | f18823a974f84d583b620f7e48ab0b103f818e3b (patch) | |
tree | 647c3bc71c5f4aaded306e94acccb6b093efe11a | |
parent | 610a0d3081101f7c69d762eda68884b76b999c1b (diff) | |
parent | e236301e5fc778bffe1748ed80d7936e6c807012 (diff) | |
download | caliper-f18823a974f84d583b620f7e48ab0b103f818e3b.tar.gz |
Upgrade to latest (at the time of writing) version of Caliper
am: e236301e5f
* commit 'e236301e5fc778bffe1748ed80d7936e6c807012':
Upgrade to latest (at the time of writing) version of Caliper
294 files changed, 25123 insertions, 5839 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c4e65ae --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +*.iml +*.ipr +*.iws +.idea +target +*~ @@ -14,6 +14,9 @@ LOCAL_PATH := $(call my-dir) +# Include definitions of DAGGER2_PROCESSOR_CLASSES/LIBRARIES +include external/dagger2/dagger2_annotation_processor.mk + # build caliper host jar # ============================================================ @@ -24,16 +27,55 @@ LOCAL_MODULE_TAGS := optional LOCAL_MODULE_CLASS := JAVA_LIBRARIES LOCAL_SRC_FILES := $(call all-java-files-under, caliper/src/main/java/) LOCAL_JAVA_RESOURCE_DIRS := caliper/src/main/resources +LOCAL_IS_HOST_MODULE := true LOCAL_STATIC_JAVA_LIBRARIES := \ + apache-commons-math-host \ caliper-gson-host \ caliper-java-allocation-instrumenter-host \ + caliper-jersey-client-host \ + caliper-jersey-core-host \ + caliper-joda-time-host \ + caliper-jsr311-api-host \ + dagger2-host \ + dagger2-inject-host \ guavalib +# Use Dagger2 annotation processor +PROCESSOR_LIBRARIES := $(DAGGER2_PROCESSOR_LIBRARIES) +PROCESSOR_CLASSES := $(DAGGER2_PROCESSOR_CLASSES) +include external/dagger2/java_annotation_processors.mk + include $(BUILD_HOST_JAVA_LIBRARY) +# Remember the location of the generated files, this is needed for when +# building for target +caliper_host_generated_sources_dir := $(local-generated-sources-dir)/annotation_processor_output + +# build caliper target api jar +# ============================================================ +# This contains just those classes needed for benchmarks to compile. + +include $(CLEAR_VARS) + +LOCAL_MODULE := caliper-api-target +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_CLASS := JAVA_LIBRARIES +LOCAL_SRC_FILES := \ + caliper/src/main/java/com/google/caliper/AfterExperiment.java \ + caliper/src/main/java/com/google/caliper/BeforeExperiment.java \ + caliper/src/main/java/com/google/caliper/Param.java \ + caliper/src/main/java/com/google/caliper/All.java \ + caliper/src/main/java/com/google/caliper/Benchmark.java + +include $(BUILD_JAVA_LIBRARY) + # build caliper tests # ============================================================ +# vogar --expectations $ANDROID_BUILD_TOP/external/caliper/expectations/knownfailures.txt \ + --test-only \ + --classpath $ANDROID_BUILD_TOP/out/host/common/obj/JAVA_LIBRARIES/caliper-tests_intermediates/javalib.jar \ + com.google.caliper include $(CLEAR_VARS) @@ -41,19 +83,49 @@ LOCAL_MODULE := caliper-tests LOCAL_MODULE_TAGS := optional LOCAL_MODULE_CLASS := JAVA_LIBRARIES LOCAL_SRC_FILES := $(call all-java-files-under, caliper/src/test/java/) +LOCAL_JAVA_RESOURCE_DIRS := caliper/src/test/resources +LOCAL_IS_HOST_MODULE := true + +LOCAL_STATIC_JAVA_LIBRARIES := \ + caliper-host \ + junit \ + mockito-host + +# Use Dagger2 annotation processor +PROCESSOR_LIBRARIES := $(DAGGER2_PROCESSOR_LIBRARIES) +PROCESSOR_CLASSES := $(DAGGER2_PROCESSOR_CLASSES) +include external/dagger2/java_annotation_processors.mk + +include $(BUILD_HOST_JAVA_LIBRARY) + +# build caliper examples +# ============================================================ + +include $(CLEAR_VARS) + +LOCAL_MODULE := caliper-examples +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_CLASS := JAVA_LIBRARIES +LOCAL_SRC_FILES := $(call all-java-files-under, examples/src/main/java/) +LOCAL_IS_HOST_MODULE := true LOCAL_STATIC_JAVA_LIBRARIES := \ caliper-host \ - junit + junit \ + mockito-host include $(BUILD_HOST_JAVA_LIBRARY) -# Build dependencies. +# Build host dependencies. # ============================================================ include $(CLEAR_VARS) LOCAL_PREBUILT_JAVA_LIBRARIES := \ - caliper-gson-host:lib/gson-1.7.1$(COMMON_JAVA_PACKAGE_SUFFIX) \ - caliper-java-allocation-instrumenter-host:lib/java-allocation-instrumenter-2.0$(COMMON_JAVA_PACKAGE_SUFFIX) + caliper-gson-host:lib/gson-2.2.2$(COMMON_JAVA_PACKAGE_SUFFIX) \ + caliper-java-allocation-instrumenter-host:lib/java-allocation-instrumenter-2.0$(COMMON_JAVA_PACKAGE_SUFFIX) \ + caliper-jersey-client-host:lib/jersey-client-1.11$(COMMON_JAVA_PACKAGE_SUFFIX) \ + caliper-jersey-core-host:lib/jersey-core-1.11$(COMMON_JAVA_PACKAGE_SUFFIX) \ + caliper-joda-time-host:lib/joda-time-2.1$(COMMON_JAVA_PACKAGE_SUFFIX) \ + caliper-jsr311-api-host:lib/jsr311-api-1.1.1$(COMMON_JAVA_PACKAGE_SUFFIX) include $(BUILD_HOST_PREBUILT) diff --git a/README.android b/README.android index 91617b5..850d2ee 100644 --- a/README.android +++ b/README.android @@ -2,9 +2,14 @@ URL: https://github.com/google/caliper.git License: Apache 2 Description: "Google's Caliper Benchmarking And Measuring Tool" -Version: 0.5-rc1 +Version: 73efbe138dafba57d6a890257961ba83f41b89f2 -Some of the examples do not build, that is an issue with upstream. +This uses the Dagger2 dependency injection framework which runs as an annotation +processor and generates the dependency injecting code. At the moment the +generated code for the caliper-host target can be found at: + out/host/common/gen/JAVA_LIBRARIES/caliper-host_intermediates/annotation_processor_output/ +For the caliper-tests target it can be found at: + out/host/common/gen/JAVA_LIBRARIES/caliper-tests_intermediates/annotation_processor_output/ Local Patches: None diff --git a/caliper/pom.xml b/caliper/pom.xml index befd583..9884724 100644 --- a/caliper/pom.xml +++ b/caliper/pom.xml @@ -1,22 +1,57 @@ -<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/maven-v4_0_0.xsd"> +<?xml version="1.0" encoding="UTF-8"?> +<!-- + ~ Copyright 2011 Google Inc. + ~ + ~ 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. + --> + +<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/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> - <groupId>com.google.caliper</groupId> - <artifactId>caliper</artifactId> - <packaging>jar</packaging> - <version>0.5-rc1</version> - <inceptionYear>2009</inceptionYear> - <name>caliper</name> <parent> <groupId>org.sonatype.oss</groupId> <artifactId>oss-parent</artifactId> <version>7</version> </parent> - <url>http://code.google.com/p/caliper/</url> + + <groupId>com.google.caliper</groupId> + <artifactId>caliper</artifactId> + <version>1.0-SNAPSHOT</version> + <packaging>jar</packaging> + + <name>caliper</name> <description>Caliper: Microbenchmarking Framework for Java</description> - <properties> - <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> - </properties> + + <url>http://code.google.com/p/caliper/</url> + + <inceptionYear>2009</inceptionYear> + + <organization> + <name>Google Inc.</name> + <url>http://www.google.com</url> + </organization> + + <developers> + <developer> + <name>Gregory Kick</name> + <organization>Google Inc.</organization> + </developer> + <developer> + <name>Jesse Wilson</name> + </developer> + </developers> + <licenses> <license> <name>The Apache Software License, Version 2.0</name> @@ -24,55 +59,97 @@ <distribution>repo</distribution> </license> </licenses> + <scm> <connection>scm:git:http://code.google.com/p/caliper/</connection> <developerConnection>scm:git:http://code.google.com/p/caliper/</developerConnection> <url>http://code.google.com/p/caliper/source/browse</url> </scm> + <issueManagement> <system>Google Code Issue Tracking</system> <url>http://code.google.com/p/caliper/issues/list</url> </issueManagement> - <organization> - <name>Google, Inc.</name> - <url>http://www.google.com</url> - </organization> + + <properties> + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> + <gpg.skip>true</gpg.skip> + </properties> + <dependencies> <dependency> <groupId>com.google.code.findbugs</groupId> <artifactId>jsr305</artifactId> <version>1.3.9</version> + <scope>provided</scope><!-- used only for annotations --> </dependency> <dependency> <groupId>com.google.code.gson</groupId> <artifactId>gson</artifactId> - <version>1.7.1</version> + <version>2.2.2</version> + </dependency> + <dependency> + <groupId>joda-time</groupId> + <artifactId>joda-time</artifactId> + <version>2.1</version> + </dependency> + <dependency> + <groupId>com.sun.jersey</groupId> + <artifactId>jersey-client</artifactId> + <version>1.11</version> + </dependency> + <dependency> + <groupId>com.google.dagger</groupId> + <artifactId>dagger</artifactId> + <version>2.1-SNAPSHOT</version> + </dependency> + <dependency> + <groupId>com.google.dagger</groupId> + <artifactId>dagger-compiler</artifactId> + <version>2.1-SNAPSHOT</version> </dependency> <dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> - <version>11.0.1</version> - <scope>compile</scope> + <version>18.0</version> + </dependency> + <dependency> + <groupId>org.hibernate</groupId> + <artifactId>hibernate-core</artifactId> + <version>3.6.7.Final</version> + <scope>provided</scope><!-- used only for annotations --> + </dependency> + <dependency> + <groupId>org.apache.commons</groupId> + <artifactId>commons-math</artifactId> + <version>2.2</version> </dependency> <dependency> <groupId>com.google.code.java-allocation-instrumenter</groupId> <artifactId>java-allocation-instrumenter</artifactId> - <version>2.0</version> + <version>3.0</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> - <version>3.8.2</version> + <version>4.10</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-all</artifactId> + <version>1.9.5</version> <scope>test</scope> </dependency> </dependencies> + <build> <defaultGoal>package</defaultGoal> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> - <version>2.3.2</version> + <version>3.2</version> <configuration> <source>1.6</source> <target>1.6</target> @@ -80,8 +157,13 @@ </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-jar-plugin</artifactId> + <version>2.5</version> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-eclipse-plugin</artifactId> - <version>2.8</version> + <version>2.9</version> <configuration> <downloadSources>true</downloadSources> <downloadJavadocs>true</downloadJavadocs> @@ -90,12 +172,19 @@ </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-release-plugin</artifactId> + <version>2.5.1</version> + <configuration> + <arguments>-DenableCiProfile=true</arguments> + </configuration> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-source-plugin</artifactId> - <version>2.1.2</version> + <version>2.4</version> <executions> <execution> <id>attach-sources</id> - <phase>verify</phase> <goals> <goal>jar-no-fork</goal> </goals> @@ -105,7 +194,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-javadoc-plugin</artifactId> - <version>2.7</version> + <version>2.10.1</version> <executions> <execution> <id>generate-javadocs</id> @@ -117,18 +206,91 @@ </executions> <configuration> <links> - <link>http://download.oracle.com/javase/1.5.0/docs/api/</link> + <link>http://download.oracle.com/javase/1.6.0/docs/api/</link> </links> <version>true</version> <show>public</show> </configuration> </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-shade-plugin</artifactId> + <version>2.3</version> + <executions> + <execution> + <phase>package</phase> + <goals> + <goal>shade</goal> + </goals> + <configuration> + <shadedArtifactAttached>true</shadedArtifactAttached> + <shadedClassifierName>all</shadedClassifierName> + <!-- disable relocation as it is breaking some reflection --> + <!-- <relocations> + <relocation> + <pattern>com.google</pattern> + <shadedPattern>com.google.caliper.shaded.com.google</shadedPattern> + <excludes> + <exclude>com.google.caliper.**</exclude> + </excludes> + </relocation> + <relocation> + <pattern>com.sun.jersey</pattern> + <shadedPattern>com.google.caliper.shaded.com.sun.jersey</shadedPattern> + </relocation> + <relocation> + <pattern>com.sun.ws</pattern> + <shadedPattern>com.google.caliper.shaded.com.sun.ws</shadedPattern> + </relocation> + <relocation> + <pattern>javax.inject</pattern> + <shadedPattern>com.google.caliper.shaded.javax.inject</shadedPattern> + </relocation> + <relocation> + <pattern>javax.ws</pattern> + <shadedPattern>com.google.caliper.shaded.javax.ws</shadedPattern> + </relocation> + <relocation> + <pattern>org.aopalliance</pattern> + <shadedPattern>com.google.caliper.shaded.org.aopalliance</shadedPattern> + </relocation> + <relocation> + <pattern>org.joda</pattern> + <shadedPattern>com.google.caliper.shaded.org.joda</shadedPattern> + </relocation> + <relocation> + <pattern>org.objectweb</pattern> + <shadedPattern>com.google.caliper.shaded.org.objectweb</shadedPattern> + </relocation> + </relocations> --> + <transformers> + <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"> + <manifestEntries> + <Premain-Class>com.google.monitoring.runtime.instrumentation.AllocationInstrumenter</Premain-Class> + <Can-Redefine-Classes>true</Can-Redefine-Classes> + <Can-Retransform-Classes>true</Can-Retransform-Classes> + </manifestEntries> + </transformer> + </transformers> + </configuration> + </execution> + </executions> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-gpg-plugin</artifactId> + <version>1.4</version> + <executions> + <execution> + <id>sign-artifacts</id> + <phase>verify</phase> + <goals> + <goal>sign</goal> + </goals> + </execution> + </executions> + </plugin> </plugins> </build> - <developers> - <developer> - <name>Jesse Wilson</name> - <organization>Google Inc.</organization> - </developer> - </developers> + </project> diff --git a/caliper/src/main/java/com/google/caliper/AfterExperiment.java b/caliper/src/main/java/com/google/caliper/AfterExperiment.java new file mode 100644 index 0000000..19f6cf7 --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/AfterExperiment.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2013 Google Inc. + * + * 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.google.caliper; + +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Annotation; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +/** + * An annotation for methods to be run after an experiment has been performed. + * + * @see BeforeExperiment + */ +@Target(METHOD) +@Retention(RUNTIME) +public @interface AfterExperiment { + /** + * A qualifier for which types of experiments this method should run. For example, annotating a + * method with {@code @AfterExperiment(Benchmark.class)} will cause it to only run for + * {@link Benchmark} experiments. By default, annotated methods run for all experiments. + */ + Class<? extends Annotation> value() default All.class; +} diff --git a/caliper/src/main/java/com/google/caliper/UploadResults.java b/caliper/src/main/java/com/google/caliper/All.java index 7dae7af..08b2456 100644 --- a/caliper/src/main/java/com/google/caliper/UploadResults.java +++ b/caliper/src/main/java/com/google/caliper/All.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 Google Inc. + * Copyright (C) 2013 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,13 +16,11 @@ package com.google.caliper; -import java.io.File; +import java.lang.annotation.Target; /** - * Usage: UploadResults <file_or_dir> + * The default value of lifecycle annotations that indicates it should associate with all benchmark + * types. */ -public class UploadResults { - public static void main(String[] args) { - new Runner().uploadResultsFileOrDir(new File(args[0])); - } -} +@Target({}) // should never actually be applied +public @interface All {} diff --git a/caliper/src/main/java/com/google/caliper/AllocationMeasurer.java b/caliper/src/main/java/com/google/caliper/AllocationMeasurer.java deleted file mode 100644 index 2ec3488..0000000 --- a/caliper/src/main/java/com/google/caliper/AllocationMeasurer.java +++ /dev/null @@ -1,165 +0,0 @@ -/* - * Copyright (C) 2010 Google Inc. - * - * 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.google.caliper; - -import com.google.caliper.UserException.NonConstantMemoryUsage; -import com.google.common.base.Supplier; -import com.google.monitoring.runtime.instrumentation.AllocationRecorder; -import com.google.monitoring.runtime.instrumentation.Sampler; - -public abstract class AllocationMeasurer extends Measurer { - - protected static final int ALLOCATION_DISPLAY_THRESHOLD = 50; - - private boolean log; - private long tempAllocationCount; - private long allocationsToIgnore; - private long numberOfAllocations; - private long allocationCount; - private long outOfThreadAllocationCount; - private boolean recordAllocations; - protected String type; - - protected AllocationMeasurer() { - log = false; - allocationsToIgnore = 0; - numberOfAllocations = 0; - allocationCount = 0; - outOfThreadAllocationCount = 0; - recordAllocations = false; - - final Thread allocatingThread = Thread.currentThread(); - AllocationRecorder.addSampler(new Sampler() { - // allocated {@code newObj} of type {@code desc}, whose size is {@code size}. - // if this was not an array, {@code count} is -1. If it was array, {@code count} is the - // size of the array. - @Override public void sampleAllocation(int count, String desc, Object newObj, long size) { - if (recordAllocations) { - if (Thread.currentThread().equals(allocatingThread)) { - if (log) { - logAllocation(count, desc, size); - } else if (numberOfAllocations == 0) { - log("see first run for list of allocations"); - } - allocationCount = incrementAllocationCount(allocationCount, count, size); - tempAllocationCount++; - numberOfAllocations++; - } else { - outOfThreadAllocationCount = incrementAllocationCount(outOfThreadAllocationCount, count, size); - numberOfAllocations++; - } - } - } - }); - } - - protected abstract long incrementAllocationCount(long orig, int count, long size); - - private void logAllocation(int count, String desc, long size) { - if (numberOfAllocations >= allocationsToIgnore) { - if (numberOfAllocations < ALLOCATION_DISPLAY_THRESHOLD + allocationsToIgnore) { - log("allocating " + desc + (count == -1 ? "" : " array with " + count + " elements") - + " with size " + size + " bytes"); - } else if (numberOfAllocations == ALLOCATION_DISPLAY_THRESHOLD + allocationsToIgnore) { - log("...more allocations..."); - } - } - } - - @Override public MeasurementSet run(Supplier<ConfiguredBenchmark> testSupplier) throws Exception { - - // warm up, for some reason the very first time anything is measured, it will have a few more - // allocations. - measureAllocations(testSupplier.get(), 1, 0); - - // The "one" case serves as a base line. There may be caching, lazy loading, etc going on here. - tempAllocationCount = 0; // count the number of times the sampler is called in one rep - long one = measureAllocationsTotal(testSupplier.get(), 1); - long oneAllocations = tempAllocationCount; - - // we expect that the delta between any two consecutive reps will be constant - tempAllocationCount = 0; // count the number of times the sampler is called in two reps - long two = measureAllocationsTotal(testSupplier.get(), 2); - long twoAllocations = tempAllocationCount; - long expectedDiff = two - one; - // there is some overhead on the first call that we can ignore for the purposes of measurement - long unitsToIgnore = one - expectedDiff; - allocationsToIgnore = 2 * oneAllocations - twoAllocations; - log("ignoring " + allocationsToIgnore + " allocation(s) per measurement as overhead"); - - Measurement[] allocationMeasurements = new Measurement[4]; - log = true; - allocationMeasurements[0] = measureAllocations(testSupplier.get(), 1, unitsToIgnore); - log = false; - for (int i = 1; i < allocationMeasurements.length; i++) { - allocationMeasurements[i] = - measureAllocations(testSupplier.get(), i + 1, unitsToIgnore); - if (Math.round(allocationMeasurements[i].getRaw()) != expectedDiff) { - throw new NonConstantMemoryUsage(); - } - } - - // The above logic guarantees that all the measurements are equal, so we only need to return a - // single measurement. - allocationsToIgnore = 0; - return new MeasurementSet(allocationMeasurements[0]); - } - - private Measurement measureAllocations(ConfiguredBenchmark benchmark, int reps, long toIgnore) - throws Exception { - prepareForTest(); - log(LogConstants.MEASURED_SECTION_STARTING); - resetAllocations(); - recordAllocations = true; - benchmark.run(reps); - recordAllocations = false; - log(LogConstants.MEASURED_SECTION_DONE); - long allocations = (allocationCount - toIgnore) / reps; - long outOfThreadAllocations = outOfThreadAllocationCount; - log(allocations + " " + type + "(s) allocated per rep"); - log(outOfThreadAllocations + " out of thread " + type + "(s) allocated in " + reps + " reps"); - benchmark.close(); - return getMeasurement(benchmark, allocations); - } - - protected abstract Measurement getMeasurement(ConfiguredBenchmark benchmark, long allocations); - - private long measureAllocationsTotal(ConfiguredBenchmark benchmark, int reps) - throws Exception { - prepareForTest(); - log(LogConstants.MEASURED_SECTION_STARTING); - resetAllocations(); - recordAllocations = true; - benchmark.run(reps); - recordAllocations = false; - log(LogConstants.MEASURED_SECTION_DONE); - long allocations = allocationCount; - long outOfThreadAllocations = outOfThreadAllocationCount; - log(allocations + " " + type + "(s) allocated in " + reps + " reps"); - if (outOfThreadAllocations > 0) { - log(outOfThreadAllocations + " out of thread " + type + "(s) allocated in " + reps + " reps"); - } - benchmark.close(); - return allocations; - } - - private void resetAllocations() { - allocationCount = 0; - outOfThreadAllocationCount = 0; - numberOfAllocations = 0; - } -} diff --git a/caliper/src/main/java/com/google/caliper/Arguments.java b/caliper/src/main/java/com/google/caliper/Arguments.java deleted file mode 100644 index cceb079..0000000 --- a/caliper/src/main/java/com/google/caliper/Arguments.java +++ /dev/null @@ -1,420 +0,0 @@ -/* - * Copyright (C) 2010 Google Inc. - * - * 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.google.caliper; - -import com.google.caliper.UserException.DisplayUsageException; -import com.google.caliper.UserException.IncompatibleArgumentsException; -import com.google.caliper.UserException.InvalidParameterValueException; -import com.google.caliper.UserException.MultipleBenchmarkClassesException; -import com.google.caliper.UserException.NoBenchmarkClassException; -import com.google.caliper.UserException.UnrecognizedOptionException; -import com.google.common.base.Splitter; -import com.google.common.collect.Iterables; -import com.google.common.collect.Iterators; -import com.google.common.collect.LinkedHashMultimap; -import com.google.common.collect.Maps; -import com.google.common.collect.Multimap; -import com.google.common.collect.Sets; - -import java.io.File; -import java.util.Iterator; -import java.util.Map; -import java.util.Set; - -/** - * Parse command line arguments for the runner and in-process runner. - */ -public final class Arguments { - private String suiteClassName; - - /** JVMs to run in the benchmark */ - private final Set<String> userVms = Sets.newLinkedHashSet(); - - /** - * Parameter values specified by the user on the command line. Parameters with - * no value in this multimap will get their values from the benchmark suite. - */ - private final Multimap<String, String> userParameters = LinkedHashMultimap.create(); - - /** - * VM parameters like {memory=[-Xmx64,-Xmx128],jit=[-client,-server]} - */ - private final Multimap<String, String> vmParameters = LinkedHashMultimap.create(); - - private int trials = 1; - private long warmupMillis = 3000; - private long runMillis = 1000; - private String timeUnit = null; - private String instanceUnit = null; - private String memoryUnit = null; - private File saveResultsFile = null; - private File uploadResultsFile = null; - private boolean captureVmLog = false; - private boolean printScore = false; - private boolean measureMemory = false; - private boolean debug = false; - private int debugReps = defaultDebugReps; - private MeasurementType measurementType; - private MeasurementType primaryMeasurementType; - - /** - * A signal that indicates a JSON object interleaved with the process output. - * Should be short, but unlikely to show up in the processes natural output. - */ - private String marker = "//ZxJ/"; - - private static final String defaultDelimiter = ","; - private static final int defaultDebugReps = 1000; - - public String getSuiteClassName() { - return suiteClassName; - } - - public Set<String> getUserVms() { - return userVms; - } - - public int getTrials() { - return trials; - } - - public Multimap<String, String> getVmParameters() { - return vmParameters; - } - - public Multimap<String, String> getUserParameters() { - return userParameters; - } - - public long getWarmupMillis() { - return warmupMillis; - } - - public long getRunMillis() { - return runMillis; - } - - public String getTimeUnit() { - return timeUnit; - } - - public String getInstanceUnit() { - return instanceUnit; - } - - public String getMemoryUnit() { - return memoryUnit; - } - - public File getSaveResultsFile() { - return saveResultsFile; - } - - public File getUploadResultsFile() { - return uploadResultsFile; - } - - public boolean getCaptureVmLog() { - return captureVmLog; - } - - public boolean printScore() { - return printScore; - } - - public boolean getMeasureMemory() { - return measureMemory; - } - - public MeasurementType getMeasurementType() { - return measurementType; - } - - public MeasurementType getPrimaryMeasurementType() { - return primaryMeasurementType; - } - - public boolean getDebug() { - return debug; - } - - public int getDebugReps() { - return debugReps; - } - - public String getMarker() { - return marker; - } - - public static Arguments parse(String[] argsArray) { - Arguments result = new Arguments(); - - Iterator<String> args = Iterators.forArray(argsArray); - String delimiter = defaultDelimiter; - Map<String, String> userParameterStrings = Maps.newLinkedHashMap(); - Map<String, String> vmParameterStrings = Maps.newLinkedHashMap(); - String vmString = null; - boolean standardRun = false; - while (args.hasNext()) { - String arg = args.next(); - - if ("--help".equals(arg)) { - throw new DisplayUsageException(); - } - - if (arg.startsWith("-D") || arg.startsWith("-J")) { - - /* - * Handle user parameters (-D) and VM parameters (-J) of these forms: - * - * -Dlength=100 - * -Jmemory=-Xmx1024M - * -Dlength=100,200 - * -Jmemory=-Xmx1024M,-Xmx2048M - * -Dlength 100 - * -Jmemory -Xmx1024M - * -Dlength 100,200 - * -Jmemory -Xmx1024M,-Xmx2048M - */ - - String name; - String value; - int equalsSign = arg.indexOf('='); - if (equalsSign == -1) { - name = arg.substring(2); - value = args.next(); - } else { - name = arg.substring(2, equalsSign); - value = arg.substring(equalsSign + 1); - } - - String previousValue; - if (arg.startsWith("-D")) { - previousValue = userParameterStrings.put(name, value); - } else { - previousValue = vmParameterStrings.put(name, value); - } - if (previousValue != null) { - throw new UserException.DuplicateParameterException(arg); - } - standardRun = true; - // TODO: move warmup/run to caliperrc - } else if ("--captureVmLog".equals(arg)) { - result.captureVmLog = true; - standardRun = true; - } else if ("--warmupMillis".equals(arg)) { - result.warmupMillis = Long.parseLong(args.next()); - standardRun = true; - } else if ("--runMillis".equals(arg)) { - result.runMillis = Long.parseLong(args.next()); - standardRun = true; - } else if ("--trials".equals(arg)) { - String value = args.next(); - try { - result.trials = Integer.parseInt(value); - if (result.trials < 1) { - throw new UserException.InvalidTrialsException(value); - } - } catch (NumberFormatException e) { - throw new UserException.InvalidTrialsException(value); - } - standardRun = true; - } else if ("--vm".equals(arg)) { - if (vmString != null) { - throw new UserException.DuplicateParameterException(arg); - } - vmString = args.next(); - standardRun = true; - } else if ("--delimiter".equals(arg)) { - delimiter = args.next(); - standardRun = true; - } else if ("--timeUnit".equals(arg)) { - result.timeUnit = args.next(); - standardRun = true; - } else if ("--instanceUnit".equals(arg)) { - result.instanceUnit = args.next(); - standardRun = true; - } else if ("--memoryUnit".equals(arg)) { - result.memoryUnit = args.next(); - standardRun = true; - } else if ("--saveResults".equals(arg) || "--xmlSave".equals(arg)) { - // TODO: unsupport legacy --xmlSave - result.saveResultsFile = new File(args.next()); - standardRun = true; - } else if ("--uploadResults".equals(arg)) { - result.uploadResultsFile = new File(args.next()); - } else if ("--printScore".equals(arg)) { - result.printScore = true; - standardRun = true; - } else if ("--measureMemory".equals(arg)) { - result.measureMemory = true; - standardRun = true; - } else if ("--debug".equals(arg)) { - result.debug = true; - } else if ("--debug-reps".equals(arg)) { - String value = args.next(); - try { - result.debugReps = Integer.parseInt(value); - if (result.debugReps < 1) { - throw new UserException.InvalidDebugRepsException(value); - } - } catch (NumberFormatException e) { - throw new UserException.InvalidDebugRepsException(value); - } - } else if ("--marker".equals(arg)) { - result.marker = args.next(); - } else if ("--measurementType".equals(arg)) { - String measurementType = args.next(); - try { - result.measurementType = MeasurementType.valueOf(measurementType); - } catch (Exception e) { - throw new InvalidParameterValueException(arg, measurementType); - } - standardRun = true; - } else if ("--primaryMeasurementType".equals(arg)) { - String measurementType = args.next().toUpperCase(); - try { - result.primaryMeasurementType = MeasurementType.valueOf(measurementType); - } catch (Exception e) { - throw new InvalidParameterValueException(arg, measurementType); - } - standardRun = true; - } else if (arg.startsWith("-")) { - throw new UnrecognizedOptionException(arg); - - } else { - if (result.suiteClassName != null) { - throw new MultipleBenchmarkClassesException(result.suiteClassName, arg); - } - result.suiteClassName = arg; - } - } - - Splitter delimiterSplitter = Splitter.on(delimiter); - - if (vmString != null) { - Iterables.addAll(result.userVms, delimiterSplitter.split(vmString)); - } - - Set<String> duplicates = Sets.intersection( - userParameterStrings.keySet(), vmParameterStrings.keySet()); - if (!duplicates.isEmpty()) { - throw new UserException.DuplicateParameterException(duplicates); - } - - for (Map.Entry<String, String> entry : userParameterStrings.entrySet()) { - result.userParameters.putAll(entry.getKey(), delimiterSplitter.split(entry.getValue())); - } - for (Map.Entry<String, String> entry : vmParameterStrings.entrySet()) { - result.vmParameters.putAll(entry.getKey(), delimiterSplitter.split(entry.getValue())); - } - - if (standardRun && result.uploadResultsFile != null) { - throw new IncompatibleArgumentsException("--uploadResults"); - } - - if (result.suiteClassName == null && result.uploadResultsFile == null) { - throw new NoBenchmarkClassException(); - } - - if (result.primaryMeasurementType != null - && result.primaryMeasurementType != MeasurementType.TIME && !result.measureMemory) { - throw new IncompatibleArgumentsException( - "--primaryMeasurementType " + result.primaryMeasurementType.toString().toLowerCase()); - } - - return result; - } - - public static void printUsage() { - System.out.println(); - System.out.println("Usage: Runner [OPTIONS...] <benchmark>"); - System.out.println(); - System.out.println(" <benchmark>: a benchmark class or suite"); - System.out.println(); - System.out.println("OPTIONS"); - System.out.println(); - System.out.println(" -D<param>=<value>: fix a benchmark parameter to a given value."); - System.out.println(" Multiple values can be supplied by separating them with the"); - System.out.println(" delimiter specified in the --delimiter argument."); - System.out.println(); - System.out.println(" For example: \"-Dfoo=bar,baz,bat\""); - System.out.println(); - System.out.println(" \"benchmark\" is a special parameter that can be used to specify"); - System.out.println(" which benchmark methods to run. For example, if a benchmark has"); - System.out.println(" the method \"timeFoo\", it can be run alone by using"); - System.out.println(" \"-Dbenchmark=Foo\". \"benchmark\" also accepts a delimiter"); - System.out.println(" separated list of methods to run."); - System.out.println(); - System.out.println(" -J<param>=<value>: set a JVM argument to the given value."); - System.out.println(" Multiple values can be supplied by separating them with the"); - System.out.println(" delimiter specified in the --delimiter argument."); - System.out.println(); - System.out.println(" For example: \"-JmemoryMax=-Xmx32M,-Xmx512M\""); - System.out.println(); - System.out.println(" --delimiter <delimiter>: character or string to use as a delimiter"); - System.out.println(" for parameter and vm values."); - System.out.println(" Default: \"" + defaultDelimiter + "\""); - System.out.println(); - System.out.println(" --warmupMillis <millis>: duration to warmup each benchmark"); - System.out.println(); - System.out.println(" --runMillis <millis>: duration to execute each benchmark"); - System.out.println(); - System.out.println(" --captureVmLog: record the VM's just-in-time compiler and GC logs."); - System.out.println(" This may slow down or break benchmark display tools."); - System.out.println(); - System.out.println(" --measureMemory: measure the number of allocations done and the amount of"); - System.out.println(" memory used by invocations of the benchmark."); - System.out.println(" Default: off"); - System.out.println(); - System.out.println(" --vm <vm>: executable to test benchmark on. Multiple VMs may be passed"); - System.out.println(" in as a list separated by the delimiter specified in the"); - System.out.println(" --delimiter argument."); - System.out.println(); - System.out.println(" --timeUnit <unit>: unit of time to use for result. Depends on the units"); - System.out.println(" defined in the benchmark's getTimeUnitNames() method, if defined."); - System.out.println(" Default Options: ns, us, ms, s"); - System.out.println(); - System.out.println(" --instanceUnit <unit>: unit to use for allocation instances result."); - System.out.println(" Depends on the units defined in the benchmark's"); - System.out.println(" getInstanceUnitNames() method, if defined."); - System.out.println(" Default Options: instances, K instances, M instances, B instances"); - System.out.println(); - System.out.println(" --memoryUnit <unit>: unit to use for allocation memory size result."); - System.out.println(" Depends on the units defined in the benchmark's"); - System.out.println(" getMemoryUnitNames() method, if defined."); - System.out.println(" Default Options: B, KB, MB, GB"); - System.out.println(); - System.out.println(" --saveResults <file/dir>: write results to this file or directory"); - System.out.println(); - System.out.println(" --printScore: if present, also display an aggregate score for this run,"); - System.out.println(" where higher is better. This number has no particular meaning,"); - System.out.println(" but can be compared to scores from other runs that use the exact"); - System.out.println(" same arguments."); - System.out.println(); - System.out.println(" --uploadResults <file/dir>: upload this file or directory of files"); - System.out.println(" to the web app. This argument ends Caliper early and is thus"); - System.out.println(" incompatible with all other arguments."); - System.out.println(); - System.out.println(" --debug: run without measurement for use with debugger or profiling."); - System.out.println(); - System.out.println(" --debug-reps: fixed number of reps to run with --debug."); - System.out.println(" Default: \"" + defaultDebugReps + "\""); - - // adding new options? don't forget to update executeForked() - } -} diff --git a/caliper/src/main/java/com/google/caliper/BeforeExperiment.java b/caliper/src/main/java/com/google/caliper/BeforeExperiment.java new file mode 100644 index 0000000..ec67f4c --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/BeforeExperiment.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2013 Google Inc. + * + * 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.google.caliper; + +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Annotation; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +/** + * An annotation for methods to be run before an experiment has been performed. + * + * @see AfterExperiment + */ +@Target(METHOD) +@Retention(RUNTIME) +public @interface BeforeExperiment { + /** + * A qualifier for which types of experiments this method should run. For example, annotating a + * method with {@code @BeforeExperiment(Benchmark.class)} will cause it to only run for + * {@link Benchmark} experiments. By default, annotated methods run for all experiments. + */ + Class<? extends Annotation> value() default All.class; +} diff --git a/caliper/src/main/java/com/google/caliper/Benchmark.java b/caliper/src/main/java/com/google/caliper/Benchmark.java index 06bda82..252017c 100644 --- a/caliper/src/main/java/com/google/caliper/Benchmark.java +++ b/caliper/src/main/java/com/google/caliper/Benchmark.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009 Google Inc. + * Copyright (C) 2013 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,40 +16,64 @@ package com.google.caliper; -import java.util.Map; -import java.util.Set; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; /** - * A collection of benchmarks that share a set of configuration parameters. + * Annotation for benchmark methods. To write a benchmark: + * + * <ol> + * <li>Annotate one or more methods with this annotation. + * <li>Annotate any fields with {@literal @}{@link Param} that should have parameter values + * injected (see {@literal @}{@link Param} for more details) + * <li>Optionally use {@link BeforeExperiment} and {@link AfterExperiment} on setup and teardown + * methods + * </ol> + * + * <p>Since many benchmarks may execute in a shorter duration than is accurately measured by + * available timers, benchmark methods <i>may</i> take either an {@code int} or {@code long} + * argument representing a number of repetitions to perform in a given execution. It is critical + * that the work done in the benchmark method scale linearly to the number of repetitions. + * + * <p>Benchmark methods may return any value. It will be ignored. + * + * <p>This class is instantiated and injected only once per child VM invocation, to measure one + * particular combination of parameters. + * + * <p>For example: <pre> {@code + * public final class MyBenchmark { + * {@literal @}Param FeatureEnum feature; + * {@literal @}Param({"1", "10", "100"}) int size; + * private MyObject objectToBenchmark; + * + * {@literal @}BeforeExperiment void initializeObject() { + * objectToBenchmark = new MyObject(size); + * } + * + * {@literal @}Benchmark int foo(int reps) { + * MyObject object = objectToBenchmark; // copy to local to avoid field access overhead + * int dummy = 0; + * for (int i = 0; i < reps; i++) { + * dummy += object.foo(feature); + * } + * // return a dummy value so the JIT compiler doesn't optimize away the entire method. + * return dummy; + * } + * + * {@literal @}Benchmark int bar() { + * // benchmark another operation of MyObject that doesn't require a reps parameter + * } + * } + * </pre> + * + * <p>The benchmark class MyBenchmark has two benchmark methods ({@code foo} and {@code bar}) and + * two {@link Param Params} ({@code feature} and {@code size}). For each experiment performed by + * Caliper (e.g. {@code foo} with {@code feature == FeatureEnum.A} and {@code size == 100}), + * {@code initializeObject} will be called exactly once, but {@code foo} may be called many times. */ -public interface Benchmark { - - Set<String> parameterNames(); - - Set<String> parameterValues(String parameterName); - - ConfiguredBenchmark createBenchmark(Map<String, String> parameterValues); - - /** - * A mapping of units to their values. Their values must be integers, but all values are relative, - * so if one unit is 1.5 times the size of another, then these units can be expressed as - * {"unit1"=10,"unit2"=15}. The smallest unit given by the function will be used to display - * immediate results when running at the command line. - * - * e.g. 0% Scenario{...} 16.08<SMALLEST-UNIT>; σ=1.72<SMALLEST-UNIT> @ 3 trials - */ - Map<String, Integer> getTimeUnitNames(); - - Map<String, Integer> getInstanceUnitNames(); - - Map<String, Integer> getMemoryUnitNames(); - - /** - * Converts nanoseconds to the smallest unit defined in {@link #getTimeUnitNames()}. - */ - double nanosToUnits(double nanos); - - double instancesToUnits(long instances); - - double bytesToUnits(long bytes); -}
\ No newline at end of file +@Target(METHOD) +@Retention(RUNTIME) +public @interface Benchmark {} diff --git a/caliper/src/main/java/com/google/caliper/CaliperRc.java b/caliper/src/main/java/com/google/caliper/CaliperRc.java deleted file mode 100644 index 21a283f..0000000 --- a/caliper/src/main/java/com/google/caliper/CaliperRc.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (C) 2010 Google Inc. - * - * 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.google.caliper; - -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.util.Properties; - -public class CaliperRc { - public static final CaliperRc INSTANCE = new CaliperRc(); - - private final Properties properties = new Properties(); - - private CaliperRc() { - try { - String caliperRcEnvVar = System.getenv("CALIPERRC"); - File caliperRcFile = (caliperRcEnvVar == null) - ? new File(System.getProperty("user.home"), ".caliperrc") - : new File(caliperRcEnvVar); - if (caliperRcFile.exists()) { - InputStream in = new FileInputStream(caliperRcFile); - properties.load(in); - in.close(); - } else { - // create it with a template - } - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - public String getApiKey() { - return properties.getProperty("apiKey"); - } - - public String getPostUrl() { - return properties.getProperty("postUrl"); - } - - /** - * The HTTP proxy host name and port number separated by a colon, such as - * foo.com:8080 - */ - public String getProxy() { - return properties.getProperty("proxy"); - } -} diff --git a/caliper/src/main/java/com/google/caliper/ConfiguredBenchmark.java b/caliper/src/main/java/com/google/caliper/ConfiguredBenchmark.java deleted file mode 100644 index f150ec1..0000000 --- a/caliper/src/main/java/com/google/caliper/ConfiguredBenchmark.java +++ /dev/null @@ -1,67 +0,0 @@ -/** - * Copyright (C) 2009 Google Inc. - * - * 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.google.caliper; - -import java.util.Map; - -public abstract class ConfiguredBenchmark { - - private final Benchmark underlyingBenchmark; - - protected ConfiguredBenchmark(Benchmark underlyingBenchmark) { - this.underlyingBenchmark = underlyingBenchmark; - } - - /** - * Runs the benchmark through {@code reps} iterations. - * - * @return any object or null. Benchmark implementors may keep an accumulating - * value to prevent the runtime from optimizing away the code under test. - * Such an accumulator value can be returned here. - */ - public abstract Object run(int reps) throws Exception; - - public abstract void close() throws Exception; - - public final Benchmark getBenchmark() { - return underlyingBenchmark; - } - - public final double nanosToUnits(double nanos) { - return underlyingBenchmark.nanosToUnits(nanos); - } - - public final Map<String, Integer> timeUnitNames() { - return underlyingBenchmark.getTimeUnitNames(); - } - - public final double instancesToUnits(long instances) { - return underlyingBenchmark.instancesToUnits(instances); - } - - public final Map<String, Integer> instanceUnitNames() { - return underlyingBenchmark.getInstanceUnitNames(); - } - - public final double bytesToUnits(long bytes) { - return underlyingBenchmark.bytesToUnits(bytes); - } - - public final Map<String, Integer> memoryUnitNames() { - return underlyingBenchmark.getMemoryUnitNames(); - } -} diff --git a/caliper/src/main/java/com/google/caliper/ConsoleReport.java b/caliper/src/main/java/com/google/caliper/ConsoleReport.java deleted file mode 100644 index 8449bda..0000000 --- a/caliper/src/main/java/com/google/caliper/ConsoleReport.java +++ /dev/null @@ -1,427 +0,0 @@ -/** - * Copyright (C) 2009 Google Inc. - * - * 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.google.caliper; - -import com.google.caliper.util.LinearTranslation; -import com.google.common.base.Strings; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.Iterables; -import com.google.common.collect.LinkedHashMultimap; -import com.google.common.collect.Multimap; -import com.google.common.collect.Ordering; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.EnumMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; - -/** - * Prints a report containing the tested values and the corresponding - * measurements. Measurements are grouped by variable using indentation. - * Alongside numeric values, quick-glance ascii art bar charts are printed. - * Sample output (this may not represent the exact form that is produced): - * <pre> - * benchmark d ns linear runtime - * ConcatenationBenchmark 3.14159265 4397 ======================== - * ConcatenationBenchmark -0.0 223 =============== - * FormatterBenchmark 3.14159265 33999 ============================== - * FormatterBenchmark -0.0 26399 ============================= - * </pre> - */ -final class ConsoleReport { - - private static final int barGraphWidth = 30; - - private static final int UNITS_FOR_SCORE_100 = 1; - private static final int UNITS_FOR_SCORE_10 = 1000000000; // 1 s - - private static final LinearTranslation scoreTranslation = - new LinearTranslation(Math.log(UNITS_FOR_SCORE_10), 10, - Math.log(UNITS_FOR_SCORE_100), 100); - - public static final Ordering<Entry<String, Integer>> UNIT_ORDERING = - new Ordering<Entry<String, Integer>>() { - @Override public int compare(Entry<String, Integer> a, Entry<String, Integer> b) { - return a.getValue().compareTo(b.getValue()); - } - }; - - private final List<Variable> variables; - private final Run run; - private final List<Scenario> scenarios; - - private final List<MeasurementType> orderedMeasurementTypes; - private final MeasurementType type; - private final double maxValue; - private final double logMinValue; - private final double logMaxValue; - private final EnumMap<MeasurementType, Integer> decimalDigitsMap = - new EnumMap<MeasurementType, Integer>(MeasurementType.class); - private final EnumMap<MeasurementType, Double> divideByMap = - new EnumMap<MeasurementType, Double>(MeasurementType.class); - private final EnumMap<MeasurementType, String> unitMap = - new EnumMap<MeasurementType, String>(MeasurementType.class); - private final EnumMap<MeasurementType, Integer> measurementColumnLengthMap = - new EnumMap<MeasurementType, Integer>(MeasurementType.class); - private boolean printScore; - - ConsoleReport(Run run, Arguments arguments) { - this.run = run; - unitMap.put(MeasurementType.TIME, arguments.getTimeUnit()); - unitMap.put(MeasurementType.INSTANCE, arguments.getInstanceUnit()); - unitMap.put(MeasurementType.MEMORY, arguments.getMemoryUnit()); - - if (arguments.getMeasureMemory()) { - orderedMeasurementTypes = Arrays.asList( - MeasurementType.TIME, MeasurementType.INSTANCE, MeasurementType.MEMORY); - } else { - orderedMeasurementTypes = Arrays.asList(MeasurementType.TIME); - } - - if (arguments.getPrimaryMeasurementType() != null) { - this.type = arguments.getPrimaryMeasurementType(); - } else { - this.type = MeasurementType.TIME; - } - - double min = Double.POSITIVE_INFINITY; - double max = 0; - - Multimap<String, String> nameToValues = LinkedHashMultimap.create(); - List<Variable> variablesBuilder = new ArrayList<Variable>(); - for (Entry<Scenario, ScenarioResult> entry : this.run.getMeasurements().entrySet()) { - Scenario scenario = entry.getKey(); - double d = entry.getValue().getMeasurementSet(type).medianUnits(); - - min = Math.min(min, d); - max = Math.max(max, d); - - for (Entry<String, String> variable : scenario.getVariables().entrySet()) { - String name = variable.getKey(); - nameToValues.put(name, variable.getValue()); - } - } - - for (Entry<String, Collection<String>> entry : nameToValues.asMap().entrySet()) { - Variable variable = new Variable(entry.getKey(), entry.getValue()); - variablesBuilder.add(variable); - } - - /* - * Figure out how much influence each variable has on the measured value. - * We sum the measurements taken with each value of each variable. For - * variable that have influence on the measurement, the sums will differ - * by value. If the variable has little influence, the sums will be similar - * to one another and close to the overall average. We take the standard - * deviation across each variable's collection of sums. Higher standard - * deviation implies higher influence on the measured result. - */ - double sumOfAllMeasurements = 0; - for (ScenarioResult measurement : this.run.getMeasurements().values()) { - sumOfAllMeasurements += measurement.getMeasurementSet(type).medianUnits(); - } - for (Variable variable : variablesBuilder) { - int numValues = variable.values.size(); - double[] sumForValue = new double[numValues]; - for (Entry<Scenario, ScenarioResult> entry - : this.run.getMeasurements().entrySet()) { - Scenario scenario = entry.getKey(); - sumForValue[variable.index(scenario)] += - entry.getValue().getMeasurementSet(type).medianUnits(); - } - double mean = sumOfAllMeasurements / sumForValue.length; - double stdDeviationSquared = 0; - for (double value : sumForValue) { - double distance = value - mean; - stdDeviationSquared += distance * distance; - } - variable.stdDeviation = Math.sqrt(stdDeviationSquared / numValues); - } - - this.variables = new StandardDeviationOrdering().reverse().sortedCopy(variablesBuilder); - this.scenarios = new ByVariablesOrdering().sortedCopy(this.run.getMeasurements().keySet()); - this.maxValue = max; - this.logMinValue = Math.log(min); - this.logMaxValue = Math.log(max); - - EnumMap<MeasurementType, Integer> digitsBeforeDecimalMap = - new EnumMap<MeasurementType, Integer>(MeasurementType.class); - EnumMap<MeasurementType, Integer> decimalPointMap = - new EnumMap<MeasurementType, Integer>(MeasurementType.class); - for (MeasurementType measurementType : orderedMeasurementTypes) { - double maxForType = 0; - double minForType = Double.POSITIVE_INFINITY; - for (Entry<Scenario, ScenarioResult> entry : this.run.getMeasurements().entrySet()) { - double d = entry.getValue().getMeasurementSet(measurementType).medianUnits(); - minForType = Math.min(minForType, d); - maxForType = Math.max(maxForType, d); - } - - unitMap.put(measurementType, - getUnit(unitMap.get(measurementType), measurementType, minForType)); - - divideByMap.put(measurementType, - (double) getUnits(measurementType).get(unitMap.get(measurementType))); - - int numDigitsInMin = ceil(Math.log10(minForType)); - decimalDigitsMap.put(measurementType, - ceil(Math.max(0, ceil(Math.log10(divideByMap.get(measurementType))) + 3 - numDigitsInMin))); - - digitsBeforeDecimalMap.put(measurementType, - Math.max(1, ceil(Math.log10(maxForType / divideByMap.get(measurementType))))); - - decimalPointMap.put(measurementType, decimalDigitsMap.get(measurementType) > 0 ? 1 : 0); - - measurementColumnLengthMap.put(measurementType, Math.max(maxForType > 0 - ? digitsBeforeDecimalMap.get(measurementType) + decimalPointMap.get(measurementType) - + decimalDigitsMap.get(measurementType) - : 1, unitMap.get(measurementType).trim().length())); - } - - this.printScore = arguments.printScore(); - } - - private String getUnit(String userSuppliedUnit, MeasurementType measurementType, double min) { - Map<String, Integer> units = getUnits(measurementType); - - if (userSuppliedUnit == null) { - List<Entry<String, Integer>> entries = UNIT_ORDERING.reverse().sortedCopy(units.entrySet()); - for (Entry<String, Integer> entry : entries) { - if (min / entry.getValue() >= 1) { - return entry.getKey(); - } - } - // if no unit works, just use the smallest available unit. - return entries.get(entries.size() - 1).getKey(); - } - - if (!units.keySet().contains(userSuppliedUnit)) { - throw new RuntimeException("\"" + unitMap.get(measurementType) + "\" is not a valid unit."); - } - return userSuppliedUnit; - } - - private Map<String, Integer> getUnits(MeasurementType measurementType) { - Map<String, Integer> units = null; - for (Entry<Scenario, ScenarioResult> entry : run.getMeasurements().entrySet()) { - if (units == null) { - units = entry.getValue().getMeasurementSet(measurementType).getUnitNames(); - } else { - if (!units.equals(entry.getValue().getMeasurementSet(measurementType).getUnitNames())) { - throw new RuntimeException("measurement sets for run contain multiple, incompatible unit" - + " sets."); - } - } - } - if (units == null) { - throw new RuntimeException("run has no measurements."); - } - if (units.isEmpty()) { - throw new RuntimeException("no units specified."); - } - return units; - } - - /** - * A variable and the set of values to which it has been assigned. - */ - private static class Variable { - final String name; - final ImmutableList<String> values; - final int maxLength; - double stdDeviation; - - Variable(String name, Collection<String> values) { - this.name = name; - this.values = ImmutableList.copyOf(values); - - int maxLen = name.length(); - for (String value : values) { - maxLen = Math.max(maxLen, value.length()); - } - this.maxLength = maxLen; - } - - String get(Scenario scenario) { - return scenario.getVariables().get(name); - } - - int index(Scenario scenario) { - return values.indexOf(get(scenario)); - } - - boolean isInteresting() { - return values.size() > 1; - } - } - - /** - * Orders the different variables by their standard deviation. This results - * in an appropriate grouping of output values. - */ - private static class StandardDeviationOrdering extends Ordering<Variable> { - public int compare(Variable a, Variable b) { - return Double.compare(a.stdDeviation, b.stdDeviation); - } - } - - /** - * Orders scenarios by the variables. - */ - private class ByVariablesOrdering extends Ordering<Scenario> { - public int compare(Scenario a, Scenario b) { - for (Variable variable : variables) { - int aValue = variable.values.indexOf(variable.get(a)); - int bValue = variable.values.indexOf(variable.get(b)); - int diff = aValue - bValue; - if (diff != 0) { - return diff; - } - } - return 0; - } - } - - void displayResults() { - printValues(); - System.out.println(); - printUninterestingVariables(); - printCharCounts(); - } - - private void printCharCounts() { - int systemOutCharCount = 0; - int systemErrCharCount = 0; - for (ScenarioResult scenarioResult : run.getMeasurements().values()) { - for (MeasurementType measurementType : MeasurementType.values()) { - MeasurementSet measurementSet = scenarioResult.getMeasurementSet(measurementType); - if (measurementSet != null) { - systemOutCharCount += measurementSet.getSystemOutCharCount(); - systemErrCharCount += measurementSet.getSystemErrCharCount(); - } - } - } - if (systemOutCharCount > 0 || systemErrCharCount > 0) { - System.out.println(); - System.out.println("Note: benchmarks printed " + systemOutCharCount - + " characters to System.out and " + systemErrCharCount + " characters to System.err." - + " Use --debug to see this output."); - } - } - - /** - * Prints a table of values. - */ - private void printValues() { - // header - for (Variable variable : variables) { - if (variable.isInteresting()) { - System.out.printf("%" + variable.maxLength + "s ", variable.name); - } - } - // doesn't make sense to show graphs at all for 1 - // scenario, since it leads to vacuous graphs. - boolean showGraphs = scenarios.size() > 1; - - EnumMap<MeasurementType, String> numbersFormatMap = - new EnumMap<MeasurementType, String>(MeasurementType.class); - for (MeasurementType measurementType : orderedMeasurementTypes) { - if (measurementType != type) { - System.out.printf("%" + measurementColumnLengthMap.get(measurementType) + "s ", - unitMap.get(measurementType).trim()); - } - - numbersFormatMap.put(measurementType, - "%" + measurementColumnLengthMap.get(measurementType) - + "." + decimalDigitsMap.get(measurementType) + "f" - + (type == measurementType ? "" : " ")); - } - - System.out.printf("%" + measurementColumnLengthMap.get(type) + "s", unitMap.get(type).trim()); - if (showGraphs) { - System.out.print(" linear runtime"); - } - System.out.println(); - - double sumOfLogs = 0.0; - - for (Scenario scenario : scenarios) { - for (Variable variable : variables) { - if (variable.isInteresting()) { - System.out.printf("%" + variable.maxLength + "s ", variable.get(scenario)); - } - } - ScenarioResult measurement = run.getMeasurements().get(scenario); - sumOfLogs += Math.log(measurement.getMeasurementSet(type).medianUnits()); - - for (MeasurementType measurementType : orderedMeasurementTypes) { - if (measurementType != type) { - System.out.printf(numbersFormatMap.get(measurementType), - measurement.getMeasurementSet(measurementType).medianUnits() / divideByMap.get(measurementType)); - } - } - - System.out.printf(numbersFormatMap.get(type), - measurement.getMeasurementSet(type).medianUnits() / divideByMap.get(type)); - if (showGraphs) { - System.out.printf(" %s", barGraph(measurement.getMeasurementSet(type).medianUnits())); - } - System.out.println(); - } - - if (printScore) { - // arithmetic mean of logs, aka log of geometric mean - double meanLogUnits = sumOfLogs / scenarios.size(); - System.out.format("%nScore: %.3f%n", scoreTranslation.translate(meanLogUnits)); - } - } - - /** - * Prints variables with only one unique value. - */ - private void printUninterestingVariables() { - for (Variable variable : variables) { - if (!variable.isInteresting()) { - System.out.println(variable.name + ": " + Iterables.getOnlyElement(variable.values)); - } - } - } - - /** - * Returns a string containing a bar of proportional width to the specified - * value. - */ - private String barGraph(double value) { - int graphLength = floor(value / maxValue * barGraphWidth); - graphLength = Math.max(1, graphLength); - graphLength = Math.min(barGraphWidth, graphLength); - return Strings.repeat("=", graphLength); - } - - @SuppressWarnings("NumericCastThatLosesPrecision") - private static int floor(double d) { - return (int) d; - } - - @SuppressWarnings("NumericCastThatLosesPrecision") - private static int ceil(double d) { - return (int) Math.ceil(d); - } -} diff --git a/caliper/src/main/java/com/google/caliper/CountingPrintStream.java b/caliper/src/main/java/com/google/caliper/CountingPrintStream.java deleted file mode 100644 index 5c19a61..0000000 --- a/caliper/src/main/java/com/google/caliper/CountingPrintStream.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (C) 2010 Google Inc. - * - * 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.google.caliper; - -import java.io.PrintStream; - -/** - * Counts how many characters were written. - */ -final class CountingPrintStream extends PrintStream { - - private final PrintStream delegate; - private int count; - - CountingPrintStream(PrintStream delegate) { - super(delegate); - this.delegate = delegate; - } - - public int getCount() { - return count; - } - - @Override public void flush() { - delegate.flush(); - } - - @Override public void close() { - delegate.close(); - } - - @Override public boolean checkError() { - return delegate.checkError(); - } - - @Override protected void setError() { - throw new UnsupportedOperationException(); - } - - @Override protected void clearError() { - throw new UnsupportedOperationException(); - } - - @Override public void write(int b) { - count++; - delegate.write(b); - } - - @Override public void write(byte[] buffer, int offset, int length) { - count += length; - delegate.write(buffer, offset, length); - } - - @Override public void print(char[] chars) { - count += chars.length; - delegate.print(chars); - } - - @Override public void print(String s) { - count += s.length(); - delegate.print(s); - } -} diff --git a/caliper/src/main/java/com/google/caliper/DalvikVm.java b/caliper/src/main/java/com/google/caliper/DalvikVm.java deleted file mode 100644 index f89cc28..0000000 --- a/caliper/src/main/java/com/google/caliper/DalvikVm.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (C) 2010 Google Inc. - * - * 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.google.caliper; - -import com.google.common.collect.ImmutableList; - -import java.io.File; -import java.util.ArrayList; -import java.util.List; - -/** - * The dalvikvm run on Android devices via the app_process executable. - */ -final class DalvikVm extends Vm { - - public static boolean isDalvikVm() { - return "Dalvik".equals(System.getProperty("java.vm.name")); - } - - public static String vmName() { - return "app_process"; - } - - @Override public List<String> getVmSpecificOptions(MeasurementType type, Arguments arguments) { - if (!arguments.getCaptureVmLog()) { - return ImmutableList.of(); - } - - List<String> result = new ArrayList<String>(); - if (arguments.getCaptureVmLog()) { - // TODO: currently GC goes to logcat. - // result.add("-verbose:gc"); - } - return result; - } - - @Override public ProcessBuilder newProcessBuilder(File workingDirectory, String classPath, - ImmutableList<String> vmArgs, String className, ImmutableList<String> applicationArgs) { - ProcessBuilder result = new ProcessBuilder(); - result.directory(workingDirectory); - result.command().addAll(vmArgs); - result.command().add("-Djava.class.path=" + classPath); - result.command().add(workingDirectory.getPath()); - result.command().add(className); - result.command().addAll(applicationArgs); - return result; - } -} diff --git a/caliper/src/main/java/com/google/caliper/DebugMeasurer.java b/caliper/src/main/java/com/google/caliper/DebugMeasurer.java deleted file mode 100644 index 64d4b7f..0000000 --- a/caliper/src/main/java/com/google/caliper/DebugMeasurer.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) 2010 Google Inc. - * - * 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.google.caliper; - -import static com.google.common.base.Preconditions.checkArgument; - -import com.google.common.base.Supplier; - -/** - * Measure's the benchmark's per-trial execution time. - */ -class DebugMeasurer extends Measurer { - - private final int reps; - - DebugMeasurer(int reps) { - checkArgument(reps > 0); - this.reps = reps; - } - - @Override public MeasurementSet run(Supplier<ConfiguredBenchmark> testSupplier) - throws Exception { - ConfiguredBenchmark benchmark = testSupplier.get(); - benchmark.run(reps); - benchmark.close(); - return null; - } -} diff --git a/caliper/src/main/java/com/google/caliper/Environment.java b/caliper/src/main/java/com/google/caliper/Environment.java deleted file mode 100644 index 75c2813..0000000 --- a/caliper/src/main/java/com/google/caliper/Environment.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (C) 2010 Google Inc. - * - * 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.google.caliper; - -import com.google.common.annotations.GwtCompatible; - -import java.io.Serializable; -import java.util.HashMap; -import java.util.Map; - -/** - * A description of an environment in which benchmarks are run. - * - * WARNING: a JSON representation of this class is stored on the app engine server. If any changes - * are made to this class, a deserialization adapter must be written for this class to ensure - * backwards compatibility. - * - * <p>Gwt-safe - */ -@SuppressWarnings("serial") -@GwtCompatible -public final class Environment - implements Serializable /* for GWT Serialization */ { - private /*final*/ Map<String, String> propertyMap; - - public Environment(Map<String, String> propertyMap) { - this.propertyMap = new HashMap<String, String>(propertyMap); - } - - public Map<String, String> getProperties() { - return propertyMap; - } - - @Override public boolean equals(Object o) { - return o instanceof Environment - && ((Environment) o).propertyMap.equals(propertyMap); - } - - @Override public int hashCode() { - return propertyMap.hashCode(); - } - - @Override public String toString() { - return propertyMap.toString(); - } - - @SuppressWarnings("unused") - private Environment() {} // for GWT Serialization -} diff --git a/caliper/src/main/java/com/google/caliper/EnvironmentGetter.java b/caliper/src/main/java/com/google/caliper/EnvironmentGetter.java deleted file mode 100644 index 9b7150b..0000000 --- a/caliper/src/main/java/com/google/caliper/EnvironmentGetter.java +++ /dev/null @@ -1,178 +0,0 @@ -/* - * Copyright (C) 2010 Google Inc. - * - * 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.google.caliper; - -import com.google.common.collect.ImmutableMultimap; -import com.google.common.collect.ImmutableMultiset; -import com.google.common.collect.Multimap; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.Reader; -import java.net.InetAddress; -import java.net.UnknownHostException; -import java.util.Collection; -import java.util.HashMap; -import java.util.Map; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -public final class EnvironmentGetter { - - public Environment getEnvironmentSnapshot() { - Map<String, String> propertyMap = new HashMap<String, String>(); - - @SuppressWarnings("unchecked") - Map<String, String> sysProps = (Map<String, String>) (Map) System.getProperties(); - - // Sometimes java.runtime.version is more descriptive than java.version - String version = sysProps.get("java.version"); - String alternateVersion = sysProps.get("java.runtime.version"); - if (alternateVersion != null && alternateVersion.length() > version.length()) { - version = alternateVersion; - } - propertyMap.put("jre.version", version); - - propertyMap.put("jre.vmname", sysProps.get("java.vm.name")); - propertyMap.put("jre.vmversion", sysProps.get("java.vm.version")); - propertyMap.put("jre.availableProcessors", - Integer.toString(Runtime.getRuntime().availableProcessors())); - - String osName = sysProps.get("os.name"); - propertyMap.put("os.name", osName); - propertyMap.put("os.version", sysProps.get("os.version")); - propertyMap.put("os.arch", sysProps.get("os.arch")); - - try { - propertyMap.put("host.name", InetAddress.getLocalHost().getHostName()); - } catch (UnknownHostException ignored) { - } - - if (osName.equals("Linux")) { - getLinuxEnvironment(propertyMap); - } - - return new Environment(propertyMap); - } - - private void getLinuxEnvironment(Map<String, String> propertyMap) { - // the following probably doesn't work on ALL linux - Multimap<String, String> cpuInfo = propertiesFromLinuxFile("/proc/cpuinfo"); - propertyMap.put("host.cpus", Integer.toString(cpuInfo.get("processor").size())); - String s = "cpu cores"; - propertyMap.put("host.cpu.cores", describe(cpuInfo, s)); - propertyMap.put("host.cpu.names", describe(cpuInfo, "model name")); - propertyMap.put("host.cpu.cachesize", describe(cpuInfo, "cache size")); - - Multimap<String, String> memInfo = propertiesFromLinuxFile("/proc/meminfo"); - // TODO redo memInfo.toString() so we don't get square brackets - propertyMap.put("host.memory.physical", memInfo.get("MemTotal").toString()); - propertyMap.put("host.memory.swap", memInfo.get("SwapTotal").toString()); - - getAndroidEnvironment(propertyMap); - } - - private void getAndroidEnvironment(Map<String, String> propertyMap) { - try { - Map<String, String> map = getAndroidProperties(); - String manufacturer = map.get("ro.product.manufacturer"); - String device = map.get("ro.product.device"); - propertyMap.put("android.device", manufacturer + " " + device); // "Motorola sholes" - - String brand = map.get("ro.product.brand"); - String model = map.get("ro.product.model"); - propertyMap.put("android.model", brand + " " + model); // "verizon Droid" - - String release = map.get("ro.build.version.release"); - String id = map.get("ro.build.id"); - propertyMap.put("android.release", release + " " + id); // "Gingerbread GRH07B" - } catch (IOException ignored) { - } - } - - private static String describe(Multimap<String, String> cpuInfo, String s) { - Collection<String> strings = cpuInfo.get(s); - // TODO redo the ImmutableMultiset.toString() call so we don't get square brackets - return (strings.size() == 1) - ? strings.iterator().next() - : ImmutableMultiset.copyOf(strings).toString(); - } - - /** - * Returns the key/value pairs from the specified properties-file like - * reader. Unlike standard Java properties files, {@code reader} is allowed - * to list the same property multiple times. Comments etc. are unsupported. - */ - private static Multimap<String, String> propertiesFileToMultimap(Reader reader) - throws IOException { - ImmutableMultimap.Builder<String, String> result = ImmutableMultimap.builder(); - BufferedReader in = new BufferedReader(reader); - - String line; - while((line = in.readLine()) != null) { - String[] parts = line.split("\\s*\\:\\s*", 2); - if (parts.length == 2) { - result.put(parts[0], parts[1]); - } - } - in.close(); - - return result.build(); - } - - private static Multimap<String, String> propertiesFromLinuxFile(String file) { - try { - Process process = Runtime.getRuntime().exec(new String[]{"/bin/cat", file}); - return propertiesFileToMultimap( - new InputStreamReader(process.getInputStream(), "ISO-8859-1")); - } catch (IOException e) { - return ImmutableMultimap.of(); - } - } - - public static void main(String[] args) { - Environment snapshot = new EnvironmentGetter().getEnvironmentSnapshot(); - for (Map.Entry<String, String> entry : snapshot.getProperties().entrySet()) { - System.out.println(entry.getKey() + " " + entry.getValue()); - } - } - - /** - * Android properties are available from adb shell /system/bin/getprop. That - * program prints Android system properties in this format: - * [ro.product.model]: [Droid] - * [ro.product.brand]: [verizon] - */ - private static Map<String, String> getAndroidProperties() throws IOException { - Map<String, String> result = new HashMap<String, String>(); - - Process process = Runtime.getRuntime().exec(new String[] {"/system/bin/getprop"}); - BufferedReader reader = new BufferedReader( - new InputStreamReader(process.getInputStream(), "ISO-8859-1")); - - Pattern pattern = Pattern.compile("\\[([^\\]]*)\\]: \\[([^\\]]*)\\]"); - String line; - while ((line = reader.readLine()) != null) { - Matcher matcher = pattern.matcher(line); - if (matcher.matches()) { - result.put(matcher.group(1), matcher.group(2)); - } - } - return result; - } -} diff --git a/caliper/src/main/java/com/google/caliper/InProcessRunner.java b/caliper/src/main/java/com/google/caliper/InProcessRunner.java deleted file mode 100644 index 1ef4788..0000000 --- a/caliper/src/main/java/com/google/caliper/InProcessRunner.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright (C) 2010 Google Inc. - * - * 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.google.caliper; - -import com.google.caliper.UserException.ExceptionFromUserCodeException; -import com.google.common.base.Supplier; - -import java.io.OutputStream; -import java.io.PrintStream; -import java.util.List; - -/** - * Executes a benchmark in the current VM. - */ -final class InProcessRunner { - - public void run(String... args) { - Arguments arguments = Arguments.parse(args); - - ScenarioSelection scenarioSelection = new ScenarioSelection(arguments); - - try { - Measurer measurer = getMeasurer(arguments); - List<Scenario> scenarios = scenarioSelection.select(); - // We only expect one scenario right now - if we have more, something has gone wrong. - // This matters for things like reading the measurements. This is only done once, so if - // multiple scenarios are executed, they will be ignored! - if (scenarios.size() != 1) { - throw new IllegalArgumentException("Invalid arguments to subprocess. Expected exactly one " - + "scenario but got " + scenarios.size()); - } - Scenario scenario = scenarios.get(0); - - System.out.println("starting " + scenario); - MeasurementSet measurementSet = run(scenarioSelection, scenario, measurer); - System.out.println(arguments.getMarker() + Json.measurementSetToJson(measurementSet)); - } catch (UserException e) { - throw e; - } catch (Exception e) { - throw new ExceptionFromUserCodeException(e); - } - } - - public MeasurementSet run(final ScenarioSelection scenarioSelection, final Scenario scenario, - Measurer measurer) throws Exception { - Supplier<ConfiguredBenchmark> supplier = new Supplier<ConfiguredBenchmark>() { - @Override public ConfiguredBenchmark get() { - return scenarioSelection.createBenchmark(scenario); - } - }; - - PrintStream out = System.out; - PrintStream err = System.err; - measurer.setLogStream(out); - CountingPrintStream countedOut = new CountingPrintStream(out); - CountingPrintStream countedErr = new CountingPrintStream(err); - System.setOut(countedOut); - System.setErr(countedErr); - try { - MeasurementSet measurementSet = measurer.run(supplier); - if (measurementSet != null) { - measurementSet = measurementSet.plusCharCounts( - countedOut.getCount(), countedErr.getCount()); - } - return measurementSet; - } finally { - System.setOut(out); - System.setErr(err); - } - } - - private Measurer getMeasurer(Arguments arguments) { - if (arguments.getMeasurementType() == MeasurementType.TIME) { - return new TimeMeasurer(arguments.getWarmupMillis(), arguments.getRunMillis()); - } else if (arguments.getMeasurementType() == MeasurementType.INSTANCE) { - return new InstancesAllocationMeasurer(); - } else if (arguments.getMeasurementType() == MeasurementType.MEMORY) { - return new MemoryAllocationMeasurer(); - } else if (arguments.getMeasurementType() == MeasurementType.DEBUG) { - return new DebugMeasurer(arguments.getDebugReps()); - } else { - throw new IllegalArgumentException("unrecognized measurement type: " - + arguments.getMeasurementType()); - } - } - - public static void main(String... args) throws Exception { - try { - new InProcessRunner().run(args); - System.exit(0); // user code may have leave non-daemon threads behind! - } catch (UserException e) { - e.display(); // TODO: send this to the host process - System.out.println(LogConstants.CALIPER_LOG_PREFIX + LogConstants.SCENARIOS_FINISHED); - System.exit(1); - } - } - - public PrintStream nullPrintStream() { - return new PrintStream(new OutputStream() { - public void write(int b) {} - }); - } -} diff --git a/caliper/src/main/java/com/google/caliper/InstancesAllocationMeasurer.java b/caliper/src/main/java/com/google/caliper/InstancesAllocationMeasurer.java deleted file mode 100644 index 3fe89c7..0000000 --- a/caliper/src/main/java/com/google/caliper/InstancesAllocationMeasurer.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (C) 2010 Google Inc. - * - * 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.google.caliper; - -public final class InstancesAllocationMeasurer extends AllocationMeasurer { - - InstancesAllocationMeasurer() { - type = "instance"; - } - - @Override protected long incrementAllocationCount(long oldAllocationCount, int arrayCount, - long size) { - return oldAllocationCount + 1; - } - - @Override protected Measurement getMeasurement(ConfiguredBenchmark benchmark, long allocations) { - return new Measurement(benchmark.instanceUnitNames(), allocations, - benchmark.instancesToUnits(allocations)); - } -} diff --git a/caliper/src/main/java/com/google/caliper/Json.java b/caliper/src/main/java/com/google/caliper/Json.java deleted file mode 100644 index 9e3f809..0000000 --- a/caliper/src/main/java/com/google/caliper/Json.java +++ /dev/null @@ -1,226 +0,0 @@ -/* - * Copyright (C) 2010 Google Inc. - * - * 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.google.caliper; - -import com.google.common.collect.ImmutableMap; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.JsonDeserializationContext; -import com.google.gson.JsonDeserializer; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.google.gson.JsonParseException; -import com.google.gson.JsonPrimitive; -import com.google.gson.JsonSerializationContext; -import com.google.gson.JsonSerializer; -import com.google.gson.reflect.TypeToken; - -import java.lang.reflect.Type; -import java.text.DateFormat; -import java.text.ParseException; -import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Date; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.TimeZone; - -/** - * Ordinarily serialization should be done within the class that is being serialized. However, - * many of these classes are used by GWT, which dies when it sees Gson. - */ -public final class Json { - /** - * This Gson instance must be used when serializing a class that includes a Run as a member - * or as a member of a member (etc.), otherwise the Map<Scenario, ScenarioResult> will not - * be correctly serialized. - */ - private static final Gson GSON_INSTANCE = - new GsonBuilder() - .registerTypeAdapter(Date.class, new DateTypeAdapter()) - .registerTypeAdapter(Run.class, new RunTypeAdapter()) - .registerTypeAdapter(Measurement.class, new MeasurementDeserializer()) - .create(); - - public static Gson getGsonInstance() { - return GSON_INSTANCE; - } - - public static String measurementSetToJson(MeasurementSet measurementSet) { - return new Gson().toJson(measurementSet); - } - - /** - * Attempts to extract a MeasurementSet from a string, assuming it is JSON. If this fails, it - * tries to extract it from the string assuming it is a space-separated list of double values. - */ - public static MeasurementSet measurementSetFromJson(String measurementSetJson) { - try { - return getGsonInstance().fromJson(measurementSetJson, MeasurementSet.class); - } catch (JsonParseException e) { - // might be an old MeasurementSet, so fall back on failure to the old, space separated - // serialization method. - try { - String[] measurementStrings = measurementSetJson.split("\\s+"); - List<Measurement> measurements = new ArrayList<Measurement>(); - for (String s : measurementStrings) { - measurements.add( - new Measurement(ImmutableMap.of("ns", 1, "us", 1000, "ms", 1000000, "s", 1000000000), - Double.valueOf(s), Double.valueOf(s))); - } - // seconds and variations is the default unit - return new MeasurementSet(measurements.toArray(new Measurement[measurements.size()])); - } catch (NumberFormatException ignore) { - throw new IllegalArgumentException("Not a measurement set: " + measurementSetJson); - } - } - } - - public static MeasurementSet measurementSetFromJson(JsonObject measurementSetJson) { - return getGsonInstance().fromJson(measurementSetJson, MeasurementSet.class); - } - - /** - * Backwards compatibility! - */ - private static class MeasurementDeserializer implements JsonDeserializer<Measurement> { - @Override public Measurement deserialize(JsonElement jsonElement, Type type, - JsonDeserializationContext context) throws JsonParseException { - JsonObject obj = jsonElement.getAsJsonObject(); - if (obj.has("raw") && obj.has("processed")) { - return new Measurement( - context.<Map<String, Integer>>deserialize(obj.get("unitNames"), - new TypeToken<Map<String, Integer>>() {}.getType()), - context.<Double>deserialize(obj.get("raw"), Double.class), - context.<Double>deserialize(obj.get("processed"), Double.class)); - } - if (obj.has("nanosPerRep") && obj.has("unitsPerRep") && obj.has("unitNames")) { - return new Measurement( - context.<Map<String, Integer>>deserialize(obj.get("unitNames"), - new TypeToken<Map<String, Integer>>() {}.getType()), - context.<Double>deserialize(obj.get("nanosPerRep"), Double.class), - context.<Double>deserialize(obj.get("unitsPerRep"), Double.class)); - } - throw new JsonParseException(obj.toString()); - } - } - - /** - * This adapter is necessary because gson doesn't handle Maps more complex than Map<String, ...> - * in a useful way. For example, Map<Scenario, ScenarioResult>'s serialized version simply uses - * Scenario.toString() as the keys. This adapter stores this Map as lists of - * KeyValuePair<Scenario, ScenarioResult> instead, to preserve the Scenario objects on - * deserialization. - */ - private static class RunTypeAdapter implements JsonSerializer<Run>, JsonDeserializer<Run> { - - @Override public Run deserialize(JsonElement jsonElement, Type type, - JsonDeserializationContext context) throws JsonParseException { - - List<KeyValuePair<Scenario, ScenarioResult>> mapList = context.deserialize( - jsonElement.getAsJsonObject().get("measurements"), - new TypeToken<List<KeyValuePair<Scenario, ScenarioResult>>>() {}.getType()); - Map<Scenario, ScenarioResult> measurements = new LinkedHashMap<Scenario, ScenarioResult>(); - for (KeyValuePair<Scenario, ScenarioResult> entry : mapList) { - measurements.put(entry.getKey(), entry.getValue()); - } - - String benchmarkName = - context.deserialize(jsonElement.getAsJsonObject().get("benchmarkName"), String.class); - - Date executedTimestamp = context.deserialize( - jsonElement.getAsJsonObject().get("executedTimestamp"), Date.class); - - return new Run(measurements, benchmarkName, executedTimestamp); - } - - @Override public JsonElement serialize(Run run, Type type, JsonSerializationContext context) { - JsonObject result = new JsonObject(); - result.add("benchmarkName", context.serialize(run.getBenchmarkName())); - result.add("executedTimestamp", context.serialize(run.getExecutedTimestamp())); - - List<KeyValuePair<Scenario, ScenarioResult>> mapList = - new ArrayList<KeyValuePair<Scenario, ScenarioResult>>(); - for (Map.Entry<Scenario, ScenarioResult> entry : run.getMeasurements().entrySet()) { - mapList.add(new KeyValuePair<Scenario, ScenarioResult>(entry.getKey(), entry.getValue())); - } - result.add("measurements", context.serialize(mapList, - new TypeToken<List<KeyValuePair<Scenario, ScenarioResult>>>() {}.getType())); - - return result; - } - } - - private static class DateTypeAdapter implements JsonSerializer<Date>, JsonDeserializer<Date> { - private final DateFormat dateFormat; - - private DateTypeAdapter() { - dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssz", Locale.US); - dateFormat.setTimeZone(TimeZone.getTimeZone("UTC")); - } - - @Override public synchronized JsonElement serialize(Date date, Type type, - JsonSerializationContext jsonSerializationContext) { - return new JsonPrimitive(dateFormat.format(date)); - } - - @Override public synchronized Date deserialize(JsonElement jsonElement, Type type, - JsonDeserializationContext jsonDeserializationContext) { - String dateString = jsonElement.getAsString(); - // first try to parse as an ISO 8601 date - try { - return dateFormat.parse(dateString); - } catch (ParseException ignored) { - } - // next, try a GSON-style locale-specific dates (for Caliper r282 and earlier) - try { - return DateFormat.getDateTimeInstance().parse(dateString); - } catch (ParseException ignored) { - } - throw new JsonParseException(dateString); - } - } - - /** - * This is similar to the Map.Entry class, but is necessary since Entrys are not supported - * by gson. - */ - private static class KeyValuePair<K, V> { - private K k; - private V v; - - KeyValuePair(K k, V v) { - this.k = k; - this.v = v; - } - - public K getKey() { - return k; - } - - public V getValue() { - return v; - } - - @SuppressWarnings("unused") - private KeyValuePair() {} // for gson - } - - private Json() {} // static class -} diff --git a/caliper/src/main/java/com/google/caliper/LogConstants.java b/caliper/src/main/java/com/google/caliper/LogConstants.java deleted file mode 100644 index 09acebe..0000000 --- a/caliper/src/main/java/com/google/caliper/LogConstants.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (C) 2010 Google Inc. - * - * 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.google.caliper; - -public final class LogConstants { - /** - * Must be prepended to line of XML that represents normalized scenario. - */ - public static final String SCENARIO_JSON_PREFIX = "[scenario] "; - public static final String MEASUREMENT_JSON_PREFIX = "[measurement] "; - - /** - * Must be prepended to any logs that are to be included in the run event log. - */ - public static final String CALIPER_LOG_PREFIX = "[caliper] "; - public static final String SCENARIOS_STARTING = "[starting scenarios]"; - public static final String STARTING_SCENARIO_PREFIX = "[starting scenario] "; - public static final String SCENARIO_FINISHED = "[scenario finished]"; - public static final String SCENARIOS_FINISHED = "[scenarios finished]"; - - /** - * All events will be logged from when {@code MEASURED_SECTION_STARTING} is logged until - * {@code MEASURED_SECTION_DONE} is logged. - */ - public static final String MEASURED_SECTION_STARTING = "[starting measured section]"; - public static final String MEASURED_SECTION_DONE = "[done measured section]"; - - private LogConstants() {} -} diff --git a/caliper/src/main/java/com/google/caliper/Measurement.java b/caliper/src/main/java/com/google/caliper/Measurement.java deleted file mode 100644 index 35865b6..0000000 --- a/caliper/src/main/java/com/google/caliper/Measurement.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright (C) 2010 Google Inc. - * - * 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.google.caliper; - -import com.google.common.annotations.GwtCompatible; - -import java.io.Serializable; -import java.util.Comparator; -import java.util.HashMap; -import java.util.Map; - -/** - * Represents a measurement of a single run of a benchmark. - */ -@SuppressWarnings("serial") -@GwtCompatible -public final class Measurement - implements Serializable /* for GWT */ { - - public static final Comparator<Measurement> SORT_BY_NANOS = new Comparator<Measurement>() { - @Override public int compare(Measurement a, Measurement b) { - double aNanos = a.getRaw(); - double bNanos = b.getRaw(); - return Double.compare(aNanos, bNanos); - } - }; - - public static final Comparator<Measurement> SORT_BY_UNITS = new Comparator<Measurement>() { - @Override public int compare(Measurement a, Measurement b) { - double aNanos = a.getProcessed(); - double bNanos = b.getProcessed(); - return Double.compare(aNanos, bNanos); - } - }; - - private /*final*/ double raw; - private /*final*/ double processed; - private /*final*/ Map<String, Integer> unitNames; - - public Measurement(Map<String, Integer> unitNames, double raw, double processed) { - this.unitNames = new HashMap<String, Integer>(unitNames); - this.raw = raw; - this.processed = processed; - } - - public Map<String, Integer> getUnitNames() { - return new HashMap<String, Integer>(unitNames); - } - - public double getRaw() { - return raw; - } - - public double getProcessed() { - return processed; - } - - @Override public boolean equals(Object o) { - return o instanceof Measurement - && ((Measurement) o).raw == raw - && ((Measurement) o).processed == processed - && ((Measurement) o).unitNames.equals(unitNames); - } - - @Override public int hashCode() { - return (int) raw // Double.doubleToLongBits doesn't exist on GWT - + (int) processed * 37 - + unitNames.hashCode() * 1373; - } - - @Override public String toString() { - return (raw != processed - ? raw + "/" + processed - : Double.toString(raw)); - } - - @SuppressWarnings("unused") - private Measurement() {} /* for GWT */ -} diff --git a/caliper/src/main/java/com/google/caliper/MeasurementSet.java b/caliper/src/main/java/com/google/caliper/MeasurementSet.java deleted file mode 100644 index eb0b42a..0000000 --- a/caliper/src/main/java/com/google/caliper/MeasurementSet.java +++ /dev/null @@ -1,264 +0,0 @@ -/* - * Copyright (C) 2010 Google Inc. - * - * 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.google.caliper; - -import com.google.common.annotations.GwtCompatible; - -import java.io.Serializable; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -/** - * A collection of measurements of the same scenario. - */ -@SuppressWarnings("serial") -@GwtCompatible -public final class MeasurementSet - implements Serializable /* for GWT Serialization */ { - - private /*final*/ List<Measurement> measurements; - /** - * Mapping of user-defined units to relative sizes. - */ - private /*final*/ Map<String, Integer> unitNames; - - private /*final*/ int systemOutCharCount; - private /*final*/ int systemErrCharCount; - - public MeasurementSet(Measurement... measurements) { - this(0, 0, getUnitNamesFromMeasurements(measurements), Arrays.asList(measurements)); - } - - private static Map<String, Integer> getUnitNamesFromMeasurements(Measurement... measurements) { - Map<String, Integer> unitNameToAssign = null; - for (Measurement measurement : measurements) { - if (unitNameToAssign == null) { - unitNameToAssign = new HashMap<String, Integer>(measurement.getUnitNames()); - } else if (!unitNameToAssign.equals(measurement.getUnitNames())) { - throw new IllegalArgumentException("incompatible unit names: " + unitNameToAssign + " and " - + measurement.getUnitNames()); - } - } - return unitNameToAssign; - } - - /** - * Constructor to use directly from plusMeasurement. Skips some excessive checking and takes a - * list directly. - */ - private MeasurementSet(int systemOutCharCount, int systemErrCharCount, - Map<String, Integer> unitNames, List<Measurement> measurements) { - this.systemOutCharCount = systemOutCharCount; - this.systemErrCharCount = systemErrCharCount; - this.unitNames = unitNames; - this.measurements = measurements; - } - - /** - * This is the same as getUnitNames(), but is for backwards compatibility on the server - * when null pointer exceptions need to be avoided. - */ - public Map<String, Integer> getUnitNames(Map<String, Integer> defaultValue) { - if (unitNames == null) { - return defaultValue; - } - return new HashMap<String, Integer>(unitNames); - } - - public Map<String, Integer> getUnitNames() { - return new HashMap<String, Integer>(unitNames); - } - - public List<Measurement> getMeasurements() { - return new ArrayList<Measurement>(measurements); - } - - public int size() { - return measurements.size(); - } - - public int getSystemOutCharCount() { - return systemOutCharCount; - } - - public int getSystemErrCharCount() { - return systemErrCharCount; - } - - public List<Double> getMeasurementsRaw() { - List<Double> measurementRaw = new ArrayList<Double>(); - for (Measurement measurement : measurements) { - measurementRaw.add(measurement.getRaw()); - } - return measurementRaw; - } - - public List<Double> getMeasurementUnits() { - List<Double> measurementUnits = new ArrayList<Double>(); - for (Measurement measurement : measurements) { - measurementUnits.add(measurement.getProcessed()); - } - return measurementUnits; - } - - /** - * Returns the median measurement, with respect to raw units. - */ - public double medianRaw() { - return median(getMeasurementsRaw()); - } - - /** - * Returns the median measurement, with respect to user-defined units. - */ - public double medianUnits() { - return median(getMeasurementUnits()); - } - - private double median(List<Double> doubles) { - Collections.sort(doubles); - int n = doubles.size(); - return (n % 2 == 0) - ? (doubles.get(n / 2 - 1) + doubles.get(n / 2)) / 2 - : doubles.get(n / 2); - } - - /** - * Returns the average measurement with respect to raw units. - */ - public double meanRaw() { - return mean(getMeasurementsRaw()); - } - - /** - * Returns the average measurement with respect to user-defined units. - */ - public double meanUnits() { - return mean(getMeasurementUnits()); - } - - private double mean(List<Double> doubles) { - double sum = 0; - for (double d : doubles) { - sum += d; - } - return sum / doubles.size(); - } - - public double standardDeviationRaw() { - return standardDeviation(getMeasurementsRaw()); - } - - public double standardDeviationUnits() { - return standardDeviation(getMeasurementUnits()); - } - - /** - * Returns the standard deviation of the measurements. - */ - private double standardDeviation(List<Double> doubles) { - double mean = mean(doubles); - double sumOfSquares = 0; - for (double d : doubles) { - double delta = (d - mean); - sumOfSquares += (delta * delta); - } - return Math.sqrt(sumOfSquares / (doubles.size() - 1)); - } - - public double minRaw() { - return min(getMeasurementsRaw()); - } - - public double minUnits() { - return min(getMeasurementUnits()); - } - - /** - * Returns the minimum measurement. - */ - private double min(List<Double> doubles) { - Collections.sort(doubles); - return doubles.get(0); - } - - public double maxRaw() { - return max(getMeasurementsRaw()); - } - - public double maxUnits() { - return max(getMeasurementUnits()); - } - - /** - * Returns the maximum measurement. - */ - private double max(List<Double> doubles) { - Collections.sort(doubles, Collections.reverseOrder()); - return doubles.get(0); - } - - /** - * Returns a new measurement set that contains the measurements in this set - * plus the given additional measurement. - */ - public MeasurementSet plusMeasurement(Measurement measurement) { - // verify that this Measurement is compatible with this MeasurementSet - if (unitNames != null && !unitNames.equals(measurement.getUnitNames())) { - throw new IllegalArgumentException("new measurement incompatible with units of measurement " - + "set. Expected " + unitNames + " but got " + measurement.getUnitNames()); - } - - List<Measurement> resultMeasurements = new ArrayList<Measurement>(measurements); - resultMeasurements.add(measurement); - Map<String, Integer> newUnitNames = unitNames == null ? measurement.getUnitNames() : unitNames; - return new MeasurementSet(systemOutCharCount, systemErrCharCount, - newUnitNames, resultMeasurements); - } - - public MeasurementSet plusCharCounts(int systemOutCharCount, int systemErrCharCount) { - return new MeasurementSet(this.systemOutCharCount + systemOutCharCount, - this.systemErrCharCount + systemErrCharCount, unitNames, measurements); - } - - @Override public boolean equals(Object o) { - return o instanceof MeasurementSet - && ((MeasurementSet) o).measurements.equals(measurements) - && ((MeasurementSet) o).unitNames.equals(unitNames) - && ((MeasurementSet) o).systemOutCharCount == systemOutCharCount - && ((MeasurementSet) o).systemErrCharCount == systemErrCharCount; - } - - @Override public int hashCode() { - return measurements.hashCode() - + unitNames.hashCode() * 37 - + systemOutCharCount * 1373 - + systemErrCharCount * 53549; - } - - @Override public String toString() { - return measurements.toString() + " " + unitNames + " " - + systemOutCharCount + "/" + systemErrCharCount; - } - - @SuppressWarnings("unused") - private MeasurementSet() {} // for GWT Serialization -} diff --git a/caliper/src/main/java/com/google/caliper/Measurer.java b/caliper/src/main/java/com/google/caliper/Measurer.java deleted file mode 100644 index 93002e6..0000000 --- a/caliper/src/main/java/com/google/caliper/Measurer.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (C) 2010 Google Inc. - * - * 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.google.caliper; - -import com.google.common.base.Supplier; - -import java.io.PrintStream; - -abstract class Measurer { - - private PrintStream logStream = System.out; - - /** - * Sets the stream used to log caliper events. - */ - void setLogStream(PrintStream logStream) { - this.logStream = logStream; - } - - public abstract MeasurementSet run(Supplier<ConfiguredBenchmark> testSupplier) throws Exception; - - protected void prepareForTest() { - System.gc(); - System.gc(); - } - - protected final void log(String message) { - logStream.println(LogConstants.CALIPER_LOG_PREFIX + message); - } -} diff --git a/caliper/src/main/java/com/google/caliper/MemoryAllocationMeasurer.java b/caliper/src/main/java/com/google/caliper/MemoryAllocationMeasurer.java deleted file mode 100644 index a28aabf..0000000 --- a/caliper/src/main/java/com/google/caliper/MemoryAllocationMeasurer.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (C) 2010 Google Inc. - * - * 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.google.caliper; - -public final class MemoryAllocationMeasurer extends AllocationMeasurer { - - public MemoryAllocationMeasurer() { - type = "byte"; - } - - @Override protected long incrementAllocationCount(long oldAllocationCount, int arrayCount, - long size) { - return oldAllocationCount + size; - } - - @Override protected Measurement getMeasurement(ConfiguredBenchmark benchmark, long allocations) { - return new Measurement(benchmark.memoryUnitNames(), allocations, - benchmark.bytesToUnits(allocations)); - } -} diff --git a/caliper/src/main/java/com/google/caliper/Param.java b/caliper/src/main/java/com/google/caliper/Param.java index f4cdf45..82e3471 100644 --- a/caliper/src/main/java/com/google/caliper/Param.java +++ b/caliper/src/main/java/com/google/caliper/Param.java @@ -29,12 +29,6 @@ import java.lang.annotation.Target; * <ul> * <li>The command line, if specified using {@code -Dname=value1,value2,value3} * <li>Otherwise, the {@link #value()} list given in the annotation - * <li>Otherwise, Caliper looks for a static method named {@code paramName + "Values"} (for - * example, if the parameter field is {@code size}, it looks for {@code sizeValues()}). The - * method can return any subtype of {@link Iterable}. The contents of that iterable are used as - * the parameter values. - * <li>Otherwise, Caliper repeats the previous check looking for a static <em>field</em> instead - * of a method. * <li>Otherwise, if the parameter type is either {@code boolean} or an {@code enum} type, Caliper * assumes you want all possible values. * <li>Finally, if none of the above match, Caliper will display an error and exit. @@ -47,7 +41,7 @@ import java.lang.annotation.Target; * * <p>Caliper will test every possible combination of parameter values for your benchmark. For * example, if you have two parameters, {@code -Dletter=a,b,c -Dnumber=1,2}, Caliper will construct - * six independent "scenarios" and perform measurement for each one. + * six independent "scenarios" and perform measurement for each one. */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) diff --git a/caliper/src/main/java/com/google/caliper/Parameter.java b/caliper/src/main/java/com/google/caliper/Parameter.java deleted file mode 100644 index c79bc86..0000000 --- a/caliper/src/main/java/com/google/caliper/Parameter.java +++ /dev/null @@ -1,202 +0,0 @@ -/* - * Copyright (C) 2009 Google Inc. - * - * 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.google.caliper; - -import com.google.common.annotations.VisibleForTesting; - -import java.lang.reflect.Field; -import java.lang.reflect.Member; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.EnumSet; -import java.util.Map; -import java.util.Set; -import java.util.TreeMap; - -/** - * A parameter in a {@link SimpleBenchmark}. - * - * @param <T> the (possibly wrapped) type of the parameter field, such as {@link - * String} or {@link Integer} - */ -abstract class Parameter<T> { - - private final Field field; - - private Parameter(Field field) { - this.field = field; - } - - /** - * Returns all parameters for the given class. - */ - public static Map<String, Parameter<?>> forClass(Class<? extends Benchmark> suiteClass) { - Map<String, Parameter<?>> parameters = new TreeMap<String, Parameter<?>>(); - for (Field field : suiteClass.getDeclaredFields()) { - if (field.isAnnotationPresent(Param.class)) { - field.setAccessible(true); - Parameter<?> parameter = forField(suiteClass, field); - parameters.put(parameter.getName(), parameter); - } - } - return parameters; - } - - @VisibleForTesting - static Parameter<?> forField( - Class<? extends Benchmark> suiteClass, final Field field) { - // First check for String values on the annotation itself - final Object[] defaults = field.getAnnotation(Param.class).value(); - if (defaults.length > 0) { - return new Parameter<Object>(field) { - @Override public Iterable<Object> values() throws Exception { - return Arrays.asList(defaults); - } - }; - // TODO: or should we continue so we can give an error/warning if params are also give in a - // method or field? - } - - Parameter<?> result = null; - Type returnType = null; - Member member = null; - - // Now check for a fooValues() method - try { - final Method valuesMethod = suiteClass.getDeclaredMethod(field.getName() + "Values"); - if (!Modifier.isStatic(valuesMethod.getModifiers())) { - throw new ConfigurationException("Values method must be static " + member); - } - valuesMethod.setAccessible(true); - member = valuesMethod; - returnType = valuesMethod.getGenericReturnType(); - result = new Parameter<Object>(field) { - @SuppressWarnings("unchecked") // guarded below - @Override public Iterable<Object> values() throws Exception { - return (Iterable<Object>) valuesMethod.invoke(null); - } - }; - } catch (NoSuchMethodException ignored) { - } - - // Now check for a fooValues field - try { - final Field valuesField = suiteClass.getDeclaredField(field.getName() + "Values"); - if (!Modifier.isStatic(valuesField.getModifiers())) { - throw new ConfigurationException("Values field must be static " + member); - } - valuesField.setAccessible(true); - member = valuesField; - if (result != null) { - throw new ConfigurationException("Two values members defined for " + field); - } - returnType = valuesField.getGenericType(); - result = new Parameter<Object>(field) { - @SuppressWarnings("unchecked") // guarded below - @Override public Iterable<Object> values() throws Exception { - return (Iterable<Object>) valuesField.get(null); - } - }; - } catch (NoSuchFieldException ignored) { - } - - // If there isn't a values member but the parameter is an enum, we default - // to EnumSet.allOf. - if (member == null && field.getType().isEnum()) { - returnType = Collection.class; - result = new Parameter<Object>(field) { - // TODO: figure out the simplest way to make this compile and be green in IDEA too - @SuppressWarnings({"unchecked", "RawUseOfParameterizedType", "RedundantCast"}) - // guarded above - @Override public Iterable<Object> values() throws Exception { - Set<Enum> set = EnumSet.allOf((Class<Enum>) field.getType()); - return Collections.<Object>unmodifiableSet(set); - } - }; - } - - // If it's boolean, default to (true, false) - if (member == null && field.getType() == boolean.class) { - returnType = Collection.class; - result = new Parameter<Object>(field) { - @Override public Iterable<Object> values() throws Exception { - return Arrays.<Object>asList(Boolean.TRUE, Boolean.FALSE); - } - }; - } - - if (result == null) { - return new Parameter<Object>(field) { - @Override public Iterable<Object> values() { - // TODO: need tests to make sure this fails properly when no cmdline params given and - // works properly when they are given. Also, can we restructure the code so that we - // just throw here instead of later? - return Collections.emptySet(); - } - }; - } else if (!isValidReturnType(returnType)) { - throw new ConfigurationException("Invalid return type " + returnType - + " for values member " + member + "; must be Collection"); - } - return result; - } - - private static boolean isValidReturnType(Type type) { - if (type instanceof Class) { - return isIterableClass(type); - } - if (type instanceof ParameterizedType) { - return isIterableClass(((ParameterizedType) type).getRawType()); - } - return false; - } - - private static boolean isIterableClass(Type returnClass) { - return Iterable.class.isAssignableFrom((Class<?>) returnClass); - } - - /** - * Sets the value of this property to the specified value for the given suite. - */ - public void set(Benchmark suite, Object value) throws Exception { - field.set(suite, value); - } - - /** - * Returns the available values of the property as specified by the suite. - */ - public abstract Iterable<T> values() throws Exception; - - /** - * Returns the parameter's type, such as double.class. - */ - public Type getType() { - return field.getGenericType(); - } - - /** - * Returns the field's name. - */ - String getName() { - return field.getName(); - } -} diff --git a/caliper/src/main/java/com/google/caliper/Result.java b/caliper/src/main/java/com/google/caliper/Result.java deleted file mode 100644 index c57f5a3..0000000 --- a/caliper/src/main/java/com/google/caliper/Result.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (C) 2010 Google Inc. - * - * 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.google.caliper; - -/** - * Represents an invocation of a benchmark, including the run itself, as well as the environment - * in which the run occurred. - */ -public final class Result { - private /*final*/ Run run; - private /*final*/ Environment environment; - - public Result(Run run, Environment environment) { - this.run = run; - this.environment = environment; - } - - public Run getRun() { - return run; - } - - public Environment getEnvironment() { - return environment; - } - - @Override public boolean equals(Object o) { - return o instanceof Result - && ((Result) o).run.equals(run) - && ((Result) o).environment.equals(environment); - } - - @Override public int hashCode() { - return run.hashCode() * 37 + environment.hashCode(); - } - - @Override public String toString() { - return run + "@" + environment; - } - - @SuppressWarnings("unused") - private Result() {} // for gson -} diff --git a/caliper/src/main/java/com/google/caliper/ResultsReader.java b/caliper/src/main/java/com/google/caliper/ResultsReader.java deleted file mode 100644 index 2396dd3..0000000 --- a/caliper/src/main/java/com/google/caliper/ResultsReader.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (C) 2010 Google Inc. - * - * 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.google.caliper; - -import com.google.gson.JsonParseException; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; - -/** - * Helps with deserialization of results, given uncertainty about the format (xml or json) they - * are in. - */ -public final class ResultsReader { - public Result getResult(InputStream in) throws IOException { - // save input into a byte array since we may need to read it twice - byte[] postedData = readAllBytes(in); - Result result; - InputStreamReader baisJsonReader = new InputStreamReader(new ByteArrayInputStream(postedData)); - try { - result = Json.getGsonInstance().fromJson(baisJsonReader, Result.class); - } catch (JsonParseException e) { - // probably an old client is trying to send data, so try to parse it as XML instead. - ByteArrayInputStream baisXml = new ByteArrayInputStream(postedData); - try { - result = Xml.resultFromXml(baisXml); - } catch (Exception e2) { - throw new RuntimeException(e); - } finally { - baisXml.close(); - } - } finally { - baisJsonReader.close(); - } - return result; - } - - private byte[] readAllBytes(InputStream in) throws IOException { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - byte[] buf = new byte[4096]; - int read; - while ((read = in.read(buf)) != -1) { - baos.write(buf, 0, read); - } - return baos.toByteArray(); - } -} diff --git a/caliper/src/main/java/com/google/caliper/Run.java b/caliper/src/main/java/com/google/caliper/Run.java deleted file mode 100644 index 00f9350..0000000 --- a/caliper/src/main/java/com/google/caliper/Run.java +++ /dev/null @@ -1,93 +0,0 @@ -/** - * Copyright (C) 2009 Google Inc. - * - * 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.google.caliper; - -import com.google.common.annotations.GwtCompatible; - -import java.io.Serializable; -import java.util.Date; -import java.util.LinkedHashMap; -import java.util.Map; - -/** - * The complete result of a benchmark suite run. - * - * WARNING: a JSON representation of this class is stored on the app engine server. If any changes - * are made to this class, a deserialization adapter must be written for this class to ensure - * backwards compatibility. - * - * <p>Gwt-safe. - */ -@SuppressWarnings("serial") -@GwtCompatible -public final class Run - implements Serializable /* for GWT Serialization */ { - - private /*final*/ Map<Scenario, ScenarioResult> measurements; - private /*final*/ String benchmarkName; - private /*final*/ long executedTimestamp; - - // TODO: add more run properties such as checksums of the executed code - - public Run(Map<Scenario, ScenarioResult> measurements, - String benchmarkName, Date executedTimestamp) { - if (benchmarkName == null || executedTimestamp == null) { - throw new NullPointerException(); - } - - this.measurements = new LinkedHashMap<Scenario, ScenarioResult>(measurements); - this.benchmarkName = benchmarkName; - this.executedTimestamp = executedTimestamp.getTime(); - } - - public Map<Scenario, ScenarioResult> getMeasurements() { - return measurements; - } - - public String getBenchmarkName() { - return benchmarkName; - } - - public Date getExecutedTimestamp() { - return new Date(executedTimestamp); - } - - @Override public boolean equals(Object o) { - if (o instanceof Run) { - Run that = (Run) o; - return measurements.equals(that.measurements) - && benchmarkName.equals(that.benchmarkName) - && executedTimestamp == that.executedTimestamp; - } - - return false; - } - - @Override public int hashCode() { - int result = measurements.hashCode(); - result = result * 37 + benchmarkName.hashCode(); - result = result * 37 + (int) ((executedTimestamp >> 32) ^ executedTimestamp); - return result; - } - - @Override public String toString() { - return measurements.toString(); - } - - @SuppressWarnings("unused") - private Run() {} // for GWT Serialization -} diff --git a/caliper/src/main/java/com/google/caliper/Runner.java b/caliper/src/main/java/com/google/caliper/Runner.java deleted file mode 100644 index 57715c9..0000000 --- a/caliper/src/main/java/com/google/caliper/Runner.java +++ /dev/null @@ -1,438 +0,0 @@ -/* - * Copyright (C) 2009 Google Inc. - * - * 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.google.caliper; - -import com.google.caliper.UserException.DisplayUsageException; -import com.google.caliper.UserException.ExceptionFromUserCodeException; -import com.google.caliper.util.InterleavedReader; -import com.google.common.base.Joiner; -import com.google.common.base.Splitter; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ObjectArrays; -import com.google.common.io.Closeables; -import com.google.gson.JsonObject; - -import java.io.BufferedReader; -import java.io.File; -import java.io.FileFilter; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.PrintStream; -import java.net.HttpURLConnection; -import java.net.InetSocketAddress; -import java.net.Proxy; -import java.net.URL; -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Map.Entry; -import java.util.TimeZone; -import java.util.regex.Pattern; - -/** - * Creates, executes and reports benchmark runs. - */ -public final class Runner { - - private static final FileFilter UPLOAD_FILE_FILTER = new FileFilter() { - @Override public boolean accept(File file) { - return file.getName().endsWith(".xml") || file.getName().endsWith(".json"); - } - }; - - private static final String FILE_NAME_DATE_FORMAT = "yyyy-MM-dd'T'HH-mm-ssZ"; - - private static final Splitter ARGUMENT_SPLITTER - = Splitter.on(Pattern.compile("\\s+")).omitEmptyStrings(); - - /** Command line arguments to the process */ - private Arguments arguments; - private ScenarioSelection scenarioSelection; - - private String createFileName(Result result) { - String timestamp = createTimestamp(); - return String.format("%s.%s.json", result.getRun().getBenchmarkName(), timestamp); - } - - private String createTimestamp() { - SimpleDateFormat dateFormat = new SimpleDateFormat(FILE_NAME_DATE_FORMAT, Locale.US); - dateFormat.setTimeZone(TimeZone.getTimeZone("UTC")); - dateFormat.setLenient(true); - return dateFormat.format(new Date()); - } - - public void run(String... args) { - this.arguments = Arguments.parse(args); - File resultsUploadFile = arguments.getUploadResultsFile(); - if (resultsUploadFile != null) { - uploadResultsFileOrDir(resultsUploadFile); - return; - } - this.scenarioSelection = new ScenarioSelection(arguments); - if (arguments.getDebug()) { - debug(); - return; - } - Result result = runOutOfProcess(); - new ConsoleReport(result.getRun(), arguments).displayResults(); - boolean saveResultsLocally = arguments.getSaveResultsFile() != null; - try { - postResults(result); - } catch (Exception e) { - System.out.println(); - System.out.println(e); - saveResultsLocally = true; - } - - if (saveResultsLocally) { - saveResults(result); - } - } - - void uploadResultsFileOrDir(File resultsFileOrDir) { - try { - if (resultsFileOrDir.isDirectory()) { - for (File resultsFile : resultsFileOrDir.listFiles(UPLOAD_FILE_FILTER)) { - uploadResults(resultsFile); - } - } else { - uploadResults(resultsFileOrDir); - } - } catch (Exception e) { - throw new RuntimeException("uploading XML file failed", e); - } - } - - private void uploadResults(File resultsUploadFile) throws IOException { - System.out.println(); - System.out.println("Uploading " + resultsUploadFile.getCanonicalPath()); - InputStream inputStream = new FileInputStream(resultsUploadFile); - try { - Result result = new ResultsReader().getResult(inputStream); - postResults(result); - } finally { - inputStream.close(); - } - } - - private void saveResults(Result result) { - File resultsFile = arguments.getSaveResultsFile(); - File destinationFile; - if (resultsFile == null) { - File dir = new File("./caliper-results"); - dir.mkdirs(); - destinationFile = new File(dir, createFileName(result)); - } else if (resultsFile.exists() && resultsFile.isDirectory()) { - destinationFile = new File(resultsFile, createFileName(result)); - } else { - // assume this is a file - File parent = resultsFile.getParentFile(); - if (parent != null) { - parent.mkdirs(); - } - destinationFile = resultsFile; - } - - PrintStream filePrintStream; - try { - filePrintStream = new PrintStream(new FileOutputStream(destinationFile)); - } catch (FileNotFoundException e) { - throw new RuntimeException("can't open " + destinationFile, e); - } - String resultJson = Json.getGsonInstance().toJson(result); - try { - System.out.println(); - System.out.println("Writing results to " + destinationFile.getCanonicalPath()); - filePrintStream.print(resultJson); - } catch (Exception e) { - System.out.println(e); - System.out.println("Failed to write results to file, writing to standard out instead:"); - System.out.println(resultJson); - System.out.flush(); - } finally { - filePrintStream.close(); - } - } - - private void postResults(Result result) { - CaliperRc caliperrc = CaliperRc.INSTANCE; - String postUrl = caliperrc.getPostUrl(); - String apiKey = caliperrc.getApiKey(); - if (postUrl == null || apiKey == null) { - // TODO: probably nicer to show a message if only one is null - return; - } - - try { - URL url = new URL(postUrl + apiKey + "/" + result.getRun().getBenchmarkName()); - HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection(getProxy()); - urlConnection.setDoOutput(true); - String resultJson = Json.getGsonInstance().toJson(result); - urlConnection.getOutputStream().write(resultJson.getBytes()); - if (urlConnection.getResponseCode() == 200) { - System.out.println(""); - System.out.println("View current and previous benchmark results online:"); - BufferedReader in = new BufferedReader( - new InputStreamReader(urlConnection.getInputStream())); - System.out.println(" " + in.readLine()); - in.close(); - return; - } - - System.out.println("Posting to " + postUrl + " failed: " - + urlConnection.getResponseMessage()); - BufferedReader reader = new BufferedReader( - new InputStreamReader(urlConnection.getInputStream())); - String line; - while ((line = reader.readLine()) != null) { - System.out.println(line); - } - reader.close(); - } catch (IOException e) { - throw new RuntimeException("Posting to " + postUrl + " failed.", e); - } - } - - private Proxy getProxy() { - String proxyAddress = CaliperRc.INSTANCE.getProxy(); - if (proxyAddress == null) { - return Proxy.NO_PROXY; - } - - String[] proxyHostAndPort = proxyAddress.trim().split(":"); - return new Proxy(Proxy.Type.HTTP, new InetSocketAddress( - proxyHostAndPort[0], Integer.parseInt(proxyHostAndPort[1]))); - } - - private ScenarioResult runScenario(Scenario scenario) { - MeasurementResult timeMeasurementResult = measure(scenario, MeasurementType.TIME); - MeasurementSet allocationMeasurements = null; - String allocationEventLog = null; - MeasurementSet memoryMeasurements = null; - String memoryEventLog = null; - if (arguments.getMeasureMemory()) { - MeasurementResult allocationsMeasurementResult = - measure(scenario, MeasurementType.INSTANCE); - allocationMeasurements = allocationsMeasurementResult.getMeasurements(); - allocationEventLog = allocationsMeasurementResult.getEventLog(); - MeasurementResult memoryMeasurementResult = - measure(scenario, MeasurementType.MEMORY); - memoryMeasurements = memoryMeasurementResult.getMeasurements(); - memoryEventLog = memoryMeasurementResult.getEventLog(); - } - - return new ScenarioResult(timeMeasurementResult.getMeasurements(), - timeMeasurementResult.getEventLog(), - allocationMeasurements, allocationEventLog, - memoryMeasurements, memoryEventLog); - } - - private static class MeasurementResult { - private final MeasurementSet measurements; - private final String eventLog; - - MeasurementResult(MeasurementSet measurements, String eventLog) { - this.measurements = measurements; - this.eventLog = eventLog; - } - - public MeasurementSet getMeasurements() { - return measurements; - } - - public String getEventLog() { - return eventLog; - } - } - - private MeasurementResult measure(Scenario scenario, MeasurementType type) { - Vm vm = new VmFactory().createVm(scenario); - // this must be done before starting the forked process on certain VMs - ProcessBuilder processBuilder = createCommand(scenario, vm, type) - .redirectErrorStream(true); - Process timeProcess; - try { - timeProcess = processBuilder.start(); - } catch (IOException e) { - throw new RuntimeException("failed to start subprocess", e); - } - - MeasurementSet measurementSet = null; - StringBuilder eventLog = new StringBuilder(); - InterleavedReader reader = null; - try { - reader = new InterleavedReader(arguments.getMarker(), - new InputStreamReader(timeProcess.getInputStream())); - Object o; - while ((o = reader.read()) != null) { - if (o instanceof String) { - eventLog.append(o); - } else if (measurementSet == null) { - JsonObject jsonObject = (JsonObject) o; - measurementSet = Json.measurementSetFromJson(jsonObject); - } else { - throw new RuntimeException("Unexpected value: " + o); - } - } - } catch (IOException e) { - throw new RuntimeException(e); - } finally { - Closeables.closeQuietly(reader); - timeProcess.destroy(); - } - - if (measurementSet == null) { - String message = "Failed to execute " + Joiner.on(" ").join(processBuilder.command()); - System.err.println(" " + message); - System.err.println(eventLog.toString()); - throw new ConfigurationException(message); - } - - return new MeasurementResult(measurementSet, eventLog.toString()); - } - - private ProcessBuilder createCommand(Scenario scenario, Vm vm, MeasurementType type) { - File workingDirectory = new File(System.getProperty("user.dir")); - - String classPath = System.getProperty("java.class.path"); - if (classPath == null || classPath.length() == 0) { - throw new IllegalStateException("java.class.path is undefined in " + System.getProperties()); - } - - ImmutableList.Builder<String> vmArgs = ImmutableList.builder(); - vmArgs.addAll(ARGUMENT_SPLITTER.split(scenario.getVariables().get(Scenario.VM_KEY))); - if (type == MeasurementType.INSTANCE || type == MeasurementType.MEMORY) { - String allocationJarFile = System.getenv("ALLOCATION_JAR"); - vmArgs.add("-javaagent:" + allocationJarFile); - } - vmArgs.addAll(vm.getVmSpecificOptions(type, arguments)); - - Map<String, String> vmParameters = scenario.getVariables( - scenarioSelection.getVmParameterNames()); - for (String vmParameter : vmParameters.values()) { - vmArgs.addAll(ARGUMENT_SPLITTER.split(vmParameter)); - } - - ImmutableList.Builder<String> caliperArgs = ImmutableList.builder(); - caliperArgs.add("--warmupMillis").add(Long.toString(arguments.getWarmupMillis())); - caliperArgs.add("--runMillis").add(Long.toString(arguments.getRunMillis())); - caliperArgs.add("--measurementType").add(type.toString()); - caliperArgs.add("--marker").add(arguments.getMarker()); - - Map<String,String> userParameters = scenario.getVariables( - scenarioSelection.getUserParameterNames()); - for (Entry<String, String> entry : userParameters.entrySet()) { - caliperArgs.add("-D" + entry.getKey() + "=" + entry.getValue()); - } - caliperArgs.add(arguments.getSuiteClassName()); - - return vm.newProcessBuilder(workingDirectory, classPath, - vmArgs.build(), InProcessRunner.class.getName(), caliperArgs.build()); - } - - private void debug() { - try { - int debugReps = arguments.getDebugReps(); - InProcessRunner runner = new InProcessRunner(); - DebugMeasurer measurer = new DebugMeasurer(debugReps); - for (Scenario scenario : scenarioSelection.select()) { - System.out.println("running " + debugReps + " debug reps of " + scenario); - runner.run(scenarioSelection, scenario, measurer); - } - } catch (Exception e) { - throw new ExceptionFromUserCodeException(e); - } - } - - private Result runOutOfProcess() { - Date executedDate = new Date(); - ImmutableMap.Builder<Scenario, ScenarioResult> resultsBuilder = ImmutableMap.builder(); - - try { - List<Scenario> scenarios = scenarioSelection.select(); - - int i = 0; - for (Scenario scenario : scenarios) { - beforeMeasurement(i++, scenarios.size(), scenario); - ScenarioResult scenarioResult = runScenario(scenario); - afterMeasurement(arguments.getMeasureMemory(), scenarioResult); - resultsBuilder.put(scenario, scenarioResult); - } - System.out.println(); - - Environment environment = new EnvironmentGetter().getEnvironmentSnapshot(); - return new Result( - new Run(resultsBuilder.build(), arguments.getSuiteClassName(), executedDate), - environment); - } catch (Exception e) { - throw new ExceptionFromUserCodeException(e); - } - } - - private void beforeMeasurement(int index, int total, Scenario scenario) { - double percentDone = (double) index / total; - System.out.printf("%2.0f%% %s", percentDone * 100, scenario); - } - - private void afterMeasurement(boolean memoryMeasured, ScenarioResult scenarioResult) { - String memoryMeasurements = ""; - if (memoryMeasured) { - MeasurementSet instanceMeasurementSet = - scenarioResult.getMeasurementSet(MeasurementType.INSTANCE); - String instanceUnit = - ConsoleReport.UNIT_ORDERING.min(instanceMeasurementSet.getUnitNames().entrySet()).getKey(); - MeasurementSet memoryMeasurementSet = scenarioResult.getMeasurementSet(MeasurementType.MEMORY); - String memoryUnit = - ConsoleReport.UNIT_ORDERING.min(memoryMeasurementSet.getUnitNames().entrySet()).getKey(); - memoryMeasurements = String.format(", allocated %s%s for a total of %s%s", - Math.round(instanceMeasurementSet.medianUnits()), instanceUnit, - Math.round(memoryMeasurementSet.medianUnits()), memoryUnit); - } - - MeasurementSet timeMeasurementSet = scenarioResult.getMeasurementSet(MeasurementType.TIME); - String unit = - ConsoleReport.UNIT_ORDERING.min(timeMeasurementSet.getUnitNames().entrySet()).getKey(); - System.out.printf(" %.2f %s; \u03C3=%.2f %s @ %d trials%s%n", timeMeasurementSet.medianUnits(), - unit, timeMeasurementSet.standardDeviationUnits(), unit, - timeMeasurementSet.getMeasurements().size(), memoryMeasurements); - } - - public static void main(String[] args) { - try { - new Runner().run(args); - System.exit(0); // user code may have leave non-daemon threads behind! - } catch (DisplayUsageException e) { - e.display(); - System.exit(0); - } catch (UserException e) { - e.display(); - System.exit(1); - } - } - - @SuppressWarnings("unchecked") // temporary fakery - public static void main(Class<? extends Benchmark> suite, String[] args) { - main(ObjectArrays.concat(args, suite.getName())); - } -} diff --git a/caliper/src/main/java/com/google/caliper/Scenario.java b/caliper/src/main/java/com/google/caliper/Scenario.java deleted file mode 100644 index bc026d8..0000000 --- a/caliper/src/main/java/com/google/caliper/Scenario.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright (C) 2009 Google Inc. - * - * 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.google.caliper; - -import com.google.common.annotations.GwtCompatible; - -import java.io.Serializable; -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.Set; - -/** - * A configured benchmark. - * - * WARNING: a JSON representation of this class is stored on the app engine server. If any changes - * are made to this class, a deserialization adapter must be written for this class to ensure - * backwards compatibility. - * - * <p>Gwt-safe. - */ -@SuppressWarnings("serial") -@GwtCompatible -public final class Scenario - implements Serializable /* for GWT */ { - - static final String VM_KEY = "vm"; - static final String TRIAL_KEY = "trial"; - - private /*final*/ Map<String, String> variables; - - public Scenario(Map<String, String> variables) { - this.variables = new LinkedHashMap<String, String>(variables); - } - - public Map<String, String> getVariables() { - return variables; - } - - /** - * Returns the named set of variables. - */ - public Map<String, String> getVariables(Set<String> names) { - Map<String, String> result = new LinkedHashMap<String, String>(variables); - result.keySet().retainAll(names); - if (!result.keySet().equals(names)) { - throw new IllegalArgumentException("Not all of " + names + " are in " + result.keySet()); - } - return result; - } - - @Override public boolean equals(Object o) { - return o instanceof Scenario - && ((Scenario) o).getVariables().equals(variables); - } - - @Override public int hashCode() { - return variables.hashCode(); - } - - @Override public String toString() { - return "Scenario" + variables; - } - - @SuppressWarnings("unused") - private Scenario() {} // for GWT -} diff --git a/caliper/src/main/java/com/google/caliper/ScenarioResult.java b/caliper/src/main/java/com/google/caliper/ScenarioResult.java deleted file mode 100644 index 1fa687d..0000000 --- a/caliper/src/main/java/com/google/caliper/ScenarioResult.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright (C) 2010 Google Inc. - * - * 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.google.caliper; - -import com.google.common.annotations.GwtCompatible; - -import java.io.Serializable; -import java.util.HashMap; -import java.util.Map; - -/** - * Holds the results for a particular scenario, including timing measurements, memory use - * measurements, and event logs for both, recording significant events during measurement. - * - * WARNING: a JSON representation of this class is stored on the app engine server. If any changes - * are made to this class, a deserialization adapter must be written for this class to ensure - * backwards compatibility. - * - * <p>Gwt-safe. - */ -@SuppressWarnings({"serial", "FieldMayBeFinal"}) -@GwtCompatible -public final class ScenarioResult - implements Serializable /* for GWT Serialization */ { - - // want these to be EnumMaps, but that upsets GWT - private /*final*/ Map<String, MeasurementSet> measurementSetMap - = new HashMap<String, MeasurementSet>(); - private /*final*/ Map<String, String> eventLogMap - = new HashMap<String, String>(); - - public ScenarioResult(MeasurementSet timeMeasurementSet, - String timeEventLog, MeasurementSet instanceMeasurementSet, - String instanceEventLog, MeasurementSet memoryMeasurementSet, - String memoryEventLog) { - if (timeMeasurementSet != null) { - measurementSetMap.put(MeasurementType.TIME.toString(), timeMeasurementSet); - eventLogMap.put(MeasurementType.TIME.toString(), timeEventLog); - } - if (instanceMeasurementSet != null) { - measurementSetMap.put(MeasurementType.INSTANCE.toString(), instanceMeasurementSet); - eventLogMap.put(MeasurementType.INSTANCE.toString(), instanceEventLog); - } - if (memoryMeasurementSet != null) { - measurementSetMap.put(MeasurementType.MEMORY.toString(), memoryMeasurementSet); - eventLogMap.put(MeasurementType.MEMORY.toString(), memoryEventLog); - } - } - - public MeasurementSet getMeasurementSet(MeasurementType type) { - return measurementSetMap.get(type.toString()); - } - - public String getEventLog(MeasurementType type) { - return eventLogMap.get(type.toString()); - } - - @Override public boolean equals(Object o) { - return o instanceof ScenarioResult - && ((ScenarioResult) o).measurementSetMap.equals(measurementSetMap) - && ((ScenarioResult) o).eventLogMap.equals(eventLogMap); - } - - @Override public int hashCode() { - return measurementSetMap.hashCode() * 37 + eventLogMap.hashCode(); - } - - @Override public String toString() { - return "measurementSetMap: " + measurementSetMap + ", eventLogMap: " + eventLogMap; - } - - @SuppressWarnings("unused") - private ScenarioResult() {} // for GWT Serialization -} diff --git a/caliper/src/main/java/com/google/caliper/ScenarioSelection.java b/caliper/src/main/java/com/google/caliper/ScenarioSelection.java deleted file mode 100644 index fcfead0..0000000 --- a/caliper/src/main/java/com/google/caliper/ScenarioSelection.java +++ /dev/null @@ -1,253 +0,0 @@ -/* - * Copyright (C) 2010 Google Inc. - * - * 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.google.caliper; - -import com.google.caliper.UserException.AbstractBenchmarkException; -import com.google.caliper.UserException.DoesntImplementBenchmarkException; -import com.google.caliper.UserException.ExceptionFromUserCodeException; -import com.google.caliper.UserException.NoParameterlessConstructorException; -import com.google.caliper.UserException.NoSuchClassException; -import com.google.common.collect.LinkedHashMultimap; -import com.google.common.collect.Multimap; - -import java.lang.reflect.Constructor; -import java.lang.reflect.InvocationTargetException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; - -/** - * Figures out which scenarios to benchmark given a benchmark suite, set of user - * parameters, and set of user VMs. - */ -public final class ScenarioSelection { - - private final Set<String> userVms; - private final Multimap<String, String> vmParameters; - private final String suiteClassName; - - /** - * The user parameters specified on the command line. This may be a subset of - * the effective user parameters because parameters not specified here may get - * default values from the benchmark class. - */ - private final Multimap<String, String> userParameterArguments; - - /** - * The actual user parameters we'll use to run in the benchmark. This contains - * the userParameterArguments plus the default user parameters. - */ - private Multimap<String, String> userParameters; - - private final int trials; - private Benchmark suite; - - - public ScenarioSelection(Arguments arguments) { - this(arguments.getUserVms(), arguments.getVmParameters(), arguments.getSuiteClassName(), - arguments.getUserParameters(), arguments.getTrials()); - } - - public ScenarioSelection(Set<String> userVms, Multimap<String, String> vmParameters, - String suiteClassName, Multimap<String, String> userParameterArguments, int trials) { - this.userVms = userVms; - this.vmParameters = vmParameters; - this.suiteClassName = suiteClassName; - this.userParameterArguments = userParameterArguments; - this.trials = trials; - } - - /** - * Returns the selected scenarios for this benchmark. - */ - public List<Scenario> select() { - prepareSuite(); - userParameters = computeUserParameters(); - return createScenarios(); - } - - /** - * Returns a normalized version of {@code scenario}, with information from {@code suite} - * assisting in correcting problems. - */ - public Scenario normalizeScenario(Scenario scenario) { - // This only applies to SimpleBenchmarks since they accept the special "benchmark" - // parameter. This is a special case because SimpleBenchmark is the most commonly - // used benchmark class. Have to do this horrible stuff since Benchmark API - // doesn't provide scenario-normalization (and it shouldn't), which SimpleBenchmark - // requires. - if (suite instanceof SimpleBenchmark) { - return ((SimpleBenchmark) suite).normalizeScenario(scenario); - } - - return scenario; - } - - public Set<String> getUserParameterNames() { - if (userParameters == null) { - throw new IllegalStateException(); - } - return userParameters.keySet(); - } - - public Set<String> getVmParameterNames() { - return vmParameters.keySet(); - } - - public ConfiguredBenchmark createBenchmark(Scenario scenario) { - return suite.createBenchmark(scenario.getVariables(getUserParameterNames())); - } - - private void prepareSuite() { - Class<?> benchmarkClass; - try { - benchmarkClass = getClassByName(suiteClassName); - } catch (ExceptionInInitializerError e) { - throw new ExceptionFromUserCodeException(e.getCause()); - } catch (ClassNotFoundException ignored) { - throw new NoSuchClassException(suiteClassName); - } - - Object s; - try { - Constructor<?> constructor = benchmarkClass.getDeclaredConstructor(); - constructor.setAccessible(true); - s = constructor.newInstance(); - } catch (InstantiationException ignore) { - throw new AbstractBenchmarkException(benchmarkClass); - } catch (NoSuchMethodException ignore) { - throw new NoParameterlessConstructorException(benchmarkClass); - } catch (IllegalAccessException impossible) { - throw new AssertionError(impossible); // shouldn't happen since we setAccessible(true) - } catch (InvocationTargetException e) { - throw new ExceptionFromUserCodeException(e.getCause()); - } - - if (s instanceof Benchmark) { - this.suite = (Benchmark) s; - } else { - throw new DoesntImplementBenchmarkException(benchmarkClass); - } - } - - private static Class<?> getClassByName(String className) throws ClassNotFoundException { - try { - return Class.forName(className); - } catch (ClassNotFoundException ignored) { - // try replacing the last dot with a $, in case that helps - // example: tutorial.Tutorial.Benchmark1 becomes tutorial.Tutorial$Benchmark1 - // amusingly, the $ character means three different things in this one line alone - String newName = className.replaceFirst("\\.([^.]+)$", "\\$$1"); - return Class.forName(newName); - } - } - - private Multimap<String, String> computeUserParameters() { - Multimap<String, String> result = LinkedHashMultimap.create(); - for (String key : suite.parameterNames()) { - // first check if the user has specified values - Collection<String> userValues = userParameterArguments.get(key); - if (!userValues.isEmpty()) { - result.putAll(key, userValues); - // TODO: type convert 'em to validate? - - } else { // otherwise use the default values from the suite - Set<String> values = suite.parameterValues(key); - if (values.isEmpty()) { - throw new ConfigurationException(key + " has no values. " - + "Did you forget a -D" + key + "=<value> command line argument?"); - } - result.putAll(key, values); - } - } - return result; - } - - /** - * Returns a complete set of scenarios with every combination of variables. - */ - private List<Scenario> createScenarios() { - List<ScenarioBuilder> builders = new ArrayList<ScenarioBuilder>(); - builders.add(new ScenarioBuilder()); - - Map<String, Collection<String>> variables = new LinkedHashMap<String, Collection<String>>(); - variables.put(Scenario.VM_KEY, userVms.isEmpty() ? VmFactory.defaultVms() : userVms); - variables.put(Scenario.TRIAL_KEY, newListOfSize(trials)); - variables.putAll(userParameters.asMap()); - variables.putAll(vmParameters.asMap()); - - for (Entry<String, Collection<String>> entry : variables.entrySet()) { - Iterator<String> values = entry.getValue().iterator(); - if (!values.hasNext()) { - throw new ConfigurationException("Not enough values for " + entry); - } - - String firstValue = values.next(); - for (ScenarioBuilder builder : builders) { - builder.variables.put(entry.getKey(), firstValue); - } - - // multiply the size of the specs by the number of alternate values - int size = builders.size(); - while (values.hasNext()) { - String alternate = values.next(); - for (int s = 0; s < size; s++) { - ScenarioBuilder copy = builders.get(s).copy(); - copy.variables.put(entry.getKey(), alternate); - builders.add(copy); - } - } - } - - List<Scenario> result = new ArrayList<Scenario>(); - for (ScenarioBuilder builder : builders) { - result.add(normalizeScenario(builder.build())); - } - - return result; - } - - /** - * Returns a list containing {@code count} distinct elements. - */ - private Collection<String> newListOfSize(int count) { - List<String> result = new ArrayList<String>(); - for (int i = 0; i < count; i++) { - result.add(Integer.toString(i)); - } - return result; - } - - private static class ScenarioBuilder { - final Map<String, String> variables = new LinkedHashMap<String, String>(); - - ScenarioBuilder copy() { - ScenarioBuilder result = new ScenarioBuilder(); - result.variables.putAll(variables); - return result; - } - - public Scenario build() { - return new Scenario(variables); - } - } -} diff --git a/caliper/src/main/java/com/google/caliper/SimpleBenchmark.java b/caliper/src/main/java/com/google/caliper/SimpleBenchmark.java deleted file mode 100644 index 01fc3a8..0000000 --- a/caliper/src/main/java/com/google/caliper/SimpleBenchmark.java +++ /dev/null @@ -1,227 +0,0 @@ -/* - * Copyright (C) 2009 Google Inc. - * - * 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.google.caliper; - -import com.google.caliper.UserException.ExceptionFromUserCodeException; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableSet; - -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; -import java.util.Arrays; -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.Set; - -/** - * A convenience class for implementing benchmarks in plain code. - * Implementing classes must have a no-arguments constructor. - * - * <h3>Benchmarks</h3> - * The benchmarks of a suite are defined by . They may be - * static. They are not permitted to take parameters . . .. - * - * <h3>Parameters</h3> - * See the {@link Param} documentation to learn about parameters. - */ -public abstract class SimpleBenchmark - implements Benchmark { - private static final Class<?>[] ARGUMENT_TYPES = { int.class }; - - private final Map<String, Parameter<?>> parameters; - private final Map<String, Method> methods; - - protected SimpleBenchmark() { - parameters = Parameter.forClass(getClass()); - methods = createTimedMethods(); - - if (methods.isEmpty()) { - throw new ConfigurationException( - "No benchmarks defined in " + getClass().getName()); - } - } - - protected void setUp() throws Exception {} - - protected void tearDown() throws Exception {} - - @Override public Set<String> parameterNames() { - return ImmutableSet.<String>builder() - .add("benchmark") - .addAll(parameters.keySet()) - .build(); - } - - @Override public Set<String> parameterValues(String parameterName) { - if ("benchmark".equals(parameterName)) { - return methods.keySet(); - } - - Parameter<?> parameter = parameters.get(parameterName); - if (parameter == null) { - throw new IllegalArgumentException(); - } - try { - Iterable<?> values = parameter.values(); - - ImmutableSet.Builder<String> result = ImmutableSet.builder(); - for (Object value : values) { - result.add(String.valueOf(value)); - } - return result.build(); - } catch (Exception e) { - throw new ExceptionFromUserCodeException(e); - } - } - - @Override public ConfiguredBenchmark createBenchmark(Map<String, String> parameterValues) { - if (!parameterNames().equals(parameterValues.keySet())) { - throw new IllegalArgumentException("Invalid parameters specified. Expected " - + parameterNames() + " but was " + parameterValues.keySet()); - } - - String methodName = parameterValues.get("benchmark"); - final Method method = methods.get(methodName); - if (method == null) { - throw new IllegalArgumentException("Invalid parameters specified. \"time" + methodName + "\" " - + "is not a method of this benchmark."); - } - - try { - @SuppressWarnings({"ClassNewInstance"}) // can throw any Exception, so we catch all Exceptions - final SimpleBenchmark copyOfSelf = getClass().newInstance(); - - for (Map.Entry<String, String> entry : parameterValues.entrySet()) { - String parameterName = entry.getKey(); - if ("benchmark".equals(parameterName)) { - continue; - } - - Parameter<?> parameter = parameters.get(parameterName); - Object value = TypeConverter.fromString(entry.getValue(), parameter.getType()); - parameter.set(copyOfSelf, value); - } - copyOfSelf.setUp(); - - return new ConfiguredBenchmark(copyOfSelf) { - @Override public Object run(int reps) throws Exception { - try { - return method.invoke(copyOfSelf, reps); - } catch (InvocationTargetException e) { - Throwable cause = e.getCause(); - if (cause instanceof Exception) { - throw (Exception) cause; - } else if (cause instanceof Error) { - throw (Error) cause; - } else { - throw e; - } - } - } - - @Override public void close() throws Exception { - copyOfSelf.tearDown(); - } - }; - } catch (Exception e) { - throw new ExceptionFromUserCodeException(e); - } - } - - public Scenario normalizeScenario(Scenario scenario) { - Map<String, String> variables = - new LinkedHashMap<String, String>(scenario.getVariables()); - // Make sure the scenario contains method names without the prefixed "time". If - // it has "time" prefixed, then remove it. Also check whether the user has - // accidentally put a lower cased letter first, and fix it if necessary. - String benchmark = variables.get("benchmark"); - Map<String, Method> timedMethods = createTimedMethods(); - if (timedMethods.get(benchmark) == null) { - // try to upper case first character - char[] benchmarkChars = benchmark.toCharArray(); - benchmarkChars[0] = Character.toUpperCase(benchmarkChars[0]); - String upperCasedBenchmark = String.valueOf(benchmarkChars); - if (timedMethods.get(upperCasedBenchmark) != null) { - variables.put("benchmark", upperCasedBenchmark); - } else if (benchmark.startsWith("time")) { - variables.put("benchmark", benchmark.substring(4)); - } - } - return new Scenario(variables); - } - - /** - * Returns a spec for each benchmark defined in the specified class. The - * returned specs have no parameter values; those must be added separately. - */ - private Map<String, Method> createTimedMethods() { - ImmutableMap.Builder<String, Method> result = ImmutableMap.builder(); - for (Method method : getClass().getDeclaredMethods()) { - int modifiers = method.getModifiers(); - if (!method.getName().startsWith("time")) { - continue; - } - - if (!Modifier.isPublic(modifiers) - || Modifier.isStatic(modifiers) - || Modifier.isAbstract(modifiers) - || !Arrays.equals(method.getParameterTypes(), ARGUMENT_TYPES)) { - throw new ConfigurationException("Timed methods must be public, " - + "non-static, non-abstract and take a single int parameter. " - + "But " + method + " violates these requirements."); - } - - result.put(method.getName().substring(4), method); - } - - return result.build(); - } - - @Override public Map<String, Integer> getTimeUnitNames() { - return ImmutableMap.of("ns", 1, - "us", 1000, - "ms", 1000000, - "s", 1000000000); - } - - @Override public double nanosToUnits(double nanos) { - return nanos; - } - - @Override public Map<String, Integer> getInstanceUnitNames() { - return ImmutableMap.of(" instances", 1, - "K instances", 1000, - "M instances", 1000000, - "B instances", 1000000000); - } - - @Override public double instancesToUnits(long instances) { - return instances; - } - - @Override public Map<String, Integer> getMemoryUnitNames() { - return ImmutableMap.of("B", 1, - "KiB", 1024, - "MiB", 1048576, - "GiB", 1073741824); - } - - @Override public double bytesToUnits(long bytes) { - return bytes; - } -} diff --git a/caliper/src/main/java/com/google/caliper/StandardVm.java b/caliper/src/main/java/com/google/caliper/StandardVm.java deleted file mode 100644 index 3366eb1..0000000 --- a/caliper/src/main/java/com/google/caliper/StandardVm.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (C) 2010 Google Inc. - * - * 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.google.caliper; - -import com.google.common.collect.ImmutableList; -import com.google.common.collect.Lists; - -import java.util.ArrayList; -import java.util.List; - -final class StandardVm extends Vm { - - @Override public List<String> getVmSpecificOptions(MeasurementType type, Arguments arguments) { - if (!arguments.getCaptureVmLog()) { - return ImmutableList.of(); - } - - List<String> result = new ArrayList<String>(); - result.add("-verbose:gc"); - result.add("-Xbatch"); - result.add("-XX:+UseSerialGC"); - if (type == MeasurementType.TIME) { - return Lists.newArrayList("-XX:+PrintCompilation"); - } - - return result; - } - - public static String defaultVmName() { - return "java"; - } -} diff --git a/caliper/src/main/java/com/google/caliper/TimeMeasurer.java b/caliper/src/main/java/com/google/caliper/TimeMeasurer.java deleted file mode 100644 index 2925d2d..0000000 --- a/caliper/src/main/java/com/google/caliper/TimeMeasurer.java +++ /dev/null @@ -1,180 +0,0 @@ -/** - * Copyright (C) 2009 Google Inc. - * - * 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.google.caliper; - -import static com.google.common.base.Preconditions.checkArgument; - -import com.google.caliper.UserException.DoesNotScaleLinearlyException; -import com.google.caliper.UserException.RuntimeOutOfRangeException; -import com.google.common.base.Supplier; - -/** - * Measure's the benchmark's per-trial execution time. - */ -class TimeMeasurer extends Measurer { - - private final long warmupNanos; - private final long runNanos; - - /** - * If the standard deviation of our measurements is within this tolerance, we - * won't bother to perform additional measurements. - */ - private static final double SHORT_CIRCUIT_TOLERANCE = 0.01; - - private static final int MAX_TRIALS = 10; - - TimeMeasurer(long warmupMillis, long runMillis) { - checkArgument(warmupMillis > 50); - checkArgument(runMillis > 50); - - this.warmupNanos = warmupMillis * 1000000; - this.runNanos = runMillis * 1000000; - } - - private double warmUp(Supplier<ConfiguredBenchmark> testSupplier) throws Exception { - long elapsedNanos = 0; - long netReps = 0; - int reps = 1; - boolean definitelyScalesLinearly = false; - - /* - * Run progressively more reps at a time until we cross our warmup - * threshold. This way any just-in-time compiler will be comfortable running - * multiple iterations of our measurement method. - */ - log("[starting warmup]"); - while (elapsedNanos < warmupNanos) { - long nanos = measureReps(testSupplier.get(), reps); - elapsedNanos += nanos; - - netReps += reps; - reps *= 2; - - // if reps overflowed, that's suspicious! Check that it time scales with reps - if (reps <= 0) { - if (!definitelyScalesLinearly) { - checkScalesLinearly(testSupplier); - definitelyScalesLinearly = true; - } - reps = Integer.MAX_VALUE; - } - } - log("[ending warmup]"); - - double nanosPerExecution = (double) elapsedNanos / netReps; - double lowerBound = 0.1; - double upperBound = 10000000000.0; - if (!(lowerBound <= nanosPerExecution && nanosPerExecution <= upperBound)) { - throw new RuntimeOutOfRangeException(nanosPerExecution, lowerBound, upperBound); - } - - return nanosPerExecution; - } - - /** - * Doing half as much work shouldn't take much more than half as much time. If - * it does we have a broken benchmark! - */ - private void checkScalesLinearly(Supplier<ConfiguredBenchmark> testSupplier) throws Exception { - double half = measureReps(testSupplier.get(), Integer.MAX_VALUE / 2); - double one = measureReps(testSupplier.get(), Integer.MAX_VALUE); - if (half / one > 0.75) { - throw new DoesNotScaleLinearlyException(); - } - } - - /** - * Measure the nanos per rep for the given test. This code uses an interesting - * strategy to measure the runtime to minimize execution time when execution - * time is consistent. - * <ol> - * <li>1.0x {@code runMillis} trial is run. - * <li>0.5x {@code runMillis} trial is run. - * <li>1.5x {@code runMillis} trial is run. - * <li>At this point, the standard deviation of these trials is computed. If - * it is within the threshold, the result is returned. - * <li>Otherwise trials continue to be executed until either the threshold - * is satisfied or the maximum number of runs have been executed. - * </ol> - * - * @param testSupplier provides instances of the code under test. A new test - * is created for each iteration because some benchmarks' performance - * depends on which memory was allocated. See SetContainsBenchmark for an - * example. - */ - @Override public MeasurementSet run(Supplier<ConfiguredBenchmark> testSupplier) - throws Exception { - double estimatedNanosPerRep = warmUp(testSupplier); - - log("[measuring nanos per rep with scale 1.00]"); - Measurement measurement100 = measure(testSupplier, 1.00, estimatedNanosPerRep); - log("[measuring nanos per rep with scale 0.50]"); - Measurement measurement050 = measure(testSupplier, 0.50, measurement100.getRaw()); - log("[measuring nanos per rep with scale 1.50]"); - Measurement measurement150 = measure(testSupplier, 1.50, measurement100.getRaw()); - MeasurementSet measurementSet = - new MeasurementSet(measurement100, measurement050, measurement150); - - for (int i = 3; i < MAX_TRIALS; i++) { - double threshold = SHORT_CIRCUIT_TOLERANCE * measurementSet.meanRaw(); - if (measurementSet.standardDeviationRaw() < threshold) { - return measurementSet; - } - - log("[performing additional measurement with scale 1.00]"); - Measurement measurement = measure(testSupplier, 1.00, measurement100.getRaw()); - measurementSet = measurementSet.plusMeasurement(measurement); - } - - return measurementSet; - } - - /** - * Runs the test method for approximately {@code runNanos * durationScale} - * nanos and returns a Measurement of the nanos per rep and units per rep. - */ - private Measurement measure(Supplier<ConfiguredBenchmark> testSupplier, - double durationScale, double estimatedNanosPerRep) throws Exception { - int reps = (int) (durationScale * runNanos / estimatedNanosPerRep); - if (reps == 0) { - reps = 1; - } - - log("[running trial with " + reps + " reps]"); - ConfiguredBenchmark benchmark = testSupplier.get(); - long elapsedTime = measureReps(benchmark, reps); - double nanosPerRep = elapsedTime / (double) reps; - log(String.format("[took %.2f nanoseconds per rep]", nanosPerRep)); - return new Measurement(benchmark.timeUnitNames(), nanosPerRep, - benchmark.nanosToUnits(nanosPerRep)); - } - - /** - * Returns the total nanos to run {@code reps}. - */ - private long measureReps(ConfiguredBenchmark benchmark, int reps) throws Exception { - prepareForTest(); - log(LogConstants.MEASURED_SECTION_STARTING); - long startNanos = System.nanoTime(); - benchmark.run(reps); - long endNanos = System.nanoTime(); - log(LogConstants.MEASURED_SECTION_DONE); - benchmark.close(); - return endNanos - startNanos; - } -} diff --git a/caliper/src/main/java/com/google/caliper/TypeConverter.java b/caliper/src/main/java/com/google/caliper/TypeConverter.java deleted file mode 100644 index c3268e0..0000000 --- a/caliper/src/main/java/com/google/caliper/TypeConverter.java +++ /dev/null @@ -1,65 +0,0 @@ -/** - * Copyright (C) 2009 Google Inc. - * - * 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.google.caliper; - -import com.google.common.collect.ImmutableMap; - -import java.lang.reflect.Method; -import java.lang.reflect.Type; -import java.util.Map; - -/** - * Convert objects to and from Strings. - */ -final class TypeConverter { - private TypeConverter() {} - - public static Object fromString(String value, Type type) { - if (type == String.class) { - return value; - } - - Class<?> c = wrap((Class<?>) type); - try { - Method m = c.getMethod("valueOf", String.class); - m.setAccessible(true); // to permit inner enums, etc. - return m.invoke(null, value); - } catch (Exception e) { - throw new UnsupportedOperationException( - "Cannot convert " + value + " of type " + type, e); - } - } - - // safe because both Long.class and long.class are of type Class<Long> - @SuppressWarnings("unchecked") - private static <T> Class<T> wrap(Class<T> c) { - return c.isPrimitive() ? (Class<T>) PRIMITIVES_TO_WRAPPERS.get(c) : c; - } - - private static final Map<Class<?>, Class<?>> PRIMITIVES_TO_WRAPPERS - = new ImmutableMap.Builder<Class<?>, Class<?>>() - .put(boolean.class, Boolean.class) - .put(byte.class, Byte.class) - .put(char.class, Character.class) - .put(double.class, Double.class) - .put(float.class, Float.class) - .put(int.class, Integer.class) - .put(long.class, Long.class) - .put(short.class, Short.class) - .put(void.class, Void.class) - .build(); -} diff --git a/caliper/src/main/java/com/google/caliper/UserException.java b/caliper/src/main/java/com/google/caliper/UserException.java deleted file mode 100644 index a4535a7..0000000 --- a/caliper/src/main/java/com/google/caliper/UserException.java +++ /dev/null @@ -1,196 +0,0 @@ -/* - * Copyright (C) 2009 Google Inc. - * - * 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.google.caliper; - -import java.util.Arrays; -import java.util.Set; - -/** - * Signifies a problem that should be explained in user-friendly terms on the command line, without - * a confusing stack trace, and optionally followed by a usage summary. - */ -@SuppressWarnings("serial") // never going to serialize these... right? -public abstract class UserException extends RuntimeException { - protected UserException(String error) { - super(error); - } - - public abstract void display(); - - // - - - - - - public abstract static class ErrorInUsageException extends UserException { - protected ErrorInUsageException(String error) { - super(error); - } - - @Override public void display() { - String message = getMessage(); - if (message != null) { - System.err.println("Error: " + message); - } - Arguments.printUsage(); - } - } - - public abstract static class ErrorInUserCodeException extends UserException { - private final String remedy; - - protected ErrorInUserCodeException(String error, String remedy) { - super(error); - this.remedy = remedy; - } - - @Override public void display() { - System.err.println("Error: " + getMessage()); - System.err.println("Typical Remedy: " + remedy); - } - } - - // - - - - - - // Not technically an error, but works nicely this way anyway - public static class DisplayUsageException extends ErrorInUsageException { - public DisplayUsageException() { - super(null); - } - } - - public static class IncompatibleArgumentsException extends ErrorInUsageException { - public IncompatibleArgumentsException(String arg) { - super("Some arguments passed in are incompatible with: " + arg); - } - } - - public static class UnrecognizedOptionException extends ErrorInUsageException { - public UnrecognizedOptionException(String arg) { - super("Argument not recognized: " + arg); - } - } - - public static class NoBenchmarkClassException extends ErrorInUsageException { - public NoBenchmarkClassException() { - super("No benchmark class specified."); - } - } - - public static class MultipleBenchmarkClassesException extends ErrorInUsageException { - public MultipleBenchmarkClassesException(String a, String b) { - super("Multiple benchmark classes specified: " + Arrays.asList(a, b)); - } - } - - public static class MalformedParameterException extends ErrorInUsageException { - public MalformedParameterException(String arg) { - super("Malformed parameter: " + arg); - } - } - - public static class DuplicateParameterException extends ErrorInUsageException { - public DuplicateParameterException(String arg) { - super("Duplicate parameter: " + arg); - } - public DuplicateParameterException(Set<String> arg) { - super("Duplicate parameters: " + arg); - } - } - - public static class InvalidParameterValueException extends ErrorInUsageException { - public InvalidParameterValueException(String arg, String value) { - super("Invalid value \"" + value + "\" for parameter: " + arg); - } - } - - public static class InvalidTrialsException extends ErrorInUsageException { - public InvalidTrialsException(String arg) { - super("Invalid trials: " + arg); - } - } - - public static class CantCustomizeInProcessVmException extends ErrorInUsageException { - public CantCustomizeInProcessVmException() { - super("Can't customize VM when running in process."); - } - } - - public static class NoSuchClassException extends ErrorInUsageException { - public NoSuchClassException(String name) { - super("No class named [" + name + "] was found (check CLASSPATH)."); - } - } - - public static class RuntimeOutOfRangeException extends ErrorInUsageException { - public RuntimeOutOfRangeException( - double nanosPerExecution, double lowerBound, double upperBound) { - super("Runtime " + nanosPerExecution + "ns/rep out of range " - + lowerBound + "-" + upperBound); - } - } - - public static class DoesNotScaleLinearlyException extends ErrorInUsageException { - public DoesNotScaleLinearlyException() { - super("Doing 2x as much work didn't take 2x as much time! " - + "Is the JIT optimizing away the body of your benchmark?"); - } - } - - public static class NonConstantMemoryUsage extends ErrorInUsageException { - public NonConstantMemoryUsage() { - super("Not all reps of the inner loop allocate the same number of times! " - + "The reps loop should use a constant number of allocations. " - + "Are you using the value of reps inside the loop?"); - } - } - - public static class AbstractBenchmarkException extends ErrorInUserCodeException { - public AbstractBenchmarkException(Class<?> specifiedClass) { - super("Class [" + specifiedClass.getName() + "] is abstract.", "Specify a concrete class."); - } - } - - public static class NoParameterlessConstructorException extends ErrorInUserCodeException { - public NoParameterlessConstructorException(Class<?> specifiedClass) { - super("Class [" + specifiedClass.getName() + "] has no parameterless constructor.", - "Remove all constructors or add a parameterless constructor."); - } - } - - public static class DoesntImplementBenchmarkException extends ErrorInUserCodeException { - public DoesntImplementBenchmarkException(Class<?> specifiedClass) { - super("Class [" + specifiedClass + "] does not implement the " + Benchmark.class.getName() - + " interface.", "Add 'extends " + SimpleBenchmark.class + "' to the class declaration."); - } - } - - public static class InvalidDebugRepsException extends ErrorInUsageException { - public InvalidDebugRepsException(String arg) { - super("Invalid debug reps: " + arg); - } - } - - // TODO: should remove the caliper stack frames.... - public static class ExceptionFromUserCodeException extends UserException { - public ExceptionFromUserCodeException(Throwable t) { - super("An exception was thrown from the benchmark code."); - initCause(t); - } - @Override public void display() { - System.err.println(getMessage()); - getCause().printStackTrace(System.err); - } - } -} diff --git a/caliper/src/main/java/com/google/caliper/Vm.java b/caliper/src/main/java/com/google/caliper/Vm.java deleted file mode 100644 index 1cc45a9..0000000 --- a/caliper/src/main/java/com/google/caliper/Vm.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (C) 2010 Google Inc. - * - * 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.google.caliper; - -import com.google.common.collect.ImmutableList; - -import java.io.File; -import java.util.List; - -class Vm { - public List<String> getVmSpecificOptions(MeasurementType type, Arguments arguments) { - return ImmutableList.of(); - } - - /** - * Returns a process builder to run this VM. - * - * @param vmArgs the path to the VM followed by VM arguments. - * @param applicationArgs arguments to the target process - */ - public ProcessBuilder newProcessBuilder(File workingDirectory, String classPath, - ImmutableList<String> vmArgs, String className, ImmutableList<String> applicationArgs) { - ProcessBuilder result = new ProcessBuilder(); - result.directory(workingDirectory); - result.command().addAll(vmArgs); - result.command().add("-cp"); - result.command().add(classPath); - result.command().add(className); - result.command().addAll(applicationArgs); - return result; - } -} diff --git a/caliper/src/main/java/com/google/caliper/VmFactory.java b/caliper/src/main/java/com/google/caliper/VmFactory.java deleted file mode 100644 index 408e34c..0000000 --- a/caliper/src/main/java/com/google/caliper/VmFactory.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (C) 2010 Google Inc. - * - * 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.google.caliper; - -import com.google.common.collect.ImmutableSet; - -import java.util.Arrays; -import java.util.List; - -public final class VmFactory { - public static ImmutableSet<String> defaultVms() { - String vmName = DalvikVm.isDalvikVm() - ? DalvikVm.vmName() - : StandardVm.defaultVmName(); - return ImmutableSet.of(vmName); - } - - public Vm createVm(Scenario scenario) { - List<String> vmList = Arrays.asList(scenario.getVariables().get(Scenario.VM_KEY).split("\\s+")); - Vm vm = null; - if (!vmList.isEmpty()) { - if (vmList.get(0).endsWith("app_process")) { - vm = new DalvikVm(); - } else if (vmList.get(0).endsWith("java")) { - vm = new StandardVm(); - } - } - if (vm == null) { - vm = new Vm(); - } - return vm; - } -} diff --git a/caliper/src/main/java/com/google/caliper/Xml.java b/caliper/src/main/java/com/google/caliper/Xml.java deleted file mode 100644 index 2aeebda..0000000 --- a/caliper/src/main/java/com/google/caliper/Xml.java +++ /dev/null @@ -1,160 +0,0 @@ -/* - * Copyright (C) 2010 Google Inc. - * - * 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.google.caliper; - -import com.google.common.collect.ImmutableMap; - -import org.w3c.dom.Document; -import org.w3c.dom.Element; -import org.w3c.dom.Node; - -import java.io.InputStream; -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.Locale; - -import javax.xml.parsers.DocumentBuilderFactory; - -/** - * This exists for backwards compatibility with old data, which is stored in XML format. - * All new data is stored in JSON. - */ -public final class Xml { - private static final String DATE_FORMAT_STRING = "yyyy-MM-dd'T'HH:mm:ssz"; - private static final String ENVIRONMENT_ELEMENT_NAME = "environment"; - private static final String RESULT_ELEMENT_NAME = "result"; - private static final String RUN_ELEMENT_NAME = "run"; - private static final String BENCHMARK_ATTRIBUTE = "benchmark"; - private static final String EXECUTED_TIMESTAMP_ATTRIBUTE = "executedTimestamp"; - private static final String OLD_SCENARIO_ELEMENT_NAME = "scenario"; - // for backwards compatibility, use a different name - private static final String SCENARIO_ELEMENT_NAME = "newScenario"; - private static final String MEASUREMENTS_ELEMENT_NAME = "measurements"; - private static final String TIME_EVENT_LOG_ELEMENT_NAME = "eventLog"; - - private static Result readResultElement(Element element) throws Exception { - Environment environment = null; - Run run = null; - for (Node topLevelNode : XmlUtils.childrenOf(element)) { - if (topLevelNode.getNodeName().equals(ENVIRONMENT_ELEMENT_NAME)) { - Element environmentElement = (Element) topLevelNode; - environment = readEnvironmentElement(environmentElement); - } else if (topLevelNode.getNodeName().equals(RUN_ELEMENT_NAME)) { - run = readRunElement((Element) topLevelNode); - } else { - throw new RuntimeException("illegal node name: " + topLevelNode.getNodeName()); - } - } - - if (environment == null || run == null) { - throw new RuntimeException("missing environment or run elements"); - } - - return new Result(run, environment); - } - - private static Environment readEnvironmentElement(Element element) { - return new Environment(XmlUtils.attributesOf(element)); - } - - private static Run readRunElement(Element element) throws Exception { - String benchmarkName = element.getAttribute(BENCHMARK_ATTRIBUTE); - String executedDateString = element.getAttribute(EXECUTED_TIMESTAMP_ATTRIBUTE); - Date executedDate = new SimpleDateFormat(DATE_FORMAT_STRING, Locale.US) - .parse(executedDateString); - - ImmutableMap.Builder<Scenario, ScenarioResult> measurementsBuilder = ImmutableMap.builder(); - for (Node scenarioNode : XmlUtils.childrenOf(element)) { - Element scenarioElement = (Element) scenarioNode; - Scenario scenario = new Scenario(XmlUtils.attributesOf(scenarioElement)); - ScenarioResult scenarioResult; - - // for backwards compatibility with older runs - if (scenarioNode.getNodeName().equals(OLD_SCENARIO_ELEMENT_NAME)) { - MeasurementSet measurement = - Json.measurementSetFromJson(scenarioElement.getTextContent()); - scenarioResult = new ScenarioResult(measurement, "", - null, null, null, null); - } else if (scenarioNode.getNodeName().equals(SCENARIO_ELEMENT_NAME)) { - MeasurementSet timeMeasurementSet = null; - String eventLog = null; - for (Node node : XmlUtils.childrenOf(scenarioElement)) { - if (node.getNodeName().equals(MEASUREMENTS_ELEMENT_NAME)) { - timeMeasurementSet = Json.measurementSetFromJson(node.getTextContent()); - } else if (node.getNodeName().equals(TIME_EVENT_LOG_ELEMENT_NAME)) { - eventLog = node.getTextContent(); - } else { - throw new RuntimeException("illegal node name: " + node.getNodeName()); - } - } - if (timeMeasurementSet == null || eventLog == null) { - throw new RuntimeException("missing node \"" + MEASUREMENTS_ELEMENT_NAME + "\" or \"" - + TIME_EVENT_LOG_ELEMENT_NAME + "\""); - } - // "new Measurement[0]" used instead of empty varargs argument since MeasurementSet has - // an empty private constructor. - scenarioResult = new ScenarioResult(timeMeasurementSet, eventLog, - null, null, null, null); - } else { - throw new RuntimeException("illegal node name: " + scenarioNode.getNodeName()); - } - - measurementsBuilder.put(scenario, scenarioResult); - } - - return new Run(measurementsBuilder.build(), benchmarkName, executedDate); - } - - /** - * Creates a result by decoding XML from the specified stream. The XML should - * be consistent with the format emitted by the now deleted runToXml(Run, OutputStream). - */ - public static Run runFromXml(InputStream in) { - try { - Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(in); - return readRunElement(document.getDocumentElement()); - } catch (Exception e) { - throw new IllegalStateException("Malformed XML document", e); - } - } - - /** - * Creates an environment by decoding XML from the specified stream. The XML should - * be consistent with the format emitted by the now deleted - * environmentToXml(Environment, OutputStream). - */ - public static Environment environmentFromXml(InputStream in) { - try { - Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(in); - Element environmentElement = document.getDocumentElement(); - return readEnvironmentElement(environmentElement); - } catch (Exception e) { - throw new IllegalStateException("Malformed XML document", e); - } - } - - public static Result resultFromXml(InputStream in) { - try { - Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(in); - return readResultElement(document.getDocumentElement()); - } catch (Exception e) { - throw new IllegalStateException("Malformed XML document", e); - } - } - - private Xml() {} -} diff --git a/caliper/src/main/java/com/google/caliper/XmlUtils.java b/caliper/src/main/java/com/google/caliper/XmlUtils.java deleted file mode 100644 index 458e6fa..0000000 --- a/caliper/src/main/java/com/google/caliper/XmlUtils.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (C) 2010 Google Inc. - * - * 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.google.caliper; - -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; - -import org.w3c.dom.Attr; -import org.w3c.dom.Element; -import org.w3c.dom.NamedNodeMap; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; - -public final class XmlUtils { - public static ImmutableList<Node> childrenOf(Node node) { - NodeList children = node.getChildNodes(); - ImmutableList.Builder<Node> result = ImmutableList.builder(); - for (int i = 0, size = children.getLength(); i < size; i++) { - result.add(children.item(i)); - } - return result.build(); - } - - public static ImmutableMap<String, String> attributesOf(Element element) { - NamedNodeMap map = element.getAttributes(); - ImmutableMap.Builder<String, String> result = ImmutableMap.builder(); - for (int i = 0, size = map.getLength(); i < size; i++) { - Attr attr = (Attr) map.item(i); - result.put(attr.getName(), attr.getValue()); - } - return result.build(); - } - - private XmlUtils() {} -} diff --git a/caliper/src/main/java/com/google/caliper/api/AfterRep.java b/caliper/src/main/java/com/google/caliper/api/AfterRep.java new file mode 100644 index 0000000..628edcc --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/api/AfterRep.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2013 Google Inc. + * + * 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.google.caliper.api; + +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import com.google.common.annotations.Beta; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +/** + * Apply this annotation to any method without parameters to have it run after each rep of a + * {@link Macrobenchmark}. + */ +@Retention(RUNTIME) +@Target(METHOD) +@Beta +public @interface AfterRep {} diff --git a/caliper/src/main/java/com/google/caliper/api/BeforeRep.java b/caliper/src/main/java/com/google/caliper/api/BeforeRep.java new file mode 100644 index 0000000..b4ae203 --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/api/BeforeRep.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2013 Google Inc. + * + * 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.google.caliper.api; + +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import com.google.common.annotations.Beta; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +/** + * Apply this annotation to any method without parameters to have it run before each rep of a + * {@link Macrobenchmark}. + */ +@Retention(RUNTIME) +@Target(METHOD) +@Beta +public @interface BeforeRep {} diff --git a/caliper/src/main/java/com/google/caliper/api/Footprint.java b/caliper/src/main/java/com/google/caliper/api/Footprint.java new file mode 100644 index 0000000..0fa7b8c --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/api/Footprint.java @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2012 Google Inc. + * + * 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.google.caliper.api; + +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +/** + * Annotates a method whose return value is an object whose total memory footprint is to be + * measured. + */ +@Retention(RUNTIME) +@Target(METHOD) +public @interface Footprint { + /** + * Optionally ignore instances of the specified types (including subclasses) when measuring. For + * example, {@code @Footprint(ignore = Element.class) public Set<Element> set() {...}} would + * measure the size of the set while ignoring the size of the elements. + */ + Class<?>[] exclude() default {}; +} diff --git a/caliper/src/main/java/com/google/caliper/api/Macrobenchmark.java b/caliper/src/main/java/com/google/caliper/api/Macrobenchmark.java new file mode 100644 index 0000000..aac4763 --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/api/Macrobenchmark.java @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2013 Google Inc. + * + * 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.google.caliper.api; + +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import com.google.common.annotations.Beta; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +/** + * Apply this annotation to any method without parameters to have it timed as a macrobenchmark. A + * macrobenchmark is roughly defined as any benchmark whose runtime is large enough that the + * granularity of the {@linkplain System#nanoTime clock} is not a factor in measurement. Thus, each + * repetition of the benchmark code can be timed individually. + * + * <p>Additionally, since each rep is independently timed, setup and tear down logic can be + * performed in between each using the {@link BeforeRep} and {@link AfterRep} annotations + * respectively. + */ +@Retention(RUNTIME) +@Target(METHOD) +@Beta +public @interface Macrobenchmark {} diff --git a/caliper/src/main/java/com/google/caliper/api/ResultProcessor.java b/caliper/src/main/java/com/google/caliper/api/ResultProcessor.java new file mode 100644 index 0000000..1cc0ee0 --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/api/ResultProcessor.java @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2011 Google Inc. + * + * 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.google.caliper.api; + +import com.google.caliper.model.Trial; + +import java.io.Closeable; + +/** + * Interface for processing results as they complete. Callers must invoke {@link #close()} after + * all trials have been {@linkplain #processTrial processed}. + * + * <p>Implementations must have a public no argument constructor. + */ +public interface ResultProcessor extends Closeable { + void processTrial(Trial trial); +} diff --git a/caliper/src/main/java/com/google/caliper/api/SkipThisScenarioException.java b/caliper/src/main/java/com/google/caliper/api/SkipThisScenarioException.java new file mode 100644 index 0000000..90b6fc1 --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/api/SkipThisScenarioException.java @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2011 Google Inc. + * + * 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.google.caliper.api; + +/** + * Throw this exception from your benchmark class's setUp method, or benchmark method + * to indicate that the combination of parameters supplied should not be benchmarked. For example, + * while you might want to test <i>most</i> combinations of the parameters {@code size} and {@code + * comparator}, you might not want to test the specific combination of {@code size=100000000} and + * {@code comparator=reallyExpensiveComparator}. + */ +@SuppressWarnings("serial") +public final class SkipThisScenarioException extends RuntimeException {} diff --git a/caliper/src/main/java/com/google/caliper/api/VmOptions.java b/caliper/src/main/java/com/google/caliper/api/VmOptions.java new file mode 100644 index 0000000..db73da8 --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/api/VmOptions.java @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2009 Google Inc. + * + * 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.google.caliper.api; + +import com.google.common.annotations.Beta; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * An annotation applied to a benchmark that specifies flags to be applied to the VM. These flags + * are applied before those specified on the command-line and thus are not guaranteed to be applied. + * + * <p>This API is likely to change. + */ +// TODO(gak): Support platform (e.g. Android and Jvm) specific options. +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +@Beta +public @interface VmOptions { + String[] value() default {}; +} diff --git a/caliper/src/main/java/com/google/caliper/bridge/AbstractLogMessageVisitor.java b/caliper/src/main/java/com/google/caliper/bridge/AbstractLogMessageVisitor.java new file mode 100644 index 0000000..f38e09c --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/bridge/AbstractLogMessageVisitor.java @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2012 Google Inc. + * + * 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.google.caliper.bridge; + +/** + * An abstract {@link LogMessageVisitor} with no-op implementations for the visit methods - + * provided so that all visitors don't have to override all methods. + */ +public abstract class AbstractLogMessageVisitor implements LogMessageVisitor { + @Override public void visit(GcLogMessage logMessage) {} + + @Override public void visit(FailureLogMessage logMessage) {} + + @Override public void visit(HotspotLogMessage logMessage) {} + + @Override public void visit(StartMeasurementLogMessage logMessage) {} + + @Override public void visit(StopMeasurementLogMessage logMessage) {} + + @Override public void visit(VmOptionLogMessage logMessage) {} + + @Override public void visit(VmPropertiesLogMessage logMessage) {} +} diff --git a/caliper/src/main/java/com/google/caliper/bridge/BridgeModule.java b/caliper/src/main/java/com/google/caliper/bridge/BridgeModule.java new file mode 100644 index 0000000..0d6ddea --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/bridge/BridgeModule.java @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2012 Google Inc. + * + * 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.google.caliper.bridge; + +import com.google.caliper.util.Parser; +import dagger.Module; +import dagger.Provides; + +/** + * Bindings for {@link Parser parsers} for {@link com.google.caliper.model model} classes. + */ +@Module +public final class BridgeModule { + @Provides static Parser<LogMessage> provideLogMessageParser(LogMessageParser parser) { + return parser; + } +} diff --git a/caliper/src/main/java/com/google/caliper/bridge/CommandLineSerializer.java b/caliper/src/main/java/com/google/caliper/bridge/CommandLineSerializer.java new file mode 100644 index 0000000..f063355 --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/bridge/CommandLineSerializer.java @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2014 Google Inc. + * + * 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.google.caliper.bridge; + +import static com.google.common.base.Preconditions.checkState; + +import com.google.common.io.BaseEncoding; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; + +/** + * Serializes and deserializes WorkerSpecs as base64 encoded strings so they can be passed on the + * command line to the worker. + * + * <p>Java serialization is a appropriate in this usecase because there are no compatibility + * concerns. The messages encoded/decoded by this class are only used to communicate with another + * JVM that is running with the same version of the java classes. Also, it should be lighter weight + * and faster than other common serialization solutions. + */ +public final class CommandLineSerializer { + private CommandLineSerializer() {} + + /** Returns the given serializable object as a base64 encoded String. */ + public static String render(WorkerSpec message) { + ByteArrayOutputStream bytes = new ByteArrayOutputStream(); + try { + ObjectOutputStream out = new ObjectOutputStream(bytes); + out.writeObject(message); + out.close(); + return BaseEncoding.base64().encode(bytes.toByteArray()); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + /** Parses the given base64 encoded string as an object of the given type. */ + public static WorkerSpec parse(String arg) { + try { + ByteArrayInputStream bais = new ByteArrayInputStream(BaseEncoding.base64().decode(arg)); + ObjectInputStream in = new ObjectInputStream(bais); + WorkerSpec instance = (WorkerSpec) in.readObject(); + in.close(); + checkState(bais.read() == -1, + "Message %s contains more than one object.", arg); + return instance; + } catch (IOException e) { + throw new RuntimeException(e); // assertion error? + } catch (ClassNotFoundException e) { + throw new RuntimeException("cannot decode message", e); + } + } +} diff --git a/caliper/src/main/java/com/google/caliper/bridge/FailureLogMessage.java b/caliper/src/main/java/com/google/caliper/bridge/FailureLogMessage.java new file mode 100644 index 0000000..4321a67 --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/bridge/FailureLogMessage.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2012 Google Inc. + * + * 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.google.caliper.bridge; + +import static com.google.common.base.Preconditions.checkNotNull; + +import com.google.common.base.Throwables; + +import java.io.Serializable; + +/** + * A message containing information on a failure encountered by the worker JVM. + */ +public class FailureLogMessage extends LogMessage implements Serializable { + private static final long serialVersionUID = 1L; + + private final String stackTrace; + + public FailureLogMessage(Throwable e) { + this(Throwables.getStackTraceAsString(e)); + } + + public FailureLogMessage(String stackTrace) { + this.stackTrace = checkNotNull(stackTrace); + } + + public String stackTrace() { + return stackTrace; + } + + @Override + public void accept(LogMessageVisitor visitor) { + visitor.visit(this); + } + + @Override + public int hashCode() { + return stackTrace.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } else if (obj instanceof FailureLogMessage) { + FailureLogMessage that = (FailureLogMessage) obj; + return this.stackTrace.equals(that.stackTrace); + } else { + return false; + } + } +} diff --git a/caliper/src/main/java/com/google/caliper/bridge/GcLogMessage.java b/caliper/src/main/java/com/google/caliper/bridge/GcLogMessage.java new file mode 100644 index 0000000..2d59788 --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/bridge/GcLogMessage.java @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2012 Google Inc. + * + * 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.google.caliper.bridge; + +import static com.google.common.base.Preconditions.checkNotNull; + +import com.google.caliper.util.ShortDuration; +import com.google.common.base.MoreObjects; +import com.google.common.base.Objects; + +/** + * A message representing output produced by the JVM when {@code -XX:+PrintGC} is enabled. + */ +public final class GcLogMessage extends LogMessage { + /** + * The type of the garbage collection performed. + */ + public static enum Type { + FULL, + INCREMENTAL, + } + + private final Type type; + private final ShortDuration duration; + + GcLogMessage(Type type, ShortDuration duration) { + this.type = checkNotNull(type); + this.duration = checkNotNull(duration); + } + + public Type type() { + return type; + } + + public ShortDuration duration() { + return duration; + } + + @Override + public void accept(LogMessageVisitor visitor) { + visitor.visit(this); + } + + @Override + public int hashCode() { + return Objects.hashCode(type, duration); + } + + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } else if (obj instanceof GcLogMessage) { + GcLogMessage that = (GcLogMessage) obj; + return this.type == that.type + && this.duration.equals(that.duration); + } else { + return false; + } + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .addValue(type) + .add("duration", duration) + .toString(); + } +} diff --git a/caliper/src/main/java/com/google/caliper/bridge/HotspotLogMessage.java b/caliper/src/main/java/com/google/caliper/bridge/HotspotLogMessage.java new file mode 100644 index 0000000..0670153 --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/bridge/HotspotLogMessage.java @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2012 Google Inc. + * + * 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.google.caliper.bridge; + +/** + * A message representing output produced by the JVM when {@code -XX:+PrintCompliation} is enabled. + */ +public final class HotspotLogMessage extends LogMessage { + HotspotLogMessage() {} + + @Override + public void accept(LogMessageVisitor visitor) { + visitor.visit(this); + } +} diff --git a/caliper/src/main/java/com/google/caliper/bridge/LogMessage.java b/caliper/src/main/java/com/google/caliper/bridge/LogMessage.java new file mode 100644 index 0000000..fc4a2ad --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/bridge/LogMessage.java @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2012 Google Inc. + * + * 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.google.caliper.bridge; + +/** + * A single line of output produced by a {@code Worker}. + */ +public abstract class LogMessage { + public abstract void accept(LogMessageVisitor visitor); +} diff --git a/caliper/src/main/java/com/google/caliper/bridge/LogMessageParser.java b/caliper/src/main/java/com/google/caliper/bridge/LogMessageParser.java new file mode 100644 index 0000000..68ab785 --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/bridge/LogMessageParser.java @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2013 Google Inc. + * + * 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.google.caliper.bridge; + +import static java.util.concurrent.TimeUnit.SECONDS; + +import com.google.caliper.util.Parser; +import com.google.caliper.util.ShortDuration; + +import java.math.BigDecimal; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import javax.inject.Inject; + +/** + * Parses {@link LogMessage} strings. + */ +final class LogMessageParser implements Parser<LogMessage> { + @Inject LogMessageParser() {} + + private static final Pattern GC_PATTERN = + Pattern.compile(".*\\[(?:(Full) )?GC.*(\\d+\\.\\d+) secs\\]"); + private static final Pattern JIT_PATTERN = + Pattern.compile(".*::.*( \\(((\\d+ bytes)|(static))\\))?"); + private static final Pattern VM_OPTION_PATTERN = + Pattern.compile("\\s*(\\w+)\\s+(\\w+)\\s+:?=\\s+([^\\s]*)\\s+\\{([^}]*)\\}\\s*"); + + @Override public LogMessage parse(CharSequence text) { + // TODO(gak): do this stuff in terms of CharSequence instead of String + String string = text.toString(); + Matcher gcMatcher = GC_PATTERN.matcher(string); + if (gcMatcher.matches()) { + return new GcLogMessage( + "Full".equals(gcMatcher.group(1)) + ? GcLogMessage.Type.FULL + : GcLogMessage.Type.INCREMENTAL, + ShortDuration.of(BigDecimal.valueOf(Double.parseDouble(gcMatcher.group(2))), SECONDS)); + } + Matcher jitMatcher = JIT_PATTERN.matcher(string); + if (jitMatcher.matches()) { + return new HotspotLogMessage(); + } + Matcher vmOptionMatcher = VM_OPTION_PATTERN.matcher(string); + if (vmOptionMatcher.matches()) { + return new VmOptionLogMessage(vmOptionMatcher.group(2), vmOptionMatcher.group(3)); + } + return null; + } +} diff --git a/caliper/src/main/java/com/google/caliper/bridge/LogMessageVisitor.java b/caliper/src/main/java/com/google/caliper/bridge/LogMessageVisitor.java new file mode 100644 index 0000000..f91bbf1 --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/bridge/LogMessageVisitor.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2012 Google Inc. + * + * 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.google.caliper.bridge; + +/** + * A visitor for the various subclasses of {@link LogMessage}. + */ +public interface LogMessageVisitor { + void visit(GcLogMessage logMessage); + void visit(FailureLogMessage logMessage); + void visit(HotspotLogMessage logMessage); + void visit(StartMeasurementLogMessage logMessage); + void visit(StopMeasurementLogMessage logMessage); + void visit(VmOptionLogMessage logMessage); + void visit(VmPropertiesLogMessage logMessage); +} diff --git a/caliper/src/main/java/com/google/caliper/bridge/OpenedSocket.java b/caliper/src/main/java/com/google/caliper/bridge/OpenedSocket.java new file mode 100644 index 0000000..510d9e6 --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/bridge/OpenedSocket.java @@ -0,0 +1,217 @@ +/* + * Copyright (C) 2014 Google Inc. + * + * 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.google.caliper.bridge; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.io.Closeable; +import java.io.EOFException; +import java.io.Flushable; +import java.io.IOException; +import java.io.InputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.OutputStream; +import java.io.Serializable; +import java.net.Socket; +import java.nio.channels.SocketChannel; + +/** + * A simple tuple for the opened streams of a socket. + */ +public final class OpenedSocket { + /** + * Waits for the channel to connect and returns a new {@link OpenedSocket}. + */ + public static OpenedSocket fromSocket(SocketChannel socket) throws IOException { + socket.configureBlocking(true); + socket.finishConnect(); + return fromSocket(socket.socket()); + } + /** + * Returns a new {@link OpenedSocket} for the given connected {@link Socket} instance. + */ + public static OpenedSocket fromSocket(Socket socket) throws IOException { + // Setting this to true disables Nagle's algorithm (RFC 896) which seeks to decrease packet + // overhead by buffering writes while there are packets outstanding (i.e. haven't been ack'd). + // This interacts poorly with another TCP feature called 'delayed acks' (RFC 1122) if the + // application sends lots of small messages (which we do!). Without this enabled messages sent + // by the worker may be delayed by up to the delayed ack timeout (on linux this is 40-500ms, + // though in practice I have only observed 40ms). So we need to enable the TCP_NO_DELAY option + // here. + socket.setTcpNoDelay(true); + // N.B. order is important here, constructing an ObjectOutputStream requires writing a header + // and constructing an ObjectInputStream requires reading that header. So we always need to + // construct the OOS first so we don't deadlock. + ObjectOutputStream output = new ObjectOutputStream(getOutputStream(socket)); + ObjectInputStream input = new ObjectInputStream(getInputStream(socket)); + return new OpenedSocket(new Reader(input), new Writer(output)); + } + + private final Reader reader; + private final Writer writer; + + private OpenedSocket(Reader reader, + Writer objectOutputStream) { + this.reader = reader; + this.writer = objectOutputStream; + } + + public Reader reader() { + return reader; + } + + public Writer writer() { + return writer; + } + + /** Reads objects from the socket. */ + public static final class Reader implements Closeable { + private final ObjectInputStream input; + + Reader(ObjectInputStream is) { + this.input = is; + } + + /** Returns the next object, or {@code null} if we are at EOF. */ + public Serializable read() throws IOException { + try { + return (Serializable) checkNotNull(input.readObject()); + } catch (EOFException eof) { + // TODO(lukes): The only 'better' way to handle this would be to use an explicit poison pill + // marker in the stream. Otherwise we just have to catch EOFException. + return null; + } catch (ClassNotFoundException e) { + throw new AssertionError(e); + } + } + + @Override public void close() throws IOException { + input.close(); + } + } + + /** Writes objects to the socket. */ + public static final class Writer implements Closeable, Flushable { + private final ObjectOutputStream output; + + Writer(ObjectOutputStream output) { + this.output = output; + } + + /** Returns the next object, or {@code null} if we are at EOF. */ + public void write(Serializable serializable) throws IOException { + output.writeObject(serializable); + } + + @Override public void flush() throws IOException { + output.flush(); + } + + @Override public void close() throws IOException { + output.close(); + } + } + + /** + * Returns an {@link OutputStream} for the socket, but unlike {@link Socket#getOutputStream()} + * when you call {@link OutputStream#close() close} it only closes the output end of the socket + * instead of the entire socket. + */ + private static OutputStream getOutputStream(final Socket socket) throws IOException { + final OutputStream delegate = socket.getOutputStream(); + return new OutputStream() { + + @Override public void close() throws IOException { + delegate.flush(); + synchronized (socket) { + socket.shutdownOutput(); + if (socket.isInputShutdown()) { + socket.close(); + } + } + } + + // Boring delegates from here on down + @Override public void write(int b) throws IOException { + delegate.write(b); + } + + @Override public void write(byte[] b) throws IOException { + delegate.write(b); + } + + @Override public void write(byte[] b, int off, int len) throws IOException { + delegate.write(b, off, len); + } + + @Override public void flush() throws IOException { + delegate.flush(); + } + }; + } + + /** + * Returns an {@link InputStream} for the socket, but unlike {@link Socket#getInputStream()} + * when you call {@link InputStream#close() close} it only closes the input end of the socket + * instead of the entire socket. + */ + private static InputStream getInputStream(final Socket socket) throws IOException { + final InputStream delegate = socket.getInputStream(); + return new InputStream() { + @Override public void close() throws IOException { + synchronized (socket) { + socket.shutdownInput(); + if (socket.isOutputShutdown()) { + socket.close(); + } + } + } + + // Boring delegates from here on down + @Override public int read() throws IOException { + return delegate.read(); + } + + @Override public int read(byte[] b) throws IOException { + return delegate.read(b); + } + + @Override public int read(byte[] b, int off, int len) throws IOException { + return delegate.read(b, off, len); + } + + @Override public long skip(long n) throws IOException { + return delegate.skip(n); + } + + @Override public int available() throws IOException { + return delegate.available(); + } + + @Override public void mark(int readlimit) { + delegate.mark(readlimit); + } + + @Override public void reset() throws IOException { + delegate.reset(); + } + + @Override public boolean markSupported() { + return delegate.markSupported(); + } + }; + } +} diff --git a/caliper/src/main/java/com/google/caliper/MeasurementType.java b/caliper/src/main/java/com/google/caliper/bridge/Renderer.java index 30de69f..f2c9037 100644 --- a/caliper/src/main/java/com/google/caliper/MeasurementType.java +++ b/caliper/src/main/java/com/google/caliper/bridge/Renderer.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 Google Inc. + * Copyright (C) 2012 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,11 +14,8 @@ * limitations under the License. */ -package com.google.caliper; +package com.google.caliper.bridge; -import com.google.common.annotations.GwtCompatible; - -@GwtCompatible -public enum MeasurementType { - TIME, INSTANCE, MEMORY, DEBUG +public interface Renderer<T> { + String render(T object); } diff --git a/caliper/src/main/java/com/google/caliper/bridge/ShouldContinueMessage.java b/caliper/src/main/java/com/google/caliper/bridge/ShouldContinueMessage.java new file mode 100644 index 0000000..114f5f6 --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/bridge/ShouldContinueMessage.java @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2013 Google Inc. + * + * 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.google.caliper.bridge; + +import java.io.Serializable; + +/** + * A message sent from the runner to the worker to indicate whether or not measuring should + * continue. + */ +public class ShouldContinueMessage implements Serializable { + private static final long serialVersionUID = 1L; + + private final boolean shouldContinue; + private final boolean warmupComplete; + + public ShouldContinueMessage(boolean shouldContinue, boolean warmupComplete) { + this.shouldContinue = shouldContinue; + this.warmupComplete = warmupComplete; + } + + public boolean shouldContinue() { + return shouldContinue; + } + + @Override public int hashCode() { + return Boolean.valueOf(shouldContinue).hashCode(); + } + + @Override public boolean equals(Object obj) { + return obj instanceof ShouldContinueMessage + && shouldContinue == ((ShouldContinueMessage) obj).shouldContinue; + } + + public boolean isWarmupComplete() { + return warmupComplete; + } +} diff --git a/caliper/src/main/java/com/google/caliper/bridge/StartMeasurementLogMessage.java b/caliper/src/main/java/com/google/caliper/bridge/StartMeasurementLogMessage.java new file mode 100644 index 0000000..e04dcd5 --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/bridge/StartMeasurementLogMessage.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2012 Google Inc. + * + * 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.google.caliper.bridge; + +import java.io.Serializable; + +/** + * A message signaling that the timing interval has started in the worker. + */ +// TODO(gak): rename in terms of measurement +public class StartMeasurementLogMessage extends LogMessage implements Serializable { + private static final long serialVersionUID = 1L; + + @Override public void accept(LogMessageVisitor visitor) { + visitor.visit(this); + } + + @Override public int hashCode() { + return StartMeasurementLogMessage.class.hashCode(); + } + + @Override + public boolean equals(Object obj) { + return obj instanceof StartMeasurementLogMessage; + } +} diff --git a/caliper/src/main/java/com/google/caliper/bridge/StartupAnnounceMessage.java b/caliper/src/main/java/com/google/caliper/bridge/StartupAnnounceMessage.java new file mode 100644 index 0000000..7efd9b3 --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/bridge/StartupAnnounceMessage.java @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2013 Google Inc. + * + * 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.google.caliper.bridge; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.io.Serializable; +import java.util.UUID; + +/** + * A message sent from the worker to the runner immediately after startup to identify the trial + * that it is performing. + */ +public final class StartupAnnounceMessage implements Serializable { + private static final long serialVersionUID = 1L; + + private final UUID trialId; + + public StartupAnnounceMessage(UUID trialId) { + this.trialId = checkNotNull(trialId); + } + + public UUID trialId() { + return trialId; + } + + @Override public int hashCode() { + return trialId.hashCode(); + } + + @Override public boolean equals(Object obj) { + return obj instanceof StartupAnnounceMessage + && trialId.equals(((StartupAnnounceMessage) obj).trialId); + } +} diff --git a/caliper/src/main/java/com/google/caliper/bridge/StopMeasurementLogMessage.java b/caliper/src/main/java/com/google/caliper/bridge/StopMeasurementLogMessage.java new file mode 100644 index 0000000..b93ee50 --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/bridge/StopMeasurementLogMessage.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2012 Google Inc. + * + * 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.google.caliper.bridge; + +import com.google.caliper.model.Measurement; +import com.google.common.base.Objects; +import com.google.common.collect.ImmutableList; + +import java.io.Serializable; + +/** + * A message signaling that the timing interval has ended in the worker. + */ +public class StopMeasurementLogMessage extends LogMessage implements Serializable { + private static final long serialVersionUID = 1L; + + private final ImmutableList<Measurement> measurements; + + public StopMeasurementLogMessage(Iterable<Measurement> measurements) { + this.measurements = ImmutableList.copyOf(measurements); + } + + public ImmutableList<Measurement> measurements() { + return measurements; + } + + @Override public void accept(LogMessageVisitor visitor) { + visitor.visit(this); + } + + @Override public int hashCode() { + return Objects.hashCode(measurements); + } + + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } else if (obj instanceof StopMeasurementLogMessage) { + StopMeasurementLogMessage that = (StopMeasurementLogMessage) obj; + return this.measurements.equals(that.measurements); + } else { + return false; + } + } +} diff --git a/caliper/src/main/java/com/google/caliper/bridge/VmOptionLogMessage.java b/caliper/src/main/java/com/google/caliper/bridge/VmOptionLogMessage.java new file mode 100644 index 0000000..dd8d5cd --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/bridge/VmOptionLogMessage.java @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2012 Google Inc. + * + * 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.google.caliper.bridge; + +import static com.google.common.base.Preconditions.checkNotNull; + + +/** + * A message representing output produced by the JVM when {@code -XX:+PrintFlagsFinal} is enabled. + */ +public final class VmOptionLogMessage extends LogMessage { + private final String name; + private final String value; + + VmOptionLogMessage(String name, String value) { + this.name = checkNotNull(name); + this.value = checkNotNull(value); + } + + public String name() { + return name; + } + + public String value() { + return value; + } + + @Override + public void accept(LogMessageVisitor visitor) { + visitor.visit(this); + } +} diff --git a/caliper/src/main/java/com/google/caliper/bridge/VmPropertiesLogMessage.java b/caliper/src/main/java/com/google/caliper/bridge/VmPropertiesLogMessage.java new file mode 100644 index 0000000..2e63879 --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/bridge/VmPropertiesLogMessage.java @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2012 Google Inc. + * + * 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.google.caliper.bridge; + +import static com.google.common.base.Preconditions.checkNotNull; + +import com.google.common.base.Objects; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Maps; + +import java.io.Serializable; + +/** + * A message containing a selection of {@link System#getProperties() system properties} from the + * worker JVM. + */ +public class VmPropertiesLogMessage extends LogMessage implements Serializable { + private static final long serialVersionUID = 1L; + + private final ImmutableMap<String, String> properties; + + public VmPropertiesLogMessage() { + this(ImmutableMap.copyOf(Maps.fromProperties(System.getProperties()))); + } + + public VmPropertiesLogMessage(ImmutableMap<String, String> properties) { + this.properties = checkNotNull(properties); + } + + public ImmutableMap<String, String> properties() { + return properties; + } + + @Override public void accept(LogMessageVisitor visitor) { + visitor.visit(this); + } + + @Override + public int hashCode() { + return Objects.hashCode(properties); + } + + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } else if (obj instanceof VmPropertiesLogMessage) { + VmPropertiesLogMessage that = (VmPropertiesLogMessage) obj; + return this.properties.equals(that.properties); + } else { + return false; + } + } +} diff --git a/caliper/src/main/java/com/google/caliper/bridge/WorkerSpec.java b/caliper/src/main/java/com/google/caliper/bridge/WorkerSpec.java new file mode 100644 index 0000000..022b1dc --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/bridge/WorkerSpec.java @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2011 Google Inc. + * + * 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.google.caliper.bridge; + +import com.google.caliper.model.BenchmarkSpec; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; + +import java.io.Serializable; +import java.util.UUID; + +/** + * This object is sent from the parent process to the child to tell it what to do. If the child + * does not do it, it will not get its allowance this week. + */ +public final class WorkerSpec implements Serializable { + private static final long serialVersionUID = 1L; + + public final UUID trialId; + // Should be ? extends Worker but circular build deps prevent that. + public final Class<?> workerClass; + public final ImmutableMap<String, String> workerOptions; + public final BenchmarkSpec benchmarkSpec; + + /** + * The names of the benchmark method parameters so that the method can be uniquely identified. + */ + public final ImmutableList<Class<?>> methodParameterClasses; + public final int port; + + public WorkerSpec( + UUID trialId, + Class<?> workerClass, + ImmutableMap<String, String> workerOptions, + BenchmarkSpec benchmarkSpec, + ImmutableList<Class<?>> methodParameterClasses, + int port) { + this.trialId = trialId; + this.workerClass = workerClass; + this.workerOptions = workerOptions; + this.benchmarkSpec = benchmarkSpec; + this.methodParameterClasses = methodParameterClasses; + this.port = port; + } +} diff --git a/caliper/src/main/java/com/google/caliper/bridge/package-info.java b/caliper/src/main/java/com/google/caliper/bridge/package-info.java new file mode 100644 index 0000000..2d474ee --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/bridge/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2012 Google Inc. + * + * 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. + */ + +/** + * Bridges between the runner and the worker. + */ +package com.google.caliper.bridge;
\ No newline at end of file diff --git a/caliper/src/main/java/com/google/caliper/config/CaliperConfig.java b/caliper/src/main/java/com/google/caliper/config/CaliperConfig.java new file mode 100644 index 0000000..3be0ade --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/config/CaliperConfig.java @@ -0,0 +1,211 @@ +/* + * Copyright (C) 2012 Google Inc. + * + * 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.google.caliper.config; + +import static com.google.caliper.util.Util.subgroupMap; +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.checkState; + +import com.google.caliper.api.ResultProcessor; +import com.google.caliper.platform.Platform; +import com.google.caliper.platform.VirtualMachineException; +import com.google.caliper.util.Util; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.MoreObjects; +import com.google.common.base.Strings; +import com.google.common.collect.BiMap; +import com.google.common.collect.HashBiMap; +import com.google.common.collect.ImmutableBiMap; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; + +import java.io.File; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import javax.annotation.Nullable; + +/** + * Represents caliper configuration. By default, {@code ~/.caliper/config.properties} and + * {@code global-config.properties}. + * + * @author gak@google.com (Gregory Kick) + */ +public final class CaliperConfig { + @VisibleForTesting final ImmutableMap<String, String> properties; + private final ImmutableMap<Class<? extends ResultProcessor>, ResultProcessorConfig> + resultProcessorConfigs; + + @VisibleForTesting + public CaliperConfig(ImmutableMap<String, String> properties) + throws InvalidConfigurationException { + this.properties = checkNotNull(properties); + this.resultProcessorConfigs = findResultProcessorConfigs(subgroupMap(properties, "results")); + } + + private static final Pattern CLASS_PROPERTY_PATTERN = Pattern.compile("(\\w+)\\.class"); + + private static <T> ImmutableBiMap<String, Class<? extends T>> mapGroupNamesToClasses( + ImmutableMap<String, String> groupProperties, Class<T> type) + throws InvalidConfigurationException { + BiMap<String, Class<? extends T>> namesToClasses = HashBiMap.create(); + for (Entry<String, String> entry : groupProperties.entrySet()) { + Matcher matcher = CLASS_PROPERTY_PATTERN.matcher(entry.getKey()); + if (matcher.matches() && !entry.getValue().isEmpty()) { + try { + Class<?> someClass = Util.loadClass(entry.getValue()); + checkState(type.isAssignableFrom(someClass)); + @SuppressWarnings("unchecked") + Class<? extends T> verifiedClass = (Class<? extends T>) someClass; + namesToClasses.put(matcher.group(1), verifiedClass); + } catch (ClassNotFoundException e) { + throw new InvalidConfigurationException("Cannot find result processor class: " + + entry.getValue()); + } + } + } + return ImmutableBiMap.copyOf(namesToClasses); + } + + private static ImmutableMap<Class<? extends ResultProcessor>, ResultProcessorConfig> + findResultProcessorConfigs(ImmutableMap<String, String> resultsProperties) + throws InvalidConfigurationException { + ImmutableBiMap<String, Class<? extends ResultProcessor>> processorToClass = + mapGroupNamesToClasses(resultsProperties, ResultProcessor.class); + ImmutableMap.Builder<Class<? extends ResultProcessor>, ResultProcessorConfig> builder = + ImmutableMap.builder(); + for (Entry<String, Class<? extends ResultProcessor>> entry : processorToClass.entrySet()) { + builder.put(entry.getValue(), getResultProcessorConfig(resultsProperties, entry.getKey())); + } + return builder.build(); + } + + public ImmutableMap<String, String> properties() { + return properties; + } + + /** + * Returns the configuration of the current host VM (including the flags used to create it). Any + * args specified using {@code vm.args} will also be applied + */ + public VmConfig getDefaultVmConfig(Platform platform) { + return new VmConfig.Builder(platform, platform.defaultVmHomeDir()) + .addAllOptions(platform.inputArguments()) + // still incorporate vm.args + .addAllOptions(getArgs(subgroupMap(properties, "vm"))) + .build(); + } + + public VmConfig getVmConfig(Platform platform, String name) + throws InvalidConfigurationException { + checkNotNull(name); + ImmutableMap<String, String> vmGroupMap = subgroupMap(properties, "vm"); + ImmutableMap<String, String> vmMap = subgroupMap(vmGroupMap, name); + File homeDir; + try { + homeDir = platform.customVmHomeDir(vmGroupMap, name); + } catch (VirtualMachineException e) { + throw new InvalidConfigurationException(e); + } + return new VmConfig.Builder(platform, homeDir) + .addAllOptions(getArgs(vmGroupMap)) + .addAllOptions(getArgs(vmMap)) + .build(); + } + + private static final Pattern INSTRUMENT_CLASS_PATTERN = Pattern.compile("([^\\.]+)\\.class"); + + public ImmutableSet<String> getConfiguredInstruments() { + ImmutableSet.Builder<String> resultBuilder = ImmutableSet.builder(); + for (String key : subgroupMap(properties, "instrument").keySet()) { + Matcher matcher = INSTRUMENT_CLASS_PATTERN.matcher(key); + if (matcher.matches()) { + resultBuilder.add(matcher.group(1)); + } + } + return resultBuilder.build(); + } + + public InstrumentConfig getInstrumentConfig(String name) { + checkNotNull(name); + ImmutableMap<String, String> instrumentGroupMap = subgroupMap(properties, "instrument"); + ImmutableMap<String, String> insrumentMap = subgroupMap(instrumentGroupMap, name); + @Nullable String className = insrumentMap.get("class"); + checkArgument(className != null, "no instrument configured named %s", name); + return new InstrumentConfig.Builder() + .className(className) + .addAllOptions(subgroupMap(insrumentMap, "options")) + .build(); + } + + public ImmutableSet<Class<? extends ResultProcessor>> getConfiguredResultProcessors() { + return resultProcessorConfigs.keySet(); + } + + public ResultProcessorConfig getResultProcessorConfig( + Class<? extends ResultProcessor> resultProcessorClass) { + return resultProcessorConfigs.get(resultProcessorClass); + } + + private static ResultProcessorConfig getResultProcessorConfig( + ImmutableMap<String, String> resultsProperties, String name) { + ImmutableMap<String, String> resultsMap = subgroupMap(resultsProperties, name); + return new ResultProcessorConfig.Builder() + .className(resultsMap.get("class")) + .addAllOptions(subgroupMap(resultsMap, "options")) + .build(); + } + + @Override public String toString() { + return MoreObjects.toStringHelper(this) + .add("properties", properties) + .toString(); + } + + private static List<String> getArgs(Map<String, String> properties) { + String argsString = Strings.nullToEmpty(properties.get("args")); + ImmutableList.Builder<String> args = ImmutableList.builder(); + StringBuilder arg = new StringBuilder(); + for (int i = 0; i < argsString.length(); i++) { + char c = argsString.charAt(i); + switch (c) { + case '\\': + arg.append(argsString.charAt(++i)); + break; + case ' ': + if (arg.length() > 0) { + args.add(arg.toString()); + } + arg = new StringBuilder(); + break; + default: + arg.append(c); + break; + } + } + if (arg.length() > 0) { + args.add(arg.toString()); + } + return args.build(); + } + +} diff --git a/caliper/src/main/java/com/google/caliper/config/CaliperConfigLoader.java b/caliper/src/main/java/com/google/caliper/config/CaliperConfigLoader.java new file mode 100644 index 0000000..a103cf2 --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/config/CaliperConfigLoader.java @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2011 Google Inc. + * + * 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.google.caliper.config; + +import com.google.caliper.options.CaliperOptions; +import com.google.caliper.util.Util; +import com.google.common.base.Predicates; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Iterables; +import com.google.common.collect.Maps; +import com.google.common.io.ByteSource; +import com.google.common.io.Files; + +import java.io.File; +import java.io.IOException; +import java.util.Map; + +import javax.inject.Inject; +import javax.inject.Singleton; + +/** + * Loads caliper configuration files and, if necessary, creates new versions from the defaults. + */ +@Singleton +public final class CaliperConfigLoader { + private final CaliperOptions options; + + @Inject CaliperConfigLoader(CaliperOptions options) { + this.options = options; + } + + public CaliperConfig loadOrCreate() throws InvalidConfigurationException { + File configFile = options.caliperConfigFile(); + ImmutableMap<String, String> defaults; + try { + defaults = Util.loadProperties( + Util.resourceSupplier(CaliperConfig.class, "global-config.properties")); + } catch (IOException impossible) { + throw new AssertionError(impossible); + } + + // TODO(kevinb): deal with migration issue from old-style .caliperrc + + if (configFile.exists()) { + try { + ImmutableMap<String, String> user = + Util.loadProperties(Files.asByteSource(configFile)); + return new CaliperConfig(mergeProperties(options.configProperties(), user, defaults)); + } catch (IOException keepGoing) { + } + } + + ByteSource supplier = Util.resourceSupplier(CaliperConfig.class, "default-config.properties"); + tryCopyIfNeeded(supplier, configFile); + + ImmutableMap<String, String> user; + try { + user = Util.loadProperties(supplier); + } catch (IOException e) { + throw new AssertionError(e); // class path must be messed up + } + return new CaliperConfig(mergeProperties(options.configProperties(), user, defaults)); + } + + private static ImmutableMap<String, String> mergeProperties(Map<String, String> commandLine, + Map<String, String> user, + Map<String, String> defaults) { + Map<String, String> map = Maps.newHashMap(defaults); + map.putAll(user); // overwrite and augment + map.putAll(commandLine); // overwrite and augment + Iterables.removeIf(map.values(), Predicates.equalTo("")); + return ImmutableMap.copyOf(map); + } + + private static void tryCopyIfNeeded(ByteSource supplier, File rcFile) { + if (!rcFile.exists()) { + try { + supplier.copyTo(Files.asByteSink(rcFile)); + } catch (IOException e) { + rcFile.delete(); + } + } + } +} diff --git a/caliper/src/main/java/com/google/caliper/config/ConfigModule.java b/caliper/src/main/java/com/google/caliper/config/ConfigModule.java new file mode 100644 index 0000000..62b64bb --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/config/ConfigModule.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2012 Google Inc. + * + * 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.google.caliper.config; + +import dagger.Module; +import dagger.Provides; + +import java.util.logging.LogManager; +import javax.inject.Singleton; + +/** + * Bindings for Caliper configuration. + */ +@Module +public final class ConfigModule { + + /** + * The {@code doNotRemove} parameter is required here to ensure that the logging configuration + * is loaded and used to update the logger settings before the configuration is loaded. + */ + @Provides @Singleton + static CaliperConfig provideCaliperConfig( + CaliperConfigLoader configLoader, + @SuppressWarnings("unused") LoggingConfigLoader doNotRemove) + throws InvalidConfigurationException { + + return configLoader.loadOrCreate(); + } + + @Provides static LogManager provideLogManager() { + return LogManager.getLogManager(); + } +} diff --git a/caliper/src/main/java/com/google/caliper/config/InstrumentConfig.java b/caliper/src/main/java/com/google/caliper/config/InstrumentConfig.java new file mode 100644 index 0000000..5715a16 --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/config/InstrumentConfig.java @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2012 Google Inc. + * + * 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.google.caliper.config; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.checkState; + +import com.google.caliper.model.InstrumentSpec; +import com.google.common.base.MoreObjects; +import com.google.common.base.Objects; +import com.google.common.collect.ImmutableMap; + +import java.util.Map; + +import javax.annotation.concurrent.Immutable; + +/** + * This is the configuration passed to the instrument by the user. This differs from the + * {@link InstrumentSpec} in that any number of configurations can yield the same spec + * (due to default option values). + * + * @author gak@google.com (Gregory Kick) + */ +@Immutable +public final class InstrumentConfig { + private final String className; + private final ImmutableMap<String, String> options; + + private InstrumentConfig(Builder builder) { + this.className = builder.className; + this.options = builder.optionsBuilder.build(); + } + + public String className() { + return className; + } + + public ImmutableMap<String, String> options() { + return options; + } + + @Override public boolean equals(Object obj) { + if (obj == this) { + return true; + } else if (obj instanceof InstrumentConfig) { + InstrumentConfig that = (InstrumentConfig) obj; + return this.className.equals(that.className) + && this.options.equals(that.options); + } else { + return false; + } + } + + @Override public int hashCode() { + return Objects.hashCode(className, options); + } + + @Override public String toString() { + return MoreObjects.toStringHelper(this) + .add("className", className) + .add("options", options) + .toString(); + } + + static final class Builder { + private String className; + private final ImmutableMap.Builder<String, String> optionsBuilder = ImmutableMap.builder(); + + public Builder className(String className) { + this.className = checkNotNull(className); + return this; + } + + public Builder instrumentClass(Class<?> insturmentClass) { + return className(insturmentClass.getName()); + } + + public Builder addOption(String option, String value) { + optionsBuilder.put(option, value); + return this; + } + + public Builder addAllOptions(Map<String, String> options) { + optionsBuilder.putAll(options); + return this; + } + + public InstrumentConfig build() { + checkState(className != null); + return new InstrumentConfig(this); + } + } +} diff --git a/caliper/src/main/java/com/google/caliper/config/InvalidConfigurationException.java b/caliper/src/main/java/com/google/caliper/config/InvalidConfigurationException.java new file mode 100644 index 0000000..5d1df97 --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/config/InvalidConfigurationException.java @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2012 Google Inc. + * + * 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.google.caliper.config; + +import java.io.PrintWriter; + +/** + * Thrown when an invalid configuration has been specified by the user. + * + * @author gak@google.com (Gregory Kick) + */ +public final class InvalidConfigurationException extends RuntimeException { + public InvalidConfigurationException() { + super(); + } + + public InvalidConfigurationException(String message, Throwable cause) { + super(message, cause); + } + + public InvalidConfigurationException(String message) { + super(message); + } + + public InvalidConfigurationException(Throwable cause) { + super(cause); + } + + public void display(PrintWriter writer) { + writer.println(getMessage()); + } +} diff --git a/caliper/src/main/java/com/google/caliper/config/LoggingConfigLoader.java b/caliper/src/main/java/com/google/caliper/config/LoggingConfigLoader.java new file mode 100644 index 0000000..4ee216c --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/config/LoggingConfigLoader.java @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2012 Google Inc. + * + * 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.google.caliper.config; + +import static java.util.logging.Level.SEVERE; +import static java.util.logging.Level.WARNING; + +import com.google.caliper.model.Run; +import com.google.caliper.options.CaliperDirectory; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Charsets; +import com.google.common.io.Closer; + +import org.joda.time.format.ISODateTimeFormat; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.util.logging.FileHandler; +import java.util.logging.LogManager; +import java.util.logging.Logger; +import java.util.logging.SimpleFormatter; + +import javax.inject.Inject; +import javax.inject.Singleton; + +/** + * Loading the logging configuration at {@code ~/.caliper/logging.properties} if present. + */ +@Singleton +final class LoggingConfigLoader { + private static final Logger logger = Logger.getLogger(LoggingConfigLoader.class.getName()); + + private final File caliperDirectory; + private final LogManager logManager; + private final Run run; + + @Inject LoggingConfigLoader(@CaliperDirectory File caliperDirectory, LogManager logManager, + Run run) { + this.caliperDirectory = caliperDirectory; + this.logManager = logManager; + this.run = run; + } + + @Inject void loadLoggingConfig() { + File loggingPropertiesFile = new File(caliperDirectory, "logging.properties"); + if (loggingPropertiesFile.isFile()) { + Closer closer = Closer.create(); + FileInputStream fis = null; + try { + fis = closer.register(new FileInputStream(loggingPropertiesFile)); + logManager.readConfiguration(fis); + } catch (SecurityException e) { + logConfigurationException(e); + } catch (IOException e) { + logConfigurationException(e); + } finally { + try { + closer.close(); + } catch (IOException e) { + logger.log(SEVERE, "could not close " + loggingPropertiesFile, e); + } + } + logger.info(String.format("Using logging configuration at %s", loggingPropertiesFile)); + } else { + try { + maybeLoadDefaultLogConfiguration(LogManager.getLogManager()); + } catch (SecurityException e) { + logConfigurationException(e); + } catch (IOException e) { + logConfigurationException(e); + } + } + } + + @VisibleForTesting void maybeLoadDefaultLogConfiguration(LogManager logManager) + throws SecurityException, IOException { + logManager.reset(); + File logDirectory = new File(caliperDirectory, "log"); + logDirectory.mkdirs(); + FileHandler fileHandler = new FileHandler(String.format("%s%c%s.%s.log", + logDirectory.getAbsolutePath(), File.separatorChar, + ISODateTimeFormat.basicDateTimeNoMillis().print(run.startTime()), run.id())); + fileHandler.setEncoding(Charsets.UTF_8.name()); + fileHandler.setFormatter(new SimpleFormatter()); + Logger globalLogger = logManager.getLogger(""); + globalLogger.addHandler(fileHandler); + } + + private static void logConfigurationException(Exception e) { + logger.log(WARNING, "Could not apply the logging configuration", e); + } +} diff --git a/caliper/src/main/java/com/google/caliper/config/ResultProcessorConfig.java b/caliper/src/main/java/com/google/caliper/config/ResultProcessorConfig.java new file mode 100644 index 0000000..cbaa0eb --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/config/ResultProcessorConfig.java @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2012 Google Inc. + * + * 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.google.caliper.config; + +import static com.google.common.base.Preconditions.checkNotNull; + +import com.google.common.base.MoreObjects; +import com.google.common.base.Objects; +import com.google.common.collect.ImmutableMap; + +import java.util.Map; + +/** + * This is the configuration passed to the {@link com.google.caliper.api.ResultProcessor} by the + * user. + * + * @author gak@google.com (Gregory Kick) + */ +public class ResultProcessorConfig { + private final String className; + private final ImmutableMap<String, String> options; + + private ResultProcessorConfig(Builder builder) { + this.className = builder.className; + this.options = builder.optionsBuilder.build(); + } + + public String className() { + return className; + } + + public ImmutableMap<String, String> options() { + return options; + } + + + @Override public boolean equals(Object obj) { + if (obj == this) { + return true; + } else if (obj instanceof ResultProcessorConfig) { + ResultProcessorConfig that = (ResultProcessorConfig) obj; + return this.className.equals(that.className) + && this.options.equals(that.options); + } else { + return false; + } + } + + @Override public int hashCode() { + return Objects.hashCode(className, options); + } + + @Override public String toString() { + return MoreObjects.toStringHelper(this) + .add("className", className) + .add("options", options) + .toString(); + } + + static final class Builder { + private String className; + private ImmutableMap.Builder<String, String> optionsBuilder = ImmutableMap.builder(); + + public Builder className(String className) { + this.className = checkNotNull(className); + return this; + } + + public Builder addOption(String option, String value) { + this.optionsBuilder.put(option, value); + return this; + } + + public Builder addAllOptions(Map<String, String> options) { + this.optionsBuilder.putAll(options); + return this; + } + + public ResultProcessorConfig build() { + return new ResultProcessorConfig(this); + } + } +} diff --git a/caliper/src/main/java/com/google/caliper/config/VmConfig.java b/caliper/src/main/java/com/google/caliper/config/VmConfig.java new file mode 100644 index 0000000..fcb63ca --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/config/VmConfig.java @@ -0,0 +1,144 @@ +/* + * Copyright (C) 2012 Google Inc. + * + * 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.google.caliper.config; + +import static com.google.common.base.Preconditions.checkNotNull; + +import com.google.caliper.model.VmSpec; +import com.google.caliper.platform.Platform; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.MoreObjects; +import com.google.common.base.Objects; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; + +import java.io.File; + +import javax.annotation.concurrent.GuardedBy; + +/** + * This is the configuration passed to the VM by the user. This differs from the {@link VmSpec} + * in that any number of configurations can yield the same spec (due to default flag values) and any + * number of specs can come from a single configuration (due to + * <a href="http://www.oracle.com/technetwork/java/ergo5-140223.html">ergonomics</a>). + * + * @author gak@google.com (Gregory Kick) + */ +public final class VmConfig { + private final Platform platform; + private final File vmHome; + private final ImmutableList<String> options; + + @GuardedBy("this") + private File vmExecutable; + + private VmConfig(Builder builder) { + this.platform = builder.platform; + this.vmHome = builder.vmHome; + this.options = builder.optionsBuilder.build(); + } + + @VisibleForTesting + public VmConfig(File vmHome, Iterable<String> options, File vmExecutable, Platform platform) { + this.platform = platform; + this.vmHome = checkNotNull(vmHome); + this.vmExecutable = checkNotNull(vmExecutable); + this.options = ImmutableList.copyOf(options); + } + + public File vmHome() { + return vmHome; + } + + public synchronized File vmExecutable() { + if (vmExecutable == null) { + vmExecutable = platform.vmExecutable(vmHome); + } + return vmExecutable; + } + + public ImmutableList<String> options() { + return options; + } + + public String platformName() { + return platform.name(); + } + + public String workerClassPath() { + return platform.workerClassPath(); + } + + public ImmutableSet<String> workerProcessArgs() { + return platform.workerProcessArgs(); + } + + public ImmutableSet<String> commonInstrumentVmArgs() { + return platform.commonInstrumentVmArgs(); + } + + @Override public boolean equals(Object obj) { + if (obj == this) { + return true; + } else if (obj instanceof VmConfig) { + VmConfig that = (VmConfig) obj; + return this.platform.equals(that.platform) + && this.vmHome.equals(that.vmHome) + && this.options.equals(that.options); + } else { + return false; + } + } + + @Override public int hashCode() { + return Objects.hashCode(platform, vmHome, options); + } + + @Override public String toString() { + return MoreObjects.toStringHelper(this) + .add("platform", platform) + .add("vmHome", vmHome) + .add("options", options) + .toString(); + } + + @VisibleForTesting public static final class Builder { + private final Platform platform; + private final File vmHome; + private final ImmutableList.Builder<String> optionsBuilder = ImmutableList.builder(); + + + public Builder(Platform platform, File vmHome) { + this.platform = checkNotNull(platform); + this.vmHome = checkNotNull(vmHome); + } + + public Builder addOption(String option) { + optionsBuilder.add(option); + return this; + } + + public Builder addAllOptions(Iterable<String> options) { + optionsBuilder.addAll(options); + return this; + } + + public VmConfig build() { + return new VmConfig(this); + } + } +} diff --git a/caliper/src/main/java/com/google/caliper/json/AnnotationExclusionStrategy.java b/caliper/src/main/java/com/google/caliper/json/AnnotationExclusionStrategy.java new file mode 100644 index 0000000..82937ba --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/json/AnnotationExclusionStrategy.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2012 Google Inc. + * + * 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.google.caliper.json; + +import com.google.caliper.model.ExcludeFromJson; +import com.google.gson.ExclusionStrategy; +import com.google.gson.FieldAttributes; + +/** + * An exclusion strategy that excludes elements annotated with {@link ExcludeFromJson}. + */ +final class AnnotationExclusionStrategy implements ExclusionStrategy { + @Override public boolean shouldSkipField(FieldAttributes f) { + return f.getAnnotation(ExcludeFromJson.class) != null; + } + + @Override public boolean shouldSkipClass(Class<?> clazz) { + return clazz.getAnnotation(ExcludeFromJson.class) != null; + } +} diff --git a/caliper/src/main/java/com/google/caliper/json/GsonModule.java b/caliper/src/main/java/com/google/caliper/json/GsonModule.java new file mode 100644 index 0000000..491dcca --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/json/GsonModule.java @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2012 Google Inc. + * + * 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.google.caliper.json; + +import com.google.gson.ExclusionStrategy; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.TypeAdapterFactory; +import com.google.gson.internal.bind.TypeAdapters; +import dagger.Module; +import dagger.Provides; +import dagger.Provides.Type; +import org.joda.time.Instant; +import java.util.Set; + +/** + * Binds a {@link Gson} instance suitable for serializing and deserializing Caliper + * {@linkplain com.google.caliper.model model} objects. + */ +@Module +public final class GsonModule { + + @Provides(type = Type.SET) + static TypeAdapterFactory provideImmutableListTypeAdapterFactory() { + return new ImmutableListTypeAdatperFactory(); + } + + @Provides(type = Type.SET) + static TypeAdapterFactory provideImmutableMapTypeAdapterFactory() { + return new ImmutableMapTypeAdapterFactory(); + } + + @Provides(type = Type.SET) + static TypeAdapterFactory provideNaturallySortedMapTypeAdapterFactory() { + return new NaturallySortedMapTypeAdapterFactory(); + } + + @Provides(type = Type.SET) + static TypeAdapterFactory provideImmutableMultimapTypeAdapterFactory() { + return new ImmutableMultimapTypeAdapterFactory(); + } + + @Provides + static ExclusionStrategy provideAnnotationExclusionStrategy() { + return new AnnotationExclusionStrategy(); + } + + @Provides(type = Type.SET) + static TypeAdapterFactory provideTypeAdapterFactoryForInstant( + InstantTypeAdapter typeAdapter) { + return TypeAdapters.newFactory(Instant.class, typeAdapter); + } + + @Provides static InstantTypeAdapter provideInstantTypeAdapter() { + return new InstantTypeAdapter(); + } + + @Provides static Gson provideGson(Set<TypeAdapterFactory> typeAdapterFactories, + ExclusionStrategy exclusionStrategy) { + GsonBuilder gsonBuilder = new GsonBuilder().setExclusionStrategies(exclusionStrategy); + for (TypeAdapterFactory typeAdapterFactory : typeAdapterFactories) { + gsonBuilder.registerTypeAdapterFactory(typeAdapterFactory); + } + return gsonBuilder.create(); + } +} diff --git a/caliper/src/main/java/com/google/caliper/json/ImmutableListTypeAdatperFactory.java b/caliper/src/main/java/com/google/caliper/json/ImmutableListTypeAdatperFactory.java new file mode 100644 index 0000000..5a7c925 --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/json/ImmutableListTypeAdatperFactory.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2012 Google Inc. + * + * 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.google.caliper.json; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Lists; +import com.google.gson.Gson; +import com.google.gson.TypeAdapter; +import com.google.gson.TypeAdapterFactory; +import com.google.gson.reflect.TypeToken; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; + +import java.io.IOException; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.List; + +/** + * Serializes and deserializes {@link ImmutableList} instances using an {@link ArrayList} as an + * intermediary. + */ +final class ImmutableListTypeAdatperFactory implements TypeAdapterFactory { + @SuppressWarnings("unchecked") + @Override public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) { + Type type = typeToken.getType(); + if (typeToken.getRawType() != ImmutableList.class + || !(type instanceof ParameterizedType)) { + return null; + } + + com.google.common.reflect.TypeToken<ImmutableList<?>> betterToken = + (com.google.common.reflect.TypeToken<ImmutableList<?>>) + com.google.common.reflect.TypeToken.of(typeToken.getType()); + final TypeAdapter<ArrayList<?>> arrayListAdapter = + (TypeAdapter<ArrayList<?>>) gson.getAdapter( + TypeToken.get(betterToken.getSupertype(List.class).getSubtype(ArrayList.class) + .getType())); + return new TypeAdapter<T>() { + @Override public void write(JsonWriter out, T value) throws IOException { + ArrayList<?> arrayList = Lists.newArrayList((List<?>) value); + arrayListAdapter.write(out, arrayList); + } + + @Override public T read(JsonReader in) throws IOException { + ArrayList<?> arrayList = arrayListAdapter.read(in); + return (T) ImmutableList.copyOf(arrayList); + } + }; + } +} diff --git a/caliper/src/main/java/com/google/caliper/json/ImmutableMapTypeAdapterFactory.java b/caliper/src/main/java/com/google/caliper/json/ImmutableMapTypeAdapterFactory.java new file mode 100644 index 0000000..fec1739 --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/json/ImmutableMapTypeAdapterFactory.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2012 Google Inc. + * + * 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.google.caliper.json; + +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Maps; +import com.google.gson.Gson; +import com.google.gson.TypeAdapter; +import com.google.gson.TypeAdapterFactory; +import com.google.gson.reflect.TypeToken; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; + +import java.io.IOException; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.HashMap; +import java.util.Map; + +/** + * Serializes and deserializes {@link ImmutableMap} instances using a {@link HashMap} as an + * intermediary. + */ +final class ImmutableMapTypeAdapterFactory implements TypeAdapterFactory { + @SuppressWarnings("unchecked") + @Override public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) { + Type type = typeToken.getType(); + if (typeToken.getRawType() != ImmutableMap.class + || !(type instanceof ParameterizedType)) { + return null; + } + + com.google.common.reflect.TypeToken<ImmutableMap<?, ?>> betterToken = + (com.google.common.reflect.TypeToken<ImmutableMap<?, ?>>) + com.google.common.reflect.TypeToken.of(typeToken.getType()); + final TypeAdapter<HashMap<?, ?>> hashMapAdapter = + (TypeAdapter<HashMap<?, ?>>) gson.getAdapter( + TypeToken.get(betterToken.getSupertype(Map.class).getSubtype(HashMap.class) + .getType())); + return new TypeAdapter<T>() { + @Override public void write(JsonWriter out, T value) throws IOException { + HashMap<?, ?> hashMap = Maps.newHashMap((Map<?, ?>) value); + hashMapAdapter.write(out, hashMap); + } + + @Override public T read(JsonReader in) throws IOException { + HashMap<?, ?> hashMap = hashMapAdapter.read(in); + return (T) ImmutableMap.copyOf(hashMap); + } + }; + } +} diff --git a/caliper/src/main/java/com/google/caliper/json/ImmutableMultimapTypeAdapterFactory.java b/caliper/src/main/java/com/google/caliper/json/ImmutableMultimapTypeAdapterFactory.java new file mode 100644 index 0000000..9781388 --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/json/ImmutableMultimapTypeAdapterFactory.java @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2012 Google Inc. + * + * 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.google.caliper.json; + +import com.google.common.collect.ImmutableListMultimap; +import com.google.common.collect.ImmutableMultimap; +import com.google.common.collect.ImmutableSetMultimap; +import com.google.common.collect.ListMultimap; +import com.google.common.collect.SetMultimap; +import com.google.common.reflect.TypeParameter; +import com.google.common.reflect.TypeToken; +import com.google.gson.Gson; +import com.google.gson.TypeAdapter; +import com.google.gson.TypeAdapterFactory; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; + +import java.io.IOException; +import java.lang.reflect.ParameterizedType; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +/** + * Serializes and deserializes {@link ImmutableMultimap} instances using maps of collections as + * intermediaries. + */ +final class ImmutableMultimapTypeAdapterFactory implements TypeAdapterFactory { + private static <K, V> TypeToken<Map<K, List<V>>> getMapOfListsToken( + TypeToken<ListMultimap<K, V>> from) { + ParameterizedType rawType = (ParameterizedType) from.getSupertype(ListMultimap.class).getType(); + @SuppressWarnings("unchecked") // key type is K + TypeToken<K> keyType = (TypeToken<K>) TypeToken.of(rawType.getActualTypeArguments()[0]); + @SuppressWarnings("unchecked") // value type is V + TypeToken<V> valueType = (TypeToken<V>) TypeToken.of(rawType.getActualTypeArguments()[1]); + return new TypeToken<Map<K, List<V>>>() {} + .where(new TypeParameter<K>() {}, keyType) + .where(new TypeParameter<V>() {}, valueType); + } + + private static <K, V> TypeToken<Map<K, Set<V>>> getMapOfSetsToken( + TypeToken<SetMultimap<K, V>> from) { + ParameterizedType rawType = (ParameterizedType) from.getSupertype(SetMultimap.class).getType(); + @SuppressWarnings("unchecked") // key type is K + TypeToken<K> keyType = (TypeToken<K>) TypeToken.of(rawType.getActualTypeArguments()[0]); + @SuppressWarnings("unchecked") // value type is V + TypeToken<V> valueType = (TypeToken<V>) TypeToken.of(rawType.getActualTypeArguments()[1]); + return new TypeToken<Map<K, Set<V>>>() {} + .where(new TypeParameter<K>() {}, keyType) + .where(new TypeParameter<V>() {}, valueType); + } + + @Override + @SuppressWarnings({"unchecked", "rawtypes"}) + public <T> TypeAdapter<T> create(Gson gson, com.google.gson.reflect.TypeToken<T> typeToken) { + if (ImmutableListMultimap.class.isAssignableFrom(typeToken.getRawType())) { + TypeToken<Map<?, List<?>>> mapToken = + getMapOfListsToken((TypeToken) TypeToken.of(typeToken.getType())); + final TypeAdapter<Map<?, List<?>>> adapter = + (TypeAdapter<Map<?, List<?>>>) gson.getAdapter( + com.google.gson.reflect.TypeToken.get(mapToken.getType())); + return new TypeAdapter<T>() { + @Override public void write(JsonWriter out, T value) throws IOException { + ImmutableListMultimap<?, ?> multimap = (ImmutableListMultimap<?, ?>) value; + adapter.write(out, (Map) multimap.asMap()); + } + + @Override public T read(JsonReader in) throws IOException { + Map<?, List<?>> value = adapter.read(in); + ImmutableListMultimap.Builder builder = ImmutableListMultimap.builder(); + for (Entry<?, List<?>> entry : value.entrySet()) { + builder.putAll(entry.getKey(), entry.getValue()); + } + return (T) builder.build(); + } + }; + } else if (ImmutableSetMultimap.class.isAssignableFrom(typeToken.getRawType())) { + TypeToken<Map<?, Set<?>>> mapToken = + getMapOfSetsToken((TypeToken) TypeToken.of(typeToken.getType())); + final TypeAdapter<Map<?, Set<?>>> adapter = + (TypeAdapter<Map<?, Set<?>>>) gson.getAdapter( + com.google.gson.reflect.TypeToken.get(mapToken.getType())); + return new TypeAdapter<T>() { + @Override public void write(JsonWriter out, T value) throws IOException { + ImmutableSetMultimap<?, ?> multimap = (ImmutableSetMultimap<?, ?>) value; + adapter.write(out, (Map) multimap.asMap()); + } + + @Override public T read(JsonReader in) throws IOException { + Map<?, Set<?>> value = adapter.read(in); + ImmutableSetMultimap.Builder builder = ImmutableSetMultimap.builder(); + for (Entry<?, Set<?>> entry : value.entrySet()) { + builder.putAll(entry.getKey(), entry.getValue()); + } + return (T) builder.build(); + } + }; + } else { + return null; + } + } +} diff --git a/caliper/src/main/java/com/google/caliper/json/InstantTypeAdapter.java b/caliper/src/main/java/com/google/caliper/json/InstantTypeAdapter.java new file mode 100644 index 0000000..ae500f7 --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/json/InstantTypeAdapter.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2012 Google Inc. + * + * 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.google.caliper.json; + +import com.google.gson.TypeAdapter; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; + +import org.joda.time.Instant; +import org.joda.time.format.ISODateTimeFormat; + +import java.io.IOException; + +/** + * Serializes and deserializes {@link Instant} instances. + */ +final class InstantTypeAdapter extends TypeAdapter<Instant> { + @Override public void write(JsonWriter out, Instant value) throws IOException { + out.value(ISODateTimeFormat.dateTime().print(value)); + } + + @Override public Instant read(JsonReader in) throws IOException { + return ISODateTimeFormat.dateTime().parseDateTime(in.nextString()).toInstant(); + } +} diff --git a/caliper/src/main/java/com/google/caliper/json/NaturallySortedMapTypeAdapterFactory.java b/caliper/src/main/java/com/google/caliper/json/NaturallySortedMapTypeAdapterFactory.java new file mode 100644 index 0000000..92fdb19 --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/json/NaturallySortedMapTypeAdapterFactory.java @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2012 Google Inc. + * + * 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.google.caliper.json; + +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Maps; +import com.google.gson.Gson; +import com.google.gson.TypeAdapter; +import com.google.gson.TypeAdapterFactory; +import com.google.gson.reflect.TypeToken; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; + +import java.io.IOException; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.Map; +import java.util.SortedMap; +import java.util.TreeMap; + +/** + * Serializes and deserializes {@link SortedMap} instances using a {@link TreeMap} with natural + * ordering as an intermediary. + */ +final class NaturallySortedMapTypeAdapterFactory implements TypeAdapterFactory { + @SuppressWarnings("rawtypes") + private static final ImmutableSet<Class<? extends SortedMap>> CLASSES = + ImmutableSet.of(SortedMap.class, TreeMap.class); + + @SuppressWarnings("unchecked") + @Override public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) { + Type type = typeToken.getType(); + if (!CLASSES.contains(typeToken.getRawType()) + || !(type instanceof ParameterizedType)) { + return null; + } + + com.google.common.reflect.TypeToken<SortedMap<?, ?>> betterToken = + (com.google.common.reflect.TypeToken<SortedMap<?, ?>>) + com.google.common.reflect.TypeToken.of(typeToken.getType()); + final TypeAdapter<Map<?, ?>> mapAdapter = + (TypeAdapter<Map<?, ?>>) gson.getAdapter( + TypeToken.get(betterToken.getSupertype(Map.class).getType())); + return new TypeAdapter<T>() { + @Override public void write(JsonWriter out, T value) throws IOException { + TreeMap<?, ?> treeMap = Maps.newTreeMap((SortedMap<?, ?>) value); + mapAdapter.write(out, treeMap); + } + + @SuppressWarnings("rawtypes") + @Override public T read(JsonReader in) throws IOException { + TreeMap treeMap = Maps.newTreeMap(); + treeMap.putAll(mapAdapter.read(in)); + return (T) treeMap; + } + }; + } +} diff --git a/caliper/src/main/java/com/google/caliper/memory/Chain.java b/caliper/src/main/java/com/google/caliper/memory/Chain.java new file mode 100644 index 0000000..115141e --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/memory/Chain.java @@ -0,0 +1,204 @@ +/* + * Copyright (C) 2012 Google Inc. + * + * 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.google.caliper.memory; + +import com.google.common.base.Preconditions; + +import java.lang.reflect.Field; +import java.util.ArrayDeque; +import java.util.Deque; +import java.util.Iterator; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +/** + * A chain of references, which starts at a root object and leads to a + * particular value (either an object or a primitive). + */ +public abstract class Chain { + private final Object value; + private final Chain parent; + + Chain(Chain parent, Object value) { + this.parent = parent; + this.value = value; + } + + static Chain root(Object value) { + return new Chain(null, Preconditions.checkNotNull(value)) { + @Override + public Class<?> getValueType() { + return getValue().getClass(); + } + }; + } + + FieldChain appendField(Field field, Object value) { + return new FieldChain(this, Preconditions.checkNotNull(field), value); + } + + ArrayIndexChain appendArrayIndex(int arrayIndex, Object value) { + return new ArrayIndexChain(this, arrayIndex, value); + } + + /** + * Returns whether this chain has a parent. This returns false only when + * this chain represents the root object itself. + */ + public boolean hasParent() { + return parent != null; + } + + /** + * Returns the parent chain, from which this chain was created. + * @throws IllegalStateException if {@code !hasParent()}, then an + */ + public @Nonnull Chain getParent() { + Preconditions.checkState(parent != null, "This is the root value, it has no parent"); + return parent; + } + + /** + * Returns the value that this chain leads to. If the value is a primitive, + * a wrapper object is returned instead. + */ + public @Nullable Object getValue() { + return value; + } + + public abstract @Nonnull Class<?> getValueType(); + + /** + * Returns whether the connection of the parent chain and this chain is + * through a field (of the getParent().getValue().getClass() class). + */ + public boolean isThroughField() { + return false; + } + + /** + * Returns whether the connection of the parent chain and this chain is + * through an array index, i.e. the parent leads to an array, and this + * chain leads to an element of that array. + */ + public boolean isThroughArrayIndex() { + return false; + } + + /** + * Returns whether the value of this chain represents a primitive. + */ + public boolean isPrimitive() { + return getValueType().isPrimitive(); + } + + /** + * Returns the root object of this chain. + */ + public @Nonnull Object getRoot() { + Chain current = this; + while (current.hasParent()) { + current = current.getParent(); + } + return current.getValue(); + } + + Deque<Chain> reverse() { + Deque<Chain> reverseChain = new ArrayDeque<Chain>(8); + Chain current = this; + reverseChain.addFirst(current); + while (current.hasParent()) { + current = current.getParent(); + reverseChain.addFirst(current); + } + return reverseChain; + } + + @Override public final String toString() { + StringBuilder sb = new StringBuilder(32); + + Iterator<Chain> it = reverse().iterator(); + sb.append(it.next().getValue()); + while (it.hasNext()) { + sb.append("->"); + Chain current = it.next(); + if (current.isThroughField()) { + sb.append(((FieldChain)current).getField().getName()); + } else if (current.isThroughArrayIndex()) { + sb.append("[").append(((ArrayIndexChain)current).getArrayIndex()).append("]"); + } + } + return sb.toString(); + } + + static class FieldChain extends Chain { + private final Field field; + + FieldChain(Chain parent, Field referringField, Object value) { + super(parent, value); + this.field = referringField; + } + + @Override + public boolean isThroughField() { + return true; + } + + @Override + public boolean isThroughArrayIndex() { + return false; + } + + @Override + public Class<?> getValueType() { + return field.getType(); + } + + public Field getField() { + return field; + } + } + + static class ArrayIndexChain extends Chain { + private final int index; + + ArrayIndexChain(Chain parent, int index, Object value) { + super(parent, value); + this.index = index; + } + + @Override + public boolean isThroughField() { + return false; + } + + @Override + public boolean isThroughArrayIndex() { + return true; + } + + @Override + public Class<?> getValueType() { + return getParent().getValue().getClass().getComponentType(); + } + + public int getArrayIndex() { + return index; + } + } +} diff --git a/caliper/src/main/java/com/google/caliper/memory/ObjectExplorer.java b/caliper/src/main/java/com/google/caliper/memory/ObjectExplorer.java new file mode 100644 index 0000000..2c4e4cc --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/memory/ObjectExplorer.java @@ -0,0 +1,260 @@ +/* + * Copyright (C) 2012 Google Inc. + * + * 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.google.caliper.memory; + +import com.google.common.base.Function; +import com.google.common.base.Predicate; +import com.google.common.collect.Lists; + +import java.lang.reflect.AccessibleObject; +import java.lang.reflect.Array; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.ArrayDeque; +import java.util.Collections; +import java.util.Deque; +import java.util.EnumSet; +import java.util.IdentityHashMap; +import java.util.List; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +import javax.annotation.Nonnull; + +/** + * A depth-first object graph explorer. The traversal starts at a root (an + * {@code Object}) and explores any other reachable object (recursively) or + * primitive value, excluding static fields from the traversal. The traversal + * is controlled by a user-supplied {@link ObjectVisitor}, which decides for + * each explored path whether to continue exploration of that path, and it can + * also return a value at the end of the traversal. + */ +public final class ObjectExplorer { + private ObjectExplorer() { } + + /** + * Explores an object graph (defined by a root object and whatever is + * reachable through it, following non-static fields) while using an + * {@link ObjectVisitor} to both control the traversal and return a value. + * + * <p>Equivalent to {@code exploreObject(rootObject, visitor, + * EnumSet.noneOf(Feature.class))}. + * + * @param <T> the type of the value obtained (after the traversal) by the + * ObjectVisitor + * @param rootObject an object to be recursively explored + * @param visitor a visitor that is notified for each explored path and + * decides whether to continue exploration of that path, and constructs a + * return value at the end of the exploration + * @return whatever value is returned by the visitor at the end of the + * traversal + * @see ObjectVisitor + */ + public static <T> T exploreObject(Object rootObject, ObjectVisitor<T> visitor) { + return exploreObject(rootObject, visitor, EnumSet.noneOf(Feature.class)); + } + + /** + * Explores an object graph (defined by a root object and whatever is + * reachable through it, following non-static fields) while using an + * {@link ObjectVisitor} to both control the traversal and return a value. + * + * <p>The {@code features} further customizes the exploration behavior. + * In particular: + * <ul> + * <li>If {@link Feature#VISIT_PRIMITIVES} is contained in features, + * the visitor will also be notified about exploration of primitive values. + * <li>If {@link Feature#VISIT_NULL} is contained in features, the visitor + * will also be notified about exploration of {@code null} values. + * </ul> + * In both cases above, the return value of + * {@link ObjectVisitor#visit(Chain)} is ignored, since neither primitive + * values or {@code null} can be further explored. + * + * @param <T> the type of the value obtained (after the traversal) by the + * ObjectVisitor + * @param rootObject an object to be recursively explored + * @param visitor a visitor that is notified for each explored path + * and decides whether to continue exploration of that path, and constructs + * a return value at the end of the exploration + * @param features a set of desired features that the object exploration should have + * @return whatever value is returned by the visitor at the end of the traversal + * @see ObjectVisitor + */ + public static <T> T exploreObject(Object rootObject, + ObjectVisitor<T> visitor, EnumSet<Feature> features) { + Deque<Chain> stack = new ArrayDeque<Chain>(32); + if (rootObject != null) stack.push(Chain.root(rootObject)); + + while (!stack.isEmpty()) { + Chain chain = stack.pop(); + //the only place where the return value of visit() is considered + ObjectVisitor.Traversal traversal = visitor.visit(chain); + switch (traversal) { + case SKIP: continue; + case EXPLORE: break; + default: throw new AssertionError(); + } + + //only nonnull values pushed in the stack + @Nonnull Object value = chain.getValue(); + Class<?> valueClass = value.getClass(); + if (valueClass.isArray()) { + boolean isPrimitive = valueClass.getComponentType().isPrimitive(); + /* + * Since we push paths to explore in a stack, we push references found in the array in + * reverse order, so when we pop them, they will be in the array's order. + */ + for (int i = Array.getLength(value) - 1; i >= 0; i--) { + Object childValue = Array.get(value, i); + if (isPrimitive) { + if (features.contains(Feature.VISIT_PRIMITIVES)) { + visitor.visit(chain.appendArrayIndex(i, childValue)); + } + } else if (childValue == null) { + if (features.contains(Feature.VISIT_NULL)) { + visitor.visit(chain.appendArrayIndex(i, childValue)); + } + } else { + stack.push(chain.appendArrayIndex(i, childValue)); + } + } + } else { + /* + * Reflection usually provides fields in declaration order. As above in arrays, we push + * them to the stack in reverse order, so when we pop them, we get them in the original + * (declaration) order. + */ + final Field[] fields = getAllFields(value); + for (int j = fields.length - 1; j >= 0; j--) { + final Field field = fields[j]; + Object childValue = null; + try { + childValue = field.get(value); + } catch (Exception e) { + throw new AssertionError(e); + } + if (childValue == null) { // handling nulls + if (features.contains(Feature.VISIT_NULL)) { + visitor.visit(chain.appendField(field, childValue)); + } + } else { // handling primitives or references + boolean isPrimitive = field.getType().isPrimitive(); + Chain extendedChain = chain.appendField(field, childValue); + if (isPrimitive) { + if (features.contains(Feature.VISIT_PRIMITIVES)) { + visitor.visit(extendedChain); + } + } else { + stack.push(extendedChain); + } + } + } + } + } + return visitor.result(); + } + + /** + * A stateful predicate that allows exploring an object (the tail of the chain) only once. + */ + static class AtMostOncePredicate implements Predicate<Chain> { + private final Set<Object> seen = Collections.newSetFromMap( + new IdentityHashMap<Object, Boolean>()); + + @Override public boolean apply(Chain chain) { + return seen.add(chain.getValue()); + } + } + + static final Predicate<Chain> notEnumFieldsOrClasses = new Predicate<Chain>() { + @Override public boolean apply(Chain chain) { + return !(Enum.class.isAssignableFrom(chain.getValueType()) + || chain.getValue() instanceof Class<?>); + } + }; + + static final Function<Chain, Object> chainToObject = + new Function<Chain, Object>() { + @Override public Object apply(Chain chain) { + return chain.getValue(); + } + }; + + /** + * A cache of {@code Field}s that are accessible for a given {@code Class<?>}. + */ + private static final ConcurrentHashMap<Class<?>, Field[]> clazzFields = + new ConcurrentHashMap<Class<?>, Field[]>(); + + private static Field[] getAllFields(Object o) { + Class<?> clazz = o.getClass(); + return getAllFields(clazz); + } + + /** + * Keep a cache of fields per class of interest. + * + * @param clazz - the {@code Class<?>} to interrogate. + * @return An array of fields of the given class. + */ + private static Field[] getAllFields(Class<?> clazz) { + Field[] f = clazzFields.get(clazz); + if (f == null) { + f = computeAllFields(clazz); + Field[] u = clazzFields.putIfAbsent(clazz, f); + return u == null ? f : u; + } + return f; + } + + private static Field[] computeAllFields(Class<?> clazz) { + List<Field> fields = Lists.newArrayListWithCapacity(8); + while (clazz != null) { + for (Field field : clazz.getDeclaredFields()) { + // add only non-static fields + if (!Modifier.isStatic(field.getModifiers())) { + fields.add(field); + } + } + clazz = clazz.getSuperclass(); + } + + // all together so there is only one security check + Field[] afields = fields.toArray(new Field[fields.size()]); + AccessibleObject.setAccessible(afields, true); + return afields; + } + + /** + * Enumeration of features that may be optionally requested for an object + * traversal. + * + * @see ObjectExplorer#exploreObject(Object, ObjectVisitor, EnumSet) + */ + public enum Feature { + /** + * Null references should be visited. + */ + VISIT_NULL, + + /** + * Primitive values should be visited. + */ + VISIT_PRIMITIVES + } +} diff --git a/caliper/src/main/java/com/google/caliper/memory/ObjectGraphMeasurer.java b/caliper/src/main/java/com/google/caliper/memory/ObjectGraphMeasurer.java new file mode 100644 index 0000000..3cacaeb --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/memory/ObjectGraphMeasurer.java @@ -0,0 +1,223 @@ +/* + * Copyright (C) 2012 Google Inc. + * + * 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.google.caliper.memory; + +import com.google.caliper.memory.ObjectExplorer.Feature; +import com.google.common.base.MoreObjects; +import com.google.common.base.Objects; +import com.google.common.base.Preconditions; +import com.google.common.base.Predicate; +import com.google.common.base.Predicates; +import com.google.common.collect.HashMultiset; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMultiset; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Multiset; + +import java.util.EnumSet; + +/** + * A tool that can qualitatively measure the footprint + * ({@literal e.g.}, number of objects, references, + * primitives) of a graph structure. + */ +public final class ObjectGraphMeasurer { + /** + * The footprint of an object graph. + */ + public final static class Footprint { + private final int objects; + private final int nonNullRefs; + private final int nullRefs; + private final ImmutableMultiset<Class<?>> primitives; + + private static final ImmutableSet<Class<?>> primitiveTypes = ImmutableSet.<Class<?>>of( + boolean.class, byte.class, char.class, short.class, + int.class, float.class, long.class, double.class); + + /** + * Constructs a Footprint, by specifying the number of objects, + * references, and primitives (represented as a {@link Multiset}). + * + * @param objects the number of objects + * @param nonNullRefs the number of non-null references + * @param nullRefs the number of null references + * @param primitives the number of primitives (represented by the + * respective primitive classes, e.g. {@code int.class} etc) + */ + public Footprint(int objects, int nonNullRefs, int nullRefs, + Multiset<Class<?>> primitives) { + Preconditions.checkArgument(objects >= 0, "Negative number of objects"); + Preconditions.checkArgument(nonNullRefs >= 0, "Negative number of references"); + Preconditions.checkArgument(nullRefs >= 0, "Negative number of references"); + Preconditions.checkArgument(primitiveTypes.containsAll(primitives.elementSet()), + "Unexpected primitive type"); + this.objects = objects; + this.nonNullRefs = nonNullRefs; + this.nullRefs = nullRefs; + this.primitives = ImmutableMultiset.copyOf(primitives); + } + + /** + * Returns the number of objects of this footprint. + */ + public int getObjects() { + return objects; + } + + /** + * Returns the number of non-null references of this footprint. + */ + public int getNonNullReferences() { + return nonNullRefs; + } + + /** + * Returns the number of null references of this footprint. + */ + public int getNullReferences() { + return nullRefs; + } + + /** + * Returns the number of all references (null and non-null) of this footprint. + */ + public int getAllReferences() { + return nonNullRefs + nullRefs; + } + + /** + * Returns the number of primitives of this footprint + * (represented by the respective primitive classes, + * {@literal e.g.} {@code int.class} etc). + */ + public ImmutableMultiset<Class<?>> getPrimitives() { + return primitives; + } + + @Override + public int hashCode() { + return Objects.hashCode(getClass().getName(), + objects, nonNullRefs, nullRefs, primitives); + } + + @Override + public boolean equals(Object o) { + if (o instanceof Footprint) { + Footprint that = (Footprint) o; + return this.objects == that.objects + && this.nonNullRefs == that.nonNullRefs + && this.nullRefs == that.nullRefs + && this.primitives.equals(that.primitives); + } + return false; + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("Objects", objects) + .add("NonNullRefs", nonNullRefs) + .add("NullRefs", nullRefs) + .add("Primitives", primitives) + .toString(); + } + } + + /** + * Measures the footprint of the specified object graph. + * The object graph is defined by a root object and whatever object can be + * reached through that, excluding static fields, {@code Class} objects, + * and fields defined in {@code enum}s (all these are considered shared + * values, which should not contribute to the cost of any single object + * graph). + * + * <p>Equivalent to {@code measure(rootObject, Predicates.alwaysTrue())}. + * + * @param rootObject the root object of the object graph + * @return the footprint of the object graph + */ + public static Footprint measure(Object rootObject) { + return measure(rootObject, Predicates.alwaysTrue()); + } + + /** + * Measures the footprint of the specified object graph. + * The object graph is defined by a root object and whatever object can be + * reached through that, excluding static fields, {@code Class} objects, + * and fields defined in {@code enum}s (all these are considered shared + * values, which should not contribute to the cost of any single object + * graph), and any object for which the user-provided predicate returns + * {@code false}. + * + * @param rootObject the root object of the object graph + * @param objectAcceptor a predicate that returns {@code true} for objects + * to be explored (and treated as part of the footprint), or {@code false} + * to forbid the traversal to traverse the given object + * @return the footprint of the object graph + */ + public static Footprint measure(Object rootObject, Predicate<Object> objectAcceptor) { + Preconditions.checkNotNull(objectAcceptor, "predicate"); + + Predicate<Chain> completePredicate = Predicates.and(ImmutableList.of( + ObjectExplorer.notEnumFieldsOrClasses, + new ObjectExplorer.AtMostOncePredicate(), + Predicates.compose(objectAcceptor, ObjectExplorer.chainToObject) + )); + + return ObjectExplorer.exploreObject(rootObject, new ObjectGraphVisitor(completePredicate), + EnumSet.of(Feature.VISIT_PRIMITIVES, Feature.VISIT_NULL)); + } + + private static class ObjectGraphVisitor implements ObjectVisitor<Footprint> { + private int objects; + // -1 to account for the root, which has no reference leading to it + private int nonNullReferences = -1; + private int nullReferences = 0; + private final Multiset<Class<?>> primitives = HashMultiset.create(); + private final Predicate<Chain> predicate; + + ObjectGraphVisitor(Predicate<Chain> predicate) { + this.predicate = predicate; + } + + @Override public Traversal visit(Chain chain) { + if (chain.isPrimitive()) { + primitives.add(chain.getValueType()); + return Traversal.SKIP; + } else { + if (chain.getValue() == null) { + nullReferences++; + } else { + nonNullReferences++; + } + } + if (predicate.apply(chain) && chain.getValue() != null) { + objects++; + return Traversal.EXPLORE; + } + return Traversal.SKIP; + } + + @Override public Footprint result() { + return new Footprint(objects, nonNullReferences, nullReferences, + ImmutableMultiset.copyOf(primitives)); + } + } + + private ObjectGraphMeasurer() {} +} diff --git a/caliper/src/main/java/com/google/caliper/memory/ObjectVisitor.java b/caliper/src/main/java/com/google/caliper/memory/ObjectVisitor.java new file mode 100644 index 0000000..2a1320e --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/memory/ObjectVisitor.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2012 Google Inc. + * + * 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.google.caliper.memory; + +/** + * A visitor that controls an object traversal. Implementations + * of this interface are passed to {@link ObjectExplorer} exploration methods. + * + * @param <T> the type of the result that this visitor returns + * (can be defined as {@code Void} to denote no result}. + * + * @see ObjectExplorer + */ +public interface ObjectVisitor<T> { + /** + * Visits an explored value (the whole chain from the root object + * leading to the value is provided), and decides whether to continue + * the exploration of that value. + * + * <p>In case the explored value is either primitive or {@code null} + * (e.g., if {@code chain.isPrimitive() || chain.getValue() == null}), + * the return value is meaningless and is ignored. + * + * @param chain the chain that leads to the explored value. + * @return {@link Traversal#EXPLORE} to denote that the visited object + * should be further explored, or {@link Traversal#SKIP} to avoid + * exploring it. + */ + Traversal visit(Chain chain); + + /** + * Returns an arbitrary value (presumably constructed during the object + * graph traversal). + */ + T result(); + + /** + * Constants that denote how the traversal of a given object (chain) + * should continue. + */ + enum Traversal { + /** + * The visited object should be further explored. + */ + EXPLORE, + + /** + * The visited object should not be explored. + */ + SKIP + } +} diff --git a/caliper/src/main/java/com/google/caliper/model/AllocationMeasurement.java b/caliper/src/main/java/com/google/caliper/model/AllocationMeasurement.java new file mode 100644 index 0000000..5ceed02 --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/model/AllocationMeasurement.java @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2011 Google Inc. + * + * 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.google.caliper.model; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Annotation that identifies a given method as an "allocation measurement" method. The method + * should take a single int parameter; its return value may be any type. Methods with this + * annotation may be used with the {@link com.google.caliper.runner.AllocationInstrument}. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface AllocationMeasurement {} diff --git a/caliper/src/main/java/com/google/caliper/model/ArbitraryMeasurement.java b/caliper/src/main/java/com/google/caliper/model/ArbitraryMeasurement.java new file mode 100644 index 0000000..bcfdd49 --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/model/ArbitraryMeasurement.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2011 Google Inc. + * + * 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.google.caliper.model; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Annotation that identifies a given method as an "arbitrary measurement" method. The method should + * take no parameters and return a double, which is the measured value. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface ArbitraryMeasurement { + + /** + * The units for the value returned by this measurement method. + */ + String units() default ""; + + /** + * Text description of the quantity measured by this measurement method. + */ + String description() default ""; +} diff --git a/caliper/src/main/java/com/google/caliper/model/BenchmarkSpec.java b/caliper/src/main/java/com/google/caliper/model/BenchmarkSpec.java new file mode 100644 index 0000000..c2fff4c --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/model/BenchmarkSpec.java @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2012 Google Inc. + * + * 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.google.caliper.model; + +import static com.google.caliper.model.PersistentHashing.getPersistentHashFunction; +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.checkState; + +import com.google.common.base.MoreObjects; +import com.google.common.collect.ImmutableSortedMap; +import com.google.common.collect.Maps; +import com.google.common.hash.Funnel; +import com.google.common.hash.PrimitiveSink; + +import java.io.Serializable; +import java.util.Map; +import java.util.SortedMap; + +/** + * A specification by which a benchmark method invocation can be uniquely identified. + * + * @author gak@google.com (Gregory Kick) + */ +public final class BenchmarkSpec implements Serializable { + private static final long serialVersionUID = 1L; + + static final BenchmarkSpec DEFAULT = new BenchmarkSpec(); + + @ExcludeFromJson private int id; + private String className; + private String methodName; + private SortedMap<String, String> parameters; + @ExcludeFromJson private int hash; + + private BenchmarkSpec() { + this.className = ""; + this.methodName = ""; + this.parameters = Maps.newTreeMap(); + } + + private BenchmarkSpec(Builder builder) { + this.className = builder.className; + this.methodName = builder.methodName; + this.parameters = Maps.newTreeMap(builder.parameters); + } + + public String className() { + return className; + } + + public String methodName() { + return methodName; + } + + public ImmutableSortedMap<String, String> parameters() { + return ImmutableSortedMap.copyOf(parameters); + } + + @Override public boolean equals(Object obj) { + if (obj == this) { + return true; + } else if (obj instanceof BenchmarkSpec) { + BenchmarkSpec that = (BenchmarkSpec) obj; + return this.className.equals(that.className) + && this.methodName.equals(that.methodName) + && this.parameters.equals(that.parameters); + } else { + return false; + } + } + + private void initHash() { + if (hash == 0) { + this.hash = getPersistentHashFunction() + .hashObject(this, BenchmarkSpecFunnel.INSTANCE).asInt(); + } + } + + @Override public int hashCode() { + initHash(); + return hash; + } + + @Override public String toString() { + return MoreObjects.toStringHelper(this) + .add("className", className) + .add("methodName", methodName) + .add("parameters", parameters) + .toString(); + } + + enum BenchmarkSpecFunnel implements Funnel<BenchmarkSpec> { + INSTANCE; + + @Override public void funnel(BenchmarkSpec from, PrimitiveSink into) { + into.putUnencodedChars(from.className) + .putUnencodedChars(from.methodName); + StringMapFunnel.INSTANCE.funnel(from.parameters, into); + } + } + + public static final class Builder { + private String className; + private String methodName; + private final SortedMap<String, String> parameters = Maps.newTreeMap(); + + public Builder className(String className) { + this.className = checkNotNull(className); + return this; + } + + public Builder methodName(String methodName) { + this.methodName = checkNotNull(methodName); + return this; + } + + public Builder addParameter(String parameterName, String value) { + this.parameters.put(checkNotNull(parameterName), checkNotNull(value)); + return this; + } + + public Builder addAllParameters(Map<String, String> parameters) { + this.parameters.putAll(parameters); + return this; + } + + public BenchmarkSpec build() { + checkState(className != null); + checkState(methodName != null); + return new BenchmarkSpec(this); + } + } +} diff --git a/caliper/src/main/java/com/google/caliper/model/Defaults.java b/caliper/src/main/java/com/google/caliper/model/Defaults.java new file mode 100644 index 0000000..1765e09 --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/model/Defaults.java @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2012 Google Inc. + * + * 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.google.caliper.model; + +import org.joda.time.Instant; + +import java.util.UUID; + +/** + * Default instances of non-model classes used by the Caliper model. These are present to facilitate + * Gson deserialization. + */ +final class Defaults { + static final UUID UUID = new UUID(0L, 0L); + static final Instant INSTANT = new Instant(0L); + + private Defaults() {} +} diff --git a/caliper/src/main/java/com/google/caliper/model/ExcludeFromJson.java b/caliper/src/main/java/com/google/caliper/model/ExcludeFromJson.java new file mode 100644 index 0000000..fdf0b95 --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/model/ExcludeFromJson.java @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2012 Google Inc. + * + * 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.google.caliper.model; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +/** + * Indicates that a field or type should not be serialized to JSON. + */ +@Target({FIELD, TYPE}) +@Retention(RUNTIME) +public @interface ExcludeFromJson {} diff --git a/caliper/src/main/java/com/google/caliper/model/Host.java b/caliper/src/main/java/com/google/caliper/model/Host.java new file mode 100644 index 0000000..ecd098c --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/model/Host.java @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2012 Google Inc. + * + * 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.google.caliper.model; + +import static com.google.caliper.model.PersistentHashing.getPersistentHashFunction; +import static com.google.common.base.Preconditions.checkNotNull; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.MoreObjects; +import com.google.common.collect.ImmutableSortedMap; +import com.google.common.collect.Maps; +import com.google.common.hash.Funnel; +import com.google.common.hash.HashFunction; +import com.google.common.hash.PrimitiveSink; + +import java.util.Map; +import java.util.SortedMap; +import java.util.logging.Logger; + +/** + * The performance-informing properties of the host on which a benchmark is run. + * + * @author gak@google.com (Gregory Kick) + */ +public final class Host { + static final Host DEFAULT = new Host(); + private static final Logger logger = Logger.getLogger(Host.class.getName()); + + @ExcludeFromJson private int id; + private SortedMap<String, String> properties; + @ExcludeFromJson private int hash; + + private Host() { + this.properties = Maps.newTreeMap(); + } + + private Host(Builder builder) { + this.properties = Maps.newTreeMap(builder.properties); + // eagerly initialize hash to allow for the test-only hash function + initHash(builder.hashFunction); + } + + public ImmutableSortedMap<String, String> properties() { + return ImmutableSortedMap.copyOf(properties); + } + + @Override public boolean equals(Object obj) { + if (obj == this) { + return true; + } else if (obj instanceof Host) { + Host that = (Host) obj; + return this.properties.equals(that.properties); + } else { + return false; + } + } + + private void initHash(HashFunction hashFunction) { + if (hash == 0) { + this.hash = hashFunction.hashObject(this, HostFunnel.INSTANCE).asInt(); + } + } + + private void initHash() { + initHash(getPersistentHashFunction()); + } + + @Override public int hashCode() { + initHash(); + return hash; + } + + @Override public String toString() { + return MoreObjects.toStringHelper(this) + .add("properties", properties) + .toString(); + } + + enum HostFunnel implements Funnel<Host> { + INSTANCE; + + @Override public void funnel(Host from, PrimitiveSink into) { + StringMapFunnel.INSTANCE.funnel(from.properties, into); + } + } + + public static final class Builder { + private final SortedMap<String, String> properties = Maps.newTreeMap(); + private HashFunction hashFunction = getPersistentHashFunction(); + + public Builder addProperty(String key, String value) { + properties.put(key, value); + return this; + } + + public Builder addAllProperies(Map<String, String> properties) { + this.properties.putAll(properties); + return this; + } + + /** + * This only exists for tests to induce hash collisions. Only use this in test code as changing + * the hash function will break persisted objects. + */ + @VisibleForTesting public Builder hashFunctionForTesting(HashFunction hashFunction) { + logger.warning("somebody is setting the hash function. this should only be used in tests"); + this.hashFunction = checkNotNull(hashFunction); + return this; + } + + public Host build() { + return new Host(this); + } + } +} diff --git a/caliper/src/main/java/com/google/caliper/model/InstrumentSpec.java b/caliper/src/main/java/com/google/caliper/model/InstrumentSpec.java new file mode 100644 index 0000000..6a7cd77 --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/model/InstrumentSpec.java @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2012 Google Inc. + * + * 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.google.caliper.model; + +import static com.google.caliper.model.PersistentHashing.getPersistentHashFunction; +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.checkState; + +import com.google.common.base.MoreObjects; +import com.google.common.collect.ImmutableSortedMap; +import com.google.common.collect.Maps; + +import java.util.Map; +import java.util.SortedMap; + +/** + * A specification by which the application of an instrument can be uniquely identified. + * + * @author gak@google.com (Gregory Kick) + */ +public final class InstrumentSpec { + static final InstrumentSpec DEFAULT = new InstrumentSpec(); + + @ExcludeFromJson + private int id; + private String className; + private SortedMap<String, String> options; + @ExcludeFromJson + private int hash; + + private InstrumentSpec() { + this.className = ""; + this.options = Maps.newTreeMap(); + } + + private InstrumentSpec(Builder builder) { + this.className = builder.className; + this.options = Maps.newTreeMap(builder.options); + } + + public String className() { + return className; + } + + public ImmutableSortedMap<String, String> options() { + return ImmutableSortedMap.copyOf(options); + } + + @Override public boolean equals(Object obj) { + if (obj == this) { + return true; + } else if (obj instanceof InstrumentSpec) { + InstrumentSpec that = (InstrumentSpec) obj; + return this.className.equals(that.className) + && this.options.equals(that.options); + } else { + return false; + } + } + + private void initHash() { + if (hash == 0) { + this.hash = getPersistentHashFunction() + .newHasher() + .putUnencodedChars(className) + .putObject(options, StringMapFunnel.INSTANCE) + .hash().asInt(); + } + } + + @Override public int hashCode() { + initHash(); + return hash; + } + + @Override public String toString() { + return MoreObjects.toStringHelper(this) + .add("className", className) + .add("options", options) + .toString(); + } + + public static final class Builder { + private String className; + private final SortedMap<String, String> options = Maps.newTreeMap(); + + public Builder className(String className) { + this.className = checkNotNull(className); + return this; + } + + public Builder instrumentClass(Class<?> insturmentClass) { + return className(insturmentClass.getName()); + } + + public Builder addOption(String option, String value) { + this.options.put(option, value); + return this; + } + + public Builder addAllOptions(Map<String, String> options) { + this.options.putAll(options); + return this; + } + + public InstrumentSpec build() { + checkState(className != null); + return new InstrumentSpec(this); + } + } +} diff --git a/caliper/src/main/java/com/google/caliper/model/Measurement.java b/caliper/src/main/java/com/google/caliper/model/Measurement.java new file mode 100644 index 0000000..8effce7 --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/model/Measurement.java @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2012 Google Inc. + * + * 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.google.caliper.model; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +import com.google.common.base.Function; +import com.google.common.base.MoreObjects; +import com.google.common.base.Objects; +import com.google.common.collect.ImmutableListMultimap; +import com.google.common.collect.Multimaps; + +import java.io.Serializable; + +/** + * A single, weighted measurement. + * + * @author gak@google.com (Gregory Kick) + */ +public class Measurement implements Serializable { + private static final long serialVersionUID = 1L; + + public static ImmutableListMultimap<String, Measurement> indexByDescription( + Iterable<Measurement> measurements) { + return Multimaps.index(measurements, new Function<Measurement, String>() { + @Override public String apply(Measurement input) { + return input.description; + } + }); + } + + @ExcludeFromJson + private int id; + private Value value; + private double weight; + private String description; + + private Measurement() { + this.value = Value.DEFAULT; + this.weight = 0.0; + this.description = ""; + } + + private Measurement(Builder builder) { + this.value = builder.value; + this.description = builder.description; + this.weight = builder.weight; + } + + @Override public boolean equals(Object obj) { + if (obj == this) { + return true; + } else if (obj instanceof Measurement) { + Measurement that = (Measurement) obj; + return this.value.equals(that.value) + && this.weight == that.weight + && this.description.equals(that.description); + } else { + return false; + } + } + + @Override public int hashCode() { + return Objects.hashCode(value, weight, description); + } + + @Override public String toString() { + return MoreObjects.toStringHelper(this) + .add("value", value) + .add("weight", weight) + .add("description", description) + .toString(); + } + + public Value value() { + return value; + } + + public double weight() { + return weight; + } + + public String description() { + return description; + } + + public static final class Builder { + private Value value; + private Double weight; + private String description; + + public Builder value(Value value) { + this.value = checkNotNull(value); + return this; + } + + public Builder weight(double weight) { + checkArgument(weight > 0); + this.weight = weight; + return this; + } + + public Builder description(String description) { + this.description = checkNotNull(description); + return this; + } + + public Measurement build() { + checkArgument(value != null); + checkArgument(weight != null); + checkArgument(description != null); + return new Measurement(this); + } + } +} diff --git a/caliper/src/test/java/com/google/caliper/AllTests.java b/caliper/src/main/java/com/google/caliper/model/PersistentHashing.java index 0e3d1cc..48a3e08 100644 --- a/caliper/src/test/java/com/google/caliper/AllTests.java +++ b/caliper/src/main/java/com/google/caliper/model/PersistentHashing.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 Google Inc. + * Copyright (C) 2012 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,20 +14,18 @@ * limitations under the License. */ -package com.google.caliper; +package com.google.caliper.model; -import junit.framework.Test; -import junit.framework.TestSuite; +import com.google.common.hash.HashFunction; +import com.google.common.hash.Hashing; -public class AllTests { - public static Test suite() { - TestSuite suite = new TestSuite(); - suite.addTestSuite(CaliperTest.class); - suite.addTestSuite(JsonTest.class); - suite.addTestSuite(MeasurementSetTest.class); - suite.addTestSuite(ParameterTest.class); - suite.addTestSuite(WarmupOverflowTest.class); +/** + * A utility class for standardizing the hash function that we're using for persistence. + */ +final class PersistentHashing { + private PersistentHashing() {} - return suite; + static HashFunction getPersistentHashFunction() { + return Hashing.murmur3_32(); } } diff --git a/caliper/src/main/java/com/google/caliper/model/Run.java b/caliper/src/main/java/com/google/caliper/model/Run.java new file mode 100644 index 0000000..eb9733b --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/model/Run.java @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2012 Google Inc. + * + * 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.google.caliper.model; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.checkState; + +import com.google.common.base.MoreObjects; +import com.google.common.base.Objects; + +import org.joda.time.Instant; + +import java.util.UUID; + +/** + * A single invocation of caliper. + * + * @author gak@google.com (Gregory Kick) + */ +public final class Run { + static final Run DEFAULT = new Run(); + + private UUID id; + private String label; + private Instant startTime; + + private Run() { + this.id = Defaults.UUID; + this.label = ""; + this.startTime = Defaults.INSTANT; + } + + private Run(Builder builder) { + this.id = builder.id; + this.label = builder.label; + this.startTime = builder.startTime; + } + + public UUID id() { + return id; + } + + public String label() { + return label; + } + + public Instant startTime() { + return startTime; + } + + @Override public boolean equals(Object obj) { + if (obj == this) { + return true; + } else if (obj instanceof Run) { + Run that = (Run) obj; + return this.id.equals(that.id) + && this.label.equals(that.label) + && this.startTime.equals(that.startTime); + } else { + return false; + } + } + + @Override public int hashCode() { + return Objects.hashCode(id, label, startTime); + } + + @Override public String toString() { + return MoreObjects.toStringHelper(this) + .add("id", id) + .add("label", label) + .add("startTime", startTime) + .toString(); + } + + public static final class Builder { + private UUID id; + private String label = ""; + private Instant startTime; + + public Builder(UUID id) { + this.id = checkNotNull(id); + } + + public Builder label(String label) { + this.label = checkNotNull(label); + return this; + } + + public Builder startTime(Instant startTime) { + this.startTime = checkNotNull(startTime); + return this; + } + + public Run build() { + checkState(id != null); + checkState(startTime != null); + return new Run(this); + } + } +} diff --git a/caliper/src/main/java/com/google/caliper/model/Scenario.java b/caliper/src/main/java/com/google/caliper/model/Scenario.java new file mode 100644 index 0000000..414d4ab --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/model/Scenario.java @@ -0,0 +1,144 @@ +/* + * Copyright (C) 2012 Google Inc. + * + * 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.google.caliper.model; + +import static com.google.caliper.model.PersistentHashing.getPersistentHashFunction; +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.checkState; + +import com.google.caliper.model.BenchmarkSpec.BenchmarkSpecFunnel; +import com.google.caliper.model.Host.HostFunnel; +import com.google.caliper.model.VmSpec.VmSpecFunnel; +import com.google.common.base.MoreObjects; + +/** + * The combination of properties whose combination, when measured with a particular instrument, + * should produce a repeatable result + * + * @author gak@google.com (Gregory Kick) + */ +public final class Scenario { + static final Scenario DEFAULT = new Scenario(); + + @ExcludeFromJson private int id; + private Host host; + private VmSpec vmSpec; + private BenchmarkSpec benchmarkSpec; + // TODO(gak): include data about caliper itself and the code being benchmarked + @ExcludeFromJson private int hash; + + private Scenario() { + this.host = Host.DEFAULT; + this.vmSpec = VmSpec.DEFAULT; + this.benchmarkSpec = BenchmarkSpec.DEFAULT; + } + + private Scenario(Builder builder) { + this.host = builder.host; + this.vmSpec = builder.vmSpec; + this.benchmarkSpec = builder.benchmarkSpec; + } + + public Host host() { + return host; + } + + public VmSpec vmSpec() { + return vmSpec; + } + + public BenchmarkSpec benchmarkSpec() { + return benchmarkSpec; + } + + @Override public boolean equals(Object obj) { + if (obj == this) { + return true; + } else if (obj instanceof Scenario) { + Scenario that = (Scenario) obj; + return this.host.equals(that.host) + && this.vmSpec.equals(that.vmSpec) + && this.benchmarkSpec.equals(that.benchmarkSpec); + } else { + return false; + } + } + + private void initHash() { + if (hash == 0) { + this.hash = getPersistentHashFunction() + .newHasher() + .putObject(host, HostFunnel.INSTANCE) + .putObject(vmSpec, VmSpecFunnel.INSTANCE) + .putObject(benchmarkSpec, BenchmarkSpecFunnel.INSTANCE) + .hash().asInt(); + } + } + + @Override public int hashCode() { + initHash(); + return hash; + } + + @Override public String toString() { + return MoreObjects.toStringHelper(this) + .add("environment", host) + .add("vmSpec", vmSpec) + .add("benchmarkSpec", benchmarkSpec) + .toString(); + } + + public static final class Builder { + private Host host; + private VmSpec vmSpec; + private BenchmarkSpec benchmarkSpec; + + public Builder host(Host.Builder hostBuilder) { + return host(hostBuilder.build()); + } + + public Builder host(Host host) { + this.host = checkNotNull(host); + return this; + } + + public Builder vmSpec(VmSpec.Builder vmSpecBuilder) { + return vmSpec(vmSpecBuilder.build()); + } + + public Builder vmSpec(VmSpec vmSpec) { + this.vmSpec = checkNotNull(vmSpec); + return this; + } + + public Builder benchmarkSpec(BenchmarkSpec.Builder benchmarkSpecBuilder) { + return benchmarkSpec(benchmarkSpecBuilder.build()); + } + + public Builder benchmarkSpec(BenchmarkSpec benchmarkSpec) { + this.benchmarkSpec = checkNotNull(benchmarkSpec); + return this; + } + + public Scenario build() { + checkState(host != null); + checkState(vmSpec != null); + checkState(benchmarkSpec != null); + return new Scenario(this); + } + } +} diff --git a/caliper/src/main/java/com/google/caliper/model/StringMapFunnel.java b/caliper/src/main/java/com/google/caliper/model/StringMapFunnel.java new file mode 100644 index 0000000..ae3f43b --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/model/StringMapFunnel.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2012 Google Inc. + * + * 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.google.caliper.model; + +import com.google.common.hash.Funnel; +import com.google.common.hash.PrimitiveSink; + +import java.util.Map; +import java.util.Map.Entry; + +/** + * A simple funnel that inserts string map entries into a funnel in iteration order. + */ +enum StringMapFunnel implements Funnel<Map<String, String>> { + INSTANCE; + + @Override + public void funnel(Map<String, String> from, PrimitiveSink into) { + for (Entry<String, String> entry : from.entrySet()) { + into.putUnencodedChars(entry.getKey()) + .putByte((byte) -1) // separate key and value + .putUnencodedChars(entry.getValue()); + } + } +} diff --git a/caliper/src/main/java/com/google/caliper/model/Trial.java b/caliper/src/main/java/com/google/caliper/model/Trial.java new file mode 100644 index 0000000..08334cf --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/model/Trial.java @@ -0,0 +1,169 @@ +/* + * Copyright (C) 2012 Google Inc. + * + * 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.google.caliper.model; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.checkState; + +import com.google.common.base.MoreObjects; +import com.google.common.base.Objects; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; + +import java.util.List; +import java.util.UUID; + +/** + * An invocation of a single scenario measured with a single instrument and the results thereof. + * + * @author gak@google.com (Gregory Kick) + */ +public final class Trial { // used to be Result + public static final Trial DEFAULT = new Trial(); + + private UUID id; + private Run run; + private InstrumentSpec instrumentSpec; + private Scenario scenario; + private List<Measurement> measurements; + + private Trial() { + this.id = Defaults.UUID; + this.run = Run.DEFAULT; + this.instrumentSpec = InstrumentSpec.DEFAULT; + this.scenario = Scenario.DEFAULT; + this.measurements = Lists.newArrayList(); + } + + private Trial(Builder builder) { + this.id = builder.id; + this.run = builder.run; + this.instrumentSpec = builder.instrumentSpec; + this.scenario = builder.scenario; + this.measurements = Lists.newArrayList(builder.measurements); + } + + public UUID id() { + return id; + } + + public Run run() { + return run; + } + + public InstrumentSpec instrumentSpec() { + return instrumentSpec; + } + + public Scenario scenario() { + return scenario; + } + + public ImmutableList<Measurement> measurements() { + return ImmutableList.copyOf(measurements); + } + + @Override public boolean equals(Object obj) { + if (obj == this) { + return true; + } else if (obj instanceof Trial) { + Trial that = (Trial) obj; + return this.id.equals(that.id) + && this.run.equals(that.run) + && this.instrumentSpec.equals(that.instrumentSpec) + && this.scenario.equals(that.scenario) + && this.measurements.equals(that.measurements); + } else { + return false; + } + } + + @Override public int hashCode() { + return Objects.hashCode(id, run, instrumentSpec, scenario, measurements); + } + + @Override public String toString() { + return MoreObjects.toStringHelper(this) + .add("id", id) + .add("run", run) + .add("instrumentSpec", instrumentSpec) + .add("scenario", scenario) + .add("measurements", measurements) + .toString(); + } + + public static final class Builder { + private final UUID id; + private Run run; + private InstrumentSpec instrumentSpec; + private Scenario scenario; + private final List<Measurement> measurements = Lists.newArrayList(); + + public Builder(UUID id) { + this.id = checkNotNull(id); + } + + public Builder run(Run.Builder runBuilder) { + return run(runBuilder.build()); + } + + public Builder run(Run run) { + this.run = checkNotNull(run); + return this; + } + + public Builder instrumentSpec(InstrumentSpec.Builder instrumentSpecBuilder) { + return instrumentSpec(instrumentSpecBuilder.build()); + } + + public Builder instrumentSpec(InstrumentSpec instrumentSpec) { + this.instrumentSpec = checkNotNull(instrumentSpec); + return this; + } + + public Builder scenario(Scenario.Builder scenarioBuilder) { + return scenario(scenarioBuilder.build()); + } + + public Builder scenario(Scenario scenario) { + this.scenario = checkNotNull(scenario); + return this; + } + + public Builder addMeasurement(Measurement.Builder measurementBuilder) { + return addMeasurement(measurementBuilder.build()); + } + + public Builder addMeasurement(Measurement measurement) { + this.measurements.add(measurement); + return this; + } + + public Builder addAllMeasurements(Iterable<Measurement> measurements) { + Iterables.addAll(this.measurements, measurements); + return this; + } + + public Trial build() { + checkState(run != null); + checkState(instrumentSpec != null); + checkState(scenario != null); + return new Trial(this); + } + } +} diff --git a/caliper/src/main/java/com/google/caliper/model/Value.java b/caliper/src/main/java/com/google/caliper/model/Value.java new file mode 100644 index 0000000..b0daee9 --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/model/Value.java @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2012 Google Inc. + * + * 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.google.caliper.model; + +import static com.google.common.base.Preconditions.checkNotNull; + +import com.google.common.base.Objects; + +import java.io.Serializable; + +/** + * A magnitude with units. + * + * @author gak@google.com (Gregory Kick) + */ +public class Value implements Serializable { + private static final long serialVersionUID = 1L; + + static final Value DEFAULT = new Value(); + + public static Value create(double value, String unit) { + return new Value(value, checkNotNull(unit)); + } + + private double magnitude; + // TODO(gak): do something smarter than string for units + // TODO(gak): give guidelines for how to specify units. E.g. s or seconds + private String unit; + + private Value() { + this.magnitude = 0.0; + this.unit = ""; + } + + private Value(double value, String unit) { + this.magnitude = value; + this.unit = unit; + } + + public String unit() { + return unit; + } + + public double magnitude() { + return magnitude; + } + + @Override public boolean equals(Object obj) { + if (obj == this) { + return true; + } else if (obj instanceof Value) { + Value that = (Value) obj; + return this.magnitude == that.magnitude + && this.unit.equals(that.unit); + } else { + return false; + } + } + + @Override public int hashCode() { + return Objects.hashCode(magnitude, unit); + } + + @Override public String toString() { + return new StringBuilder().append(magnitude).append(unit).toString(); + } +} diff --git a/caliper/src/main/java/com/google/caliper/model/VmSpec.java b/caliper/src/main/java/com/google/caliper/model/VmSpec.java new file mode 100644 index 0000000..d4b835b --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/model/VmSpec.java @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2012 Google Inc. + * + * 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.google.caliper.model; + +import static com.google.caliper.model.PersistentHashing.getPersistentHashFunction; + +import com.google.common.base.MoreObjects; +import com.google.common.collect.ImmutableSortedMap; +import com.google.common.collect.Maps; +import com.google.common.hash.Funnel; +import com.google.common.hash.PrimitiveSink; + +import java.util.Map; +import java.util.SortedMap; + +/** + * A configuration of a virtual machine. + * + * @author gak@google.com (Gregory Kick) + */ +public final class VmSpec { + static final VmSpec DEFAULT = new VmSpec(); + + @ExcludeFromJson private int id; + private SortedMap<String, String> properties; + private SortedMap<String, String> options; + @ExcludeFromJson private int hash; + + private VmSpec() { + this.properties = Maps.newTreeMap(); + this.options = Maps.newTreeMap(); + } + + private VmSpec(Builder builder) { + this.properties = Maps.newTreeMap(builder.properties); + this.options = Maps.newTreeMap(builder.options); + } + + public ImmutableSortedMap<String, String> options() { + return ImmutableSortedMap.copyOf(options); + } + + public ImmutableSortedMap<String, String> properties() { + return ImmutableSortedMap.copyOf(properties); + } + + @Override public boolean equals(Object obj) { + if (obj == this) { + return true; + } else if (obj instanceof VmSpec) { + VmSpec that = (VmSpec) obj; + return this.properties.equals(that.properties) + && this.options.equals(that.options); + } else { + return false; + } + } + + private void initHash() { + if (hash == 0) { + this.hash = getPersistentHashFunction().hashObject(this, VmSpecFunnel.INSTANCE).asInt(); + } + } + + @Override public int hashCode() { + initHash(); + return hash; + } + + @Override public String toString() { + return MoreObjects.toStringHelper(this) + .add("properties", properties) + .add("options", options) + .toString(); + } + + enum VmSpecFunnel implements Funnel<VmSpec> { + INSTANCE; + + @Override public void funnel(VmSpec from, PrimitiveSink into) { + StringMapFunnel.INSTANCE.funnel(from.properties, into); + StringMapFunnel.INSTANCE.funnel(from.options, into); + } + } + + public static final class Builder { + private final SortedMap<String, String> properties = Maps.newTreeMap(); + private final SortedMap<String, String> options = Maps.newTreeMap(); + + public Builder addOption(String optionName, String value) { + this.options.put(optionName, value); + return this; + } + + public Builder addAllOptions(Map<String, String> options) { + this.options.putAll(options); + return this; + } + + public Builder addProperty(String property, String value) { + this.properties.put(property, value); + return this; + } + + public Builder addAllProperties(Map<String, String> properties) { + this.properties.putAll(properties); + return this; + } + + public VmSpec build() { + return new VmSpec(this); + } + } +} diff --git a/caliper/src/main/java/com/google/caliper/model/package-info.java b/caliper/src/main/java/com/google/caliper/model/package-info.java new file mode 100644 index 0000000..882ca26 --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/model/package-info.java @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2012 Google Inc. + * + * 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. + */ + +/** + * These classes model the data that is collected by the caliper {@linkplain + * com.google.caliper.runner runner}: the record of which scenarios were tested on which VMs by + * which instruments and, most importantly, all the measurements that were observed. + * + * <p>The primary goal of these classes is to be as easily convertible back and forth to JSON text + * as possible. The secondary goal is to be easily persistable in a relational database. + */ +package com.google.caliper.model;
\ No newline at end of file diff --git a/caliper/src/main/java/com/google/caliper/options/CaliperDirectory.java b/caliper/src/main/java/com/google/caliper/options/CaliperDirectory.java new file mode 100644 index 0000000..30f8d92 --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/options/CaliperDirectory.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2012 Google Inc. + * + * 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.google.caliper.options; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import javax.inject.Qualifier; + +/** Binding annotation for the directory that caliper should use to store data (--directory). */ +@Retention(RUNTIME) +@Target({FIELD, PARAMETER, METHOD}) +@Qualifier +public @interface CaliperDirectory {} diff --git a/caliper/src/main/java/com/google/caliper/options/CaliperOptions.java b/caliper/src/main/java/com/google/caliper/options/CaliperOptions.java new file mode 100644 index 0000000..86b96e7 --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/options/CaliperOptions.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2011 Google Inc. + * + * 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.google.caliper.options; + +import com.google.caliper.util.ShortDuration; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.ImmutableSetMultimap; + +import java.io.File; + +public interface CaliperOptions { + String benchmarkClassName(); + ImmutableSet<String> benchmarkMethodNames(); + ImmutableSet<String> vmNames(); + ImmutableSetMultimap<String, String> userParameters(); + ImmutableSetMultimap<String, String> vmArguments(); + ImmutableMap<String, String> configProperties(); + ImmutableSet<String> instrumentNames(); + int trialsPerScenario(); + ShortDuration timeLimit(); + String runName(); + boolean printConfiguration(); + boolean dryRun(); + File caliperDirectory(); + File caliperConfigFile(); +} diff --git a/caliper/src/main/java/com/google/caliper/options/CommandLineParser.java b/caliper/src/main/java/com/google/caliper/options/CommandLineParser.java new file mode 100644 index 0000000..5e67904 --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/options/CommandLineParser.java @@ -0,0 +1,444 @@ +/* + * Copyright (C) 2011 Google Inc. + * + * 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.google.caliper.options; + +import static com.google.common.base.Preconditions.checkArgument; + +import com.google.caliper.util.DisplayUsageException; +import com.google.caliper.util.InvalidCommandException; +import com.google.caliper.util.Parser; +import com.google.caliper.util.Parsers; +import com.google.common.base.Throwables; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Iterators; +import com.google.common.collect.Lists; +import com.google.common.primitives.Primitives; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.lang.reflect.Type; +import java.text.ParseException; +import java.util.Iterator; +import java.util.List; + +// based on r135 of OptionParser.java from vogar +// NOTE: this class is still pretty messy but will be cleaned up further and possibly offered to +// Guava. + +/** + * Parses command line options. + * + * Strings in the passed-in String[] are parsed left-to-right. Each String is classified as a short + * option (such as "-v"), a long option (such as "--verbose"), an argument to an option (such as + * "out.txt" in "-f out.txt"), or a non-option positional argument. + * + * A simple short option is a "-" followed by a short option character. If the option requires an + * argument (which is true of any non-boolean option), it may be written as a separate parameter, + * but need not be. That is, "-f out.txt" and "-fout.txt" are both acceptable. + * + * It is possible to specify multiple short options after a single "-" as long as all (except + * possibly the last) do not require arguments. + * + * A long option begins with "--" followed by several characters. If the option requires an + * argument, it may be written directly after the option name, separated by "=", or as the next + * argument. (That is, "--file=out.txt" or "--file out.txt".) + * + * A boolean long option '--name' automatically gets a '--no-name' companion. Given an option + * "--flag", then, "--flag", "--no-flag", "--flag=true" and "--flag=false" are all valid, though + * neither "--flag true" nor "--flag false" are allowed (since "--flag" by itself is sufficient, the + * following "true" or "false" is interpreted separately). You can use "yes" and "no" as synonyms + * for "true" and "false". + * + * Each String not starting with a "-" and not a required argument of a previous option is a + * non-option positional argument, as are all successive Strings. Each String after a "--" is a + * non-option positional argument. + * + * The fields corresponding to options are updated as their options are processed. Any remaining + * positional arguments are returned as an ImmutableList<String>. + * + * Here's a simple example: + * + * // This doesn't need to be a separate class, if your application doesn't warrant it. // + * Non-@Option fields will be ignored. class Options { + * + * @Option(names = { "-q", "--quiet" }) boolean quiet = false; + * + * // Boolean options require a long name if it's to be possible to explicitly turn them off. // + * Here the user can use --no-color. + * @Option(names = { "--color" }) boolean color = true; + * @Option(names = { "-m", "--mode" }) String mode = "standard; // Supply a default just by setting + * the field. + * @Option(names = { "-p", "--port" }) int portNumber = 8888; + * + * // There's no need to offer a short name for rarely-used options. + * @Option(names = { "--timeout" }) double timeout = 1.0; + * @Option(names = { "-o", "--output-file" }) String outputFile; + * + * } + * + * See also: + * + * the getopt(1) man page Python's "optparse" module (http://docs.python.org/library/optparse.html) + * the POSIX "Utility Syntax Guidelines" (http://www.opengroup.org/onlinepubs/000095399/basedefs/xbd_chap12.html#tag_12_02) + * the GNU "Standards for Command Line Interfaces" (http://www.gnu.org/prep/standards/standards.html#Command_002dLine-Interfaces) + */ +final class CommandLineParser<T> { + /** + * Annotates a field or method in an options class to signify that parsed values should be + * injected. + */ + @Retention(RetentionPolicy.RUNTIME) + @Target({ElementType.FIELD, ElementType.METHOD}) + public @interface Option { + /** + * The names for this option, such as { "-h", "--help" }. Names must start with one or two '-'s. + * An option must have at least one name. + */ + String[] value(); + } + + /** + * Annotates a single method in an options class to receive any "leftover" arguments. The method + * must accept {@code ImmutableList<String>} or a supertype. The method will be invoked even if + * the list is empty. + */ + @Retention(RetentionPolicy.RUNTIME) + @Target({ElementType.FIELD, ElementType.METHOD}) + public @interface Leftovers {} + + public static <T> CommandLineParser<T> forClass(Class<? extends T> c) { + return new CommandLineParser<T>(c); + } + + private final InjectionMap injectionMap; + private T injectee; + + // TODO(kevinb): make a helper object that can be mutated during processing + private final List<PendingInjection> pendingInjections = Lists.newArrayList(); + + /** + * Constructs a new command-line parser that will inject values into {@code injectee}. + * + * @throws IllegalArgumentException if {@code injectee} contains multiple options using the same + * name + */ + private CommandLineParser(Class<? extends T> c) { + this.injectionMap = InjectionMap.forClass(c); + } + + /** + * Parses the command-line arguments 'args', setting the @Option fields of the 'optionSource' + * provided to the constructor. Returns a list of the positional arguments left over after + * processing all options. + */ + public void parseAndInject(String[] args, T injectee) throws InvalidCommandException { + this.injectee = injectee; + pendingInjections.clear(); + Iterator<String> argsIter = Iterators.forArray(args); + ImmutableList.Builder<String> builder = ImmutableList.builder(); + + while (argsIter.hasNext()) { + String arg = argsIter.next(); + if (arg.equals("--")) { + break; // "--" marks the end of options and the beginning of positional arguments. + } else if (arg.startsWith("--")) { + parseLongOption(arg, argsIter); + } else if (arg.startsWith("-")) { + parseShortOptions(arg, argsIter); + } else { + builder.add(arg); + // allow positional arguments to mix with options since many linux commands do + } + } + + for (PendingInjection pi : pendingInjections) { + pi.injectableOption.inject(pi.value, injectee); + } + + ImmutableList<String> leftovers = builder.addAll(argsIter).build(); + invokeMethod(injectee, injectionMap.leftoversMethod, leftovers); + } + + // Private stuff from here on down + + private abstract static class InjectableOption { + abstract boolean isBoolean(); + abstract void inject(String valueText, Object injectee) throws InvalidCommandException; + boolean delayedInjection() { + return false; + } + } + + private static class InjectionMap { + public static InjectionMap forClass(Class<?> injectedClass) { + ImmutableMap.Builder<String, InjectableOption> builder = ImmutableMap.builder(); + + InjectableOption helpOption = new InjectableOption() { + @Override boolean isBoolean() { + return true; + } + @Override void inject(String valueText, Object injectee) throws DisplayUsageException { + throw new DisplayUsageException(); + } + }; + builder.put("-h", helpOption); + builder.put("--help", helpOption); + + Method leftoverMethod = null; + + for (Field field : injectedClass.getDeclaredFields()) { + checkArgument(!field.isAnnotationPresent(Leftovers.class), + "Sorry, @Leftovers only works for methods at present"); // TODO(kevinb) + Option option = field.getAnnotation(Option.class); + if (option != null) { + InjectableOption injectable = FieldOption.create(field); + for (String optionName : option.value()) { + builder.put(optionName, injectable); + } + } + } + for (Method method : injectedClass.getDeclaredMethods()) { + if (method.isAnnotationPresent(Leftovers.class)) { + checkArgument(!isStaticOrAbstract(method), + "@Leftovers method cannot be static or abstract"); + checkArgument(!method.isAnnotationPresent(Option.class), + "method has both @Option and @Leftovers"); + checkArgument(leftoverMethod == null, "Two methods have @Leftovers"); + + method.setAccessible(true); + leftoverMethod = method; + + // TODO: check type is a supertype of ImmutableList<String> + } + Option option = method.getAnnotation(Option.class); + if (option != null) { + InjectableOption injectable = MethodOption.create(method); + for (String optionName : option.value()) { + builder.put(optionName, injectable); + } + } + } + + ImmutableMap<String, InjectableOption> optionMap = builder.build(); + return new InjectionMap(optionMap, leftoverMethod); + } + + final ImmutableMap<String, InjectableOption> optionMap; + final Method leftoversMethod; + + InjectionMap(ImmutableMap<String, InjectableOption> optionMap, Method leftoversMethod) { + this.optionMap = optionMap; + this.leftoversMethod = leftoversMethod; + } + + InjectableOption getInjectableOption(String optionName) throws InvalidCommandException { + InjectableOption injectable = optionMap.get(optionName); + if (injectable == null) { + throw new InvalidCommandException("Invalid option: %s", optionName); + } + return injectable; + } + } + + private static class FieldOption extends InjectableOption { + private static InjectableOption create(Field field) { + field.setAccessible(true); + Type type = field.getGenericType(); + + if (type instanceof Class) { + return new FieldOption(field, (Class<?>) type); + } + throw new IllegalArgumentException("can't inject parameterized types etc."); + } + + private Field field; + private boolean isBoolean; + private Parser<?> parser; + + private FieldOption(Field field, Class<?> c) { + this.field = field; + this.isBoolean = c == boolean.class || c == Boolean.class; + try { + this.parser = Parsers.conventionalParser(Primitives.wrap(c)); + } catch (NoSuchMethodException e) { + throw new IllegalArgumentException("No suitable String-conversion method"); + } + } + + @Override boolean isBoolean() { + return isBoolean; + } + + @Override void inject(String valueText, Object injectee) throws InvalidCommandException { + Object value = convert(parser, valueText); + try { + field.set(injectee, value); + } catch (IllegalAccessException impossible) { + throw new AssertionError(impossible); + } + } + } + + private static class MethodOption extends InjectableOption { + private static InjectableOption create(Method method) { + checkArgument(!isStaticOrAbstract(method), + "@Option methods cannot be static or abstract"); + Class<?>[] classes = method.getParameterTypes(); + checkArgument(classes.length == 1, "Method does not have exactly one argument: " + method); + return new MethodOption(method, classes[0]); + } + + private Method method; + private boolean isBoolean; + private Parser<?> parser; + + private MethodOption(Method method, Class<?> c) { + this.method = method; + this.isBoolean = c == boolean.class || c == Boolean.class; + try { + this.parser = Parsers.conventionalParser(Primitives.wrap(c)); + } catch (NoSuchMethodException e) { + throw new IllegalArgumentException("No suitable String-conversion method"); + } + + method.setAccessible(true); + } + + @Override boolean isBoolean() { + return isBoolean; + } + + @Override boolean delayedInjection() { + return true; + } + + @Override void inject(String valueText, Object injectee) throws InvalidCommandException { + invokeMethod(injectee, method, convert(parser, valueText)); + } + } + + private static Object convert(Parser<?> parser, String valueText) throws InvalidCommandException { + Object value; + try { + value = parser.parse(valueText); + } catch (ParseException e) { + throw new InvalidCommandException("wrong datatype: " + e.getMessage()); + } + return value; + } + + private void parseLongOption(String arg, Iterator<String> args) throws InvalidCommandException { + String name = arg.replaceFirst("^--no-", "--"); + String value = null; + + // Support "--name=value" as well as "--name value". + int equalsIndex = name.indexOf('='); + if (equalsIndex != -1) { + value = name.substring(equalsIndex + 1); + name = name.substring(0, equalsIndex); + } + + InjectableOption injectable = injectionMap.getInjectableOption(name); + + if (value == null) { + value = injectable.isBoolean() + ? Boolean.toString(!arg.startsWith("--no-")) + : grabNextValue(args, name); + } + injectNowOrLater(injectable, value); + } + + private void injectNowOrLater(InjectableOption injectable, String value) + throws InvalidCommandException { + if (injectable.delayedInjection()) { + pendingInjections.add(new PendingInjection(injectable, value)); + } else { + injectable.inject(value, injectee); + } + } + + private static class PendingInjection { + InjectableOption injectableOption; + String value; + + private PendingInjection(InjectableOption injectableOption, String value) { + this.injectableOption = injectableOption; + this.value = value; + } + } + + // Given boolean options a and b, and non-boolean option f, we want to allow: + // -ab + // -abf out.txt + // -abfout.txt + // (But not -abf=out.txt --- POSIX doesn't mention that either way, but GNU expressly forbids it.) + + private void parseShortOptions(String arg, Iterator<String> args) throws InvalidCommandException { + for (int i = 1; i < arg.length(); ++i) { + String name = "-" + arg.charAt(i); + InjectableOption injectable = injectionMap.getInjectableOption(name); + + String value; + if (injectable.isBoolean()) { + value = "true"; + } else { + // We need a value. If there's anything left, we take the rest of this "short option". + if (i + 1 < arg.length()) { + value = arg.substring(i + 1); + i = arg.length() - 1; // delayed "break" + + // otherwise the next arg + } else { + value = grabNextValue(args, name); + } + } + injectNowOrLater(injectable, value); + } + } + + private static void invokeMethod(Object injectee, Method method, Object value) + throws InvalidCommandException { + try { + method.invoke(injectee, value); + } catch (IllegalAccessException impossible) { + throw new AssertionError(impossible); + } catch (InvocationTargetException e) { + Throwable cause = e.getCause(); + Throwables.propagateIfPossible(cause, InvalidCommandException.class); + throw new RuntimeException(e); + } + } + + private String grabNextValue(Iterator<String> args, String name) + throws InvalidCommandException { + if (args.hasNext()) { + return args.next(); + } else { + throw new InvalidCommandException("option '" + name + "' requires an argument"); + } + } + + private static boolean isStaticOrAbstract(Method method) { + int modifiers = method.getModifiers(); + return Modifier.isStatic(modifiers) || Modifier.isAbstract(modifiers); + } +} diff --git a/caliper/src/main/java/com/google/caliper/options/OptionsModule.java b/caliper/src/main/java/com/google/caliper/options/OptionsModule.java new file mode 100644 index 0000000..9647475 --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/options/OptionsModule.java @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2012 Google Inc. + * + * 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.google.caliper.options; + +import com.google.caliper.util.InvalidCommandException; + +import dagger.Module; +import dagger.Provides; + +import java.io.File; + +import javax.inject.Singleton; + +/** + * Bindings for Caliper command line options. + */ +@Module +public final class OptionsModule { + + private static final String[] EMPTY_ARGS = new String[] {}; + + private final String[] args; + + private boolean requireBenchmarkClassName; + + /** + * Return a module that will provide access to configuration options and the name of the + * benchmark class. + * + * @param args the arguments from which the configuration options and the benchmark class name + * are parsed; must have one non-option value that is the benchmark class name. + */ + public static OptionsModule withBenchmarkClass(String [] args) { + return new OptionsModule(args, true); + } + + /** + * Return a module that will provide access to configuration options without the name of the + * benchmark class. + * + * @param args the arguments from which the configuration options are parsed; it must have no + * non-option values. + */ + public static OptionsModule withoutBenchmarkClass(String [] args) { + return new OptionsModule(args, false); + } + + /** + * Return a module that will provide access to the default configuration options. + */ + public static OptionsModule defaultOptionsModule() { + return new OptionsModule(EMPTY_ARGS, false); + } + + public OptionsModule(String[] args, boolean requireBenchmarkClassName) { + this.args = args.clone(); // defensive copy, just in case + this.requireBenchmarkClassName = requireBenchmarkClassName; + } + + @Provides + @Singleton + CaliperOptions provideOptions() throws InvalidCommandException { + return ParsedOptions.from(args, requireBenchmarkClassName); + } + + @Provides @CaliperDirectory static File provideCaliperDirectory(CaliperOptions options) { + return options.caliperDirectory(); + } +} diff --git a/caliper/src/main/java/com/google/caliper/options/ParsedOptions.java b/caliper/src/main/java/com/google/caliper/options/ParsedOptions.java new file mode 100644 index 0000000..43dd8f7 --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/options/ParsedOptions.java @@ -0,0 +1,417 @@ +/* + * Copyright (C) 2011 Google Inc. + * + * 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.google.caliper.options; + +import static com.google.common.base.Preconditions.checkNotNull; +import static java.util.concurrent.TimeUnit.MINUTES; + +import com.google.caliper.options.CommandLineParser.Leftovers; +import com.google.caliper.options.CommandLineParser.Option; +import com.google.caliper.util.InvalidCommandException; +import com.google.caliper.util.ShortDuration; +import com.google.common.base.Joiner; +import com.google.common.base.MoreObjects; +import com.google.common.base.Optional; +import com.google.common.base.Splitter; +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.ImmutableSetMultimap; +import com.google.common.collect.Maps; +import com.google.common.collect.Multimap; +import com.google.common.collect.Ordering; + +import java.io.File; +import java.util.List; +import java.util.Map; + +final class ParsedOptions implements CaliperOptions { + + public static ParsedOptions from(String[] args, boolean requireBenchmarkClassName) + throws InvalidCommandException { + ParsedOptions options = new ParsedOptions(requireBenchmarkClassName); + + CommandLineParser<ParsedOptions> parser = CommandLineParser.forClass(ParsedOptions.class); + try { + parser.parseAndInject(args, options); + } catch (InvalidCommandException e) { + e.setUsage(USAGE); + throw e; + } + return options; + } + + /** + * True if the benchmark class name is expected as the last argument, false if it is not allowed. + */ + private final boolean requireBenchmarkClassName; + + private ParsedOptions(boolean requireBenchmarkClassName) { + this.requireBenchmarkClassName = requireBenchmarkClassName; + } + + // -------------------------------------------------------------------------- + // Dry run -- simple boolean, needs to be checked in some methods + // -------------------------------------------------------------------------- + + @Option({"-n", "--dry-run"}) + private boolean dryRun; + + @Override public boolean dryRun() { + return dryRun; + } + + private void dryRunIncompatible(String optionName) throws InvalidCommandException { + // This only works because CLP does field injection before method injection + if (dryRun) { + throw new InvalidCommandException("Option not available in dry-run mode: " + optionName); + } + } + + // -------------------------------------------------------------------------- + // Delimiter -- injected early so methods can use it + // -------------------------------------------------------------------------- + + @Option({"-d", "--delimiter"}) + private String delimiter = ","; + + private ImmutableSet<String> split(String string) { + return ImmutableSet.copyOf(Splitter.on(delimiter).split(string)); + } + + // -------------------------------------------------------------------------- + // Benchmark method names to run + // -------------------------------------------------------------------------- + + private ImmutableSet<String> benchmarkNames = ImmutableSet.of(); + + @Option({"-b", "--benchmark"}) + private void setBenchmarkNames(String benchmarksString) { + benchmarkNames = split(benchmarksString); + } + + @Override public ImmutableSet<String> benchmarkMethodNames() { + return benchmarkNames; + } + + // -------------------------------------------------------------------------- + // Print configuration? + // -------------------------------------------------------------------------- + + @Option({"-p", "--print-config"}) + private boolean printConfiguration = false; + + @Override public boolean printConfiguration() { + return printConfiguration; + } + + // -------------------------------------------------------------------------- + // Trials + // -------------------------------------------------------------------------- + + private int trials = 1; + + @Option({"-t", "--trials"}) + private void setTrials(int trials) throws InvalidCommandException { + dryRunIncompatible("trials"); + if (trials < 1) { + throw new InvalidCommandException("trials must be at least 1: " + trials); + } + this.trials = trials; + } + + @Override public int trialsPerScenario() { + return trials; + } + + // -------------------------------------------------------------------------- + // Time limit + // -------------------------------------------------------------------------- + + private ShortDuration runTime = ShortDuration.of(5, MINUTES); + + @Option({"-l", "--time-limit"}) + private void setTimeLimit(String timeLimitString) throws InvalidCommandException { + try { + this.runTime = ShortDuration.valueOf(timeLimitString); + } catch (IllegalArgumentException e) { + throw new InvalidCommandException("Invalid time limit: " + timeLimitString); + } + } + + @Override public ShortDuration timeLimit() { + return runTime; + } + + // -------------------------------------------------------------------------- + // Run name + // -------------------------------------------------------------------------- + + private String runName = ""; + + @Option({"-r", "--run-name"}) + private void setRunName(String runName) { + this.runName = checkNotNull(runName); + } + + @Override public String runName() { + return runName; + } + + // -------------------------------------------------------------------------- + // VM specifications + // -------------------------------------------------------------------------- + + private ImmutableSet<String> vmNames = ImmutableSet.of(); + + @Option({"-m", "--vm"}) + private void setVms(String vmsString) throws InvalidCommandException { + dryRunIncompatible("vm"); + vmNames = split(vmsString); + } + + @Override public ImmutableSet<String> vmNames() { + return vmNames; + } + + // -------------------------------------------------------------------------- + // Measuring instruments to use + // -------------------------------------------------------------------------- + + private static final ImmutableSet<String> DEFAULT_INSTRUMENT_NAMES = + new ImmutableSet.Builder<String>() + .add("allocation") + .add("runtime") + .build(); + + private ImmutableSet<String> instrumentNames = DEFAULT_INSTRUMENT_NAMES; + + @Option({"-i", "--instrument"}) + private void setInstruments(String instrumentsString) { + instrumentNames = split(instrumentsString); + } + + @Override public ImmutableSet<String> instrumentNames() { + return instrumentNames; + } + +// -------------------------------------------------------------------------- + // Benchmark parameters + // -------------------------------------------------------------------------- + + private Multimap<String, String> mutableUserParameters = ArrayListMultimap.create(); + + @Option("-D") + private void addParameterSpec(String nameAndValues) throws InvalidCommandException { + addToMultimap(nameAndValues, mutableUserParameters); + } + + @Override public ImmutableSetMultimap<String, String> userParameters() { + // de-dup values, but keep in order + return new ImmutableSetMultimap.Builder<String, String>() + .orderKeysBy(Ordering.natural()) + .putAll(mutableUserParameters) + .build(); + } + + // -------------------------------------------------------------------------- + // VM arguments + // -------------------------------------------------------------------------- + + private Multimap<String, String> mutableVmArguments = ArrayListMultimap.create(); + + @Option("-J") + private void addVmArgumentsSpec(String nameAndValues) throws InvalidCommandException { + dryRunIncompatible("-J"); + addToMultimap(nameAndValues, mutableVmArguments); + } + + @Override public ImmutableSetMultimap<String, String> vmArguments() { + // de-dup values, but keep in order + return new ImmutableSetMultimap.Builder<String, String>() + .orderKeysBy(Ordering.natural()) + .putAll(mutableVmArguments) + .build(); + } + + // -------------------------------------------------------------------------- + // VM arguments + // -------------------------------------------------------------------------- + + private final Map<String, String> mutableConfigPropertes = Maps.newHashMap(); + + @Option("-C") + private void addConfigProperty(String nameAndValue) throws InvalidCommandException { + List<String> tokens = splitProperty(nameAndValue); + mutableConfigPropertes.put(tokens.get(0), tokens.get(1)); + } + + @Override public ImmutableMap<String, String> configProperties() { + return ImmutableMap.copyOf(mutableConfigPropertes); + } + + // -------------------------------------------------------------------------- + // Location of .caliper + // -------------------------------------------------------------------------- + + private File caliperDirectory = new File(System.getProperty("user.home"), ".caliper"); + + @Option({"--directory"}) + private void setCaliperDirectory(String path) { + caliperDirectory = new File(path); + } + + @Override public File caliperDirectory() { + return caliperDirectory; + } + + // -------------------------------------------------------------------------- + // Location of config.properties + // -------------------------------------------------------------------------- + + private Optional<File> caliperConfigFile = Optional.absent(); + + @Option({"-c", "--config"}) + private void setCaliperConfigFile(String filename) { + caliperConfigFile = Optional.of(new File(filename)); + } + + @Override public File caliperConfigFile() { + return caliperConfigFile.or(new File(caliperDirectory, "config.properties")); + } + + + // -------------------------------------------------------------------------- + // Leftover - benchmark class name + // -------------------------------------------------------------------------- + + private String benchmarkClassName; + + @Leftovers + private void setLeftovers(ImmutableList<String> leftovers) throws InvalidCommandException { + if (requireBenchmarkClassName) { + if (leftovers.isEmpty()) { + throw new InvalidCommandException("No benchmark class specified"); + } + if (leftovers.size() > 1) { + throw new InvalidCommandException("Extra stuff, expected only class name: " + leftovers); + } + this.benchmarkClassName = leftovers.get(0); + } else { + if (!leftovers.isEmpty()) { + throw new InvalidCommandException( + "Extra stuff, did not expect non-option arguments: " + leftovers); + } + } + } + + @Override public String benchmarkClassName() { + return benchmarkClassName; + } + + // -------------------------------------------------------------------------- + // Helper methods + // -------------------------------------------------------------------------- + + private static List<String> splitProperty(String propertyString) throws InvalidCommandException { + List<String> tokens = ImmutableList.copyOf(Splitter.on('=').limit(2).split(propertyString)); + if (tokens.size() != 2) { + throw new InvalidCommandException("no '=' found in: " + propertyString); + } + return tokens; + } + + private void addToMultimap(String nameAndValues, Multimap<String, String> multimap) + throws InvalidCommandException { + List<String> tokens = splitProperty(nameAndValues); + String name = tokens.get(0); + String values = tokens.get(1); + + if (multimap.containsKey(name)) { + throw new InvalidCommandException("multiple parameter sets for: " + name); + } + multimap.putAll(name, split(values)); + } + + @Override public String toString() { + return MoreObjects.toStringHelper(this) + .add("benchmarkClassName", this.benchmarkClassName()) + .add("benchmarkMethodNames", this.benchmarkMethodNames()) + .add("benchmarkParameters", this.userParameters()) + .add("dryRun", this.dryRun()) + .add("instrumentNames", this.instrumentNames()) + .add("vms", this.vmNames()) + .add("vmArguments", this.vmArguments()) + .add("trials", this.trialsPerScenario()) + .add("printConfig", this.printConfiguration()) + .add("delimiter", this.delimiter) + .add("caliperConfigFile", this.caliperConfigFile) + .toString(); + } + + // -------------------------------------------------------------------------- + // Usage + // -------------------------------------------------------------------------- + + // TODO(kevinb): kinda nice if CommandLineParser could autogenerate most of this... + // TODO(kevinb): a test could actually check that we don't exceed 79 columns. + private static final ImmutableList<String> USAGE = ImmutableList.of( + "Usage:", + " java com.google.caliper.runner.CaliperMain <benchmark_class_name> [options...]", + "", + "Options:", + " -h, --help print this message", + " -n, --dry-run instead of measuring, execute a single rep for each scenario", + " in-process", + " -b, --benchmark comma-separated list of benchmark methods to run; 'foo' is", + " an alias for 'timeFoo' (default: all found in class)", + " -m, --vm comma-separated list of VMs to test on; possible values are", + " configured in Caliper's configuration file (default:", + " whichever VM caliper itself is running in, only)", + " -i, --instrument comma-separated list of measuring instruments to use; possible ", + " values are configured in Caliper's configuration file ", + " (default: \"" + Joiner.on(",").join(DEFAULT_INSTRUMENT_NAMES) + "\")", + " -t, --trials number of independent trials to peform per benchmark scenario; ", + " a positive integer (default: 1)", + " -l, --time-limit maximum length of time allowed for a single trial; use 0 to allow ", + " trials to run indefinitely. (default: 30s) ", + " -r, --run-name a user-friendly string used to identify the run", + " -p, --print-config print the effective configuration that will be used by Caliper", + " -d, --delimiter separator used in options that take multiple values (default: ',')", + " -c, --config location of Caliper's configuration file (default:", + " $HOME/.caliper/config.properties)", + " --directory location of Caliper's configuration and data directory ", + " (default: $HOME/.caliper)", + "", + " -Dparam=val1,val2,...", + " Specifies the values to inject into the 'param' field of the benchmark", + " class; if multiple values or parameters are specified in this way, caliper", + " will try all possible combinations.", + "", + // commented out until this flag is fixed + // " -JdisplayName='vm arg list choice 1,vm arg list choice 2,...'", + // " Specifies alternate sets of VM arguments to pass. As with any variable,", + // " caliper will test all possible combinations. Example:", + // " -Jmemory='-Xms32m -Xmx32m,-Xms512m -Xmx512m'", + // "", + " -CconfigProperty=value", + " Specifies a value for any property that could otherwise be specified in ", + " $HOME/.caliper/config.properties. Properties specified on the command line", + " will override those specified in the file.", + "", + "See http://code.google.com/p/caliper/wiki/CommandLineOptions for more details.", + ""); +} diff --git a/caliper/src/main/java/com/google/caliper/options/package-info.java b/caliper/src/main/java/com/google/caliper/options/package-info.java new file mode 100644 index 0000000..b0bba1f --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/options/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2012 Google Inc. + * + * 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. + */ + +/** + * Contains code relating to parsing and managing command line options. This is distinct from + * configuration ({@code ~/.caliper/config.properties}), which lives in the + * {@link com.google.caliper.config} package. + */ +package com.google.caliper.options; diff --git a/caliper/src/main/java/com/google/caliper/platform/Platform.java b/caliper/src/main/java/com/google/caliper/platform/Platform.java new file mode 100644 index 0000000..44b6cd2 --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/platform/Platform.java @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2015 Google Inc. + * + * 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.google.caliper.platform; + +import static com.google.common.base.Preconditions.checkNotNull; + +import com.google.common.base.Predicate; +import com.google.common.collect.ImmutableSet; + +import java.io.File; +import java.util.Collection; +import java.util.Map; + +/** + * An abstraction of the platform within which caliper (both the scheduler and the actual workers) + * will run. + */ +public abstract class Platform { + + private final Platform.Type platformType; + + public Platform(Type platformType) { + this.platformType = checkNotNull(platformType); + } + + /** + * Get the executable for the virtual machine for this platform. + * + * @param vmHome the home directory of the virtual machine, allows testing across multiple vms on + * the same platform in one go. + */ + public abstract File vmExecutable(File vmHome); + + /** + * Additional virtual machine arguments common to all instruments that are passed to a worker. + */ + public abstract ImmutableSet<String> commonInstrumentVmArgs(); + + /** + * The name of the platform type. + */ + public String name() { + return platformType.name; + } + + /** + * Additional arguments that should be passed to a worker. + */ + public abstract ImmutableSet<String> workerProcessArgs(); + + /** + * The class path that should be used to run a worker.. + */ + public abstract String workerClassPath(); + + /** + * Checks to see whether the specific class is supported on this platform. + * + * <p>This checks to see whether {@link SupportedPlatform} specifies a {@link Type} that + * matches this platform. + * + * @param clazz the class to check. + * @return true if it is supported, false otherwise. + */ + public boolean supports(Class<?> clazz) { + SupportedPlatform annotation = clazz.getAnnotation(SupportedPlatform.class); + if (annotation == null) { + // Support must be explicitly declared. + return false; + } + + Platform.Type[] types = annotation.value(); + for (Type type : types) { + if (type.equals(platformType)) { + return true; + } + } + + return false; + } + + /** + * Get the input arguments for the current running JVM. + */ + public abstract Collection<String> inputArguments(); + + /** + * Selects the names of properties that will be used to populate the + * {@link com.google.caliper.model.VmSpec} for a specific run. + */ + public abstract Predicate<String> vmPropertiesToRetain(); + + /** + * Checks that the vm options are appropriate for this platform, throws an exception if they are + * not. + */ + public abstract void checkVmProperties(Map<String, String> options); + + /** + * Get the default vm home directory. + */ + public File defaultVmHomeDir() { + // Currently both supported platforms use java.home property to refer to the 'home' directory + // of the vm, in the case of Android it is the directory containing the dalvikvm executable. + return new File(System.getProperty("java.home")); + } + + /** + * Get the home directory of a custom virtual machine. + * @param vmGroupMap the configuration properties for all VMs, may contain default properties that + * apply to all VMs. + * @param vmConfigName the name of the VM within the configuration, used to access VM specific + * properties from the {@code vmGroupMap}. + * @throws VirtualMachineException if there was a problem with the VM, either the configuration + * or the file system. + */ + public abstract File customVmHomeDir(Map<String, String> vmGroupMap, String vmConfigName) + throws VirtualMachineException; + + /** + * The type of platforms supported. + */ + public enum Type { + DALVIK("Dalvik"), + JVM("Java"); + + private final String name; + + Type(String name) { + this.name = name; + } + } +} diff --git a/caliper/src/main/java/com/google/caliper/platform/SupportedPlatform.java b/caliper/src/main/java/com/google/caliper/platform/SupportedPlatform.java new file mode 100644 index 0000000..e3ac741 --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/platform/SupportedPlatform.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2015 Google Inc. + * + * 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.google.caliper.platform; + +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +/** + * Indicates the platforms supported by the annotated type. + */ +@Target(TYPE) +@Retention(RUNTIME) +@Documented +public @interface SupportedPlatform { + Platform.Type[] value(); +} diff --git a/caliper/src/main/java/com/google/caliper/platform/VirtualMachineException.java b/caliper/src/main/java/com/google/caliper/platform/VirtualMachineException.java new file mode 100644 index 0000000..b241212 --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/platform/VirtualMachineException.java @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2015 Google Inc. + * + * 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.google.caliper.platform; + +/** + * Thrown when a problem was found with a custom VM configuration. + */ +public class VirtualMachineException extends Exception { + + public VirtualMachineException(String message) { + super(message); + } +} diff --git a/caliper/src/main/java/com/google/caliper/platform/dalvik/DalvikModule.java b/caliper/src/main/java/com/google/caliper/platform/dalvik/DalvikModule.java new file mode 100644 index 0000000..45c5b00 --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/platform/dalvik/DalvikModule.java @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2015 Google Inc. + * + * 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.google.caliper.platform.dalvik; + +import com.google.common.base.Optional; + +import dagger.Module; +import dagger.Provides; + +import javax.inject.Singleton; + +/** + * Provider of a {@link DalvikPlatform} instance. + * + * <p>The {@link DalvikPlatform} is optional as it is only available when running on Android. + */ +@Module +public final class DalvikModule { + + @Provides + @Singleton + public static Optional<DalvikPlatform> provideOptionalPlatform() { + if (System.getProperty("java.specification.name").equals("Dalvik Core Library")) { + return Optional.of(new DalvikPlatform()); + } else { + return Optional.absent(); + } + } +} diff --git a/caliper/src/main/java/com/google/caliper/platform/dalvik/DalvikPlatform.java b/caliper/src/main/java/com/google/caliper/platform/dalvik/DalvikPlatform.java new file mode 100644 index 0000000..9a971d3 --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/platform/dalvik/DalvikPlatform.java @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2015 Google Inc. + * + * 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.google.caliper.platform.dalvik; + +import static com.google.common.base.Preconditions.checkState; + +import com.google.caliper.platform.Platform; +import com.google.common.base.Preconditions; +import com.google.common.base.Predicate; +import com.google.common.base.Predicates; +import com.google.common.collect.ImmutableSet; + +import java.io.File; +import java.util.Collection; +import java.util.Collections; +import java.util.Map; + +/** + * An abstraction of the Dalvik (aka Android) platform. + * + * <p>Although this talks about dalvik it actually works with ART too. + */ +public final class DalvikPlatform extends Platform { + + public DalvikPlatform() { + super(Type.DALVIK); + } + + @Override + public File vmExecutable(File vmHome) { + // TODO(user): Allow the 32/64 version of dalvik to be selected rather than the default + // standard configurations of Android systems and windows. + File bin = new File(vmHome, "bin"); + Preconditions.checkState(bin.exists() && bin.isDirectory(), + "Could not find %s under android root %s", bin, vmHome); + String executableName = "dalvikvm"; + File dalvikvm = new File(bin, executableName); + if (!dalvikvm.exists() || dalvikvm.isDirectory()) { + throw new IllegalStateException( + String.format("Cannot find %s binary in %s", executableName, bin)); + } + + return dalvikvm; + } + + @Override + public ImmutableSet<String> commonInstrumentVmArgs() { + return ImmutableSet.of(); + } + + @Override + public ImmutableSet<String> workerProcessArgs() { + return ImmutableSet.of(); + } + + @Override + public String workerClassPath() { + // TODO(user): Find a way to get the class path programmatically from the class loader. + return System.getProperty("java.class.path"); + } + + @Override + public Collection<String> inputArguments() { + return Collections.emptyList(); + } + + @Override + public Predicate<String> vmPropertiesToRetain() { + return Predicates.alwaysFalse(); + } + + @Override + public void checkVmProperties(Map<String, String> options) { + checkState(options.isEmpty()); + } + + @Override + public File customVmHomeDir(Map<String, String> vmGroupMap, String vmConfigName) { + // TODO(user): Should probably use this to support specifying dalvikvm32/dalvikvm64 + // and maybe even app_process. + throw new UnsupportedOperationException( + "Running with a custom Dalvik VM is not currently supported"); + } +} diff --git a/caliper/src/main/java/com/google/caliper/platform/jvm/EffectiveClassPath.java b/caliper/src/main/java/com/google/caliper/platform/jvm/EffectiveClassPath.java new file mode 100644 index 0000000..8963638 --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/platform/jvm/EffectiveClassPath.java @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2013 Google Inc. + * + * 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.google.caliper.platform.jvm; + +import com.google.common.base.Joiner; +import com.google.common.base.Splitter; +import com.google.common.collect.ImmutableSet; + +import java.io.File; +import java.net.URISyntaxException; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.LinkedHashSet; +import java.util.Set; + +import javax.annotation.Nullable; + +/** + * Provides a class path containing all of the jars present on the local machine that are referenced + * by a given {@link ClassLoader}. + */ +final class EffectiveClassPath { + private EffectiveClassPath() {} + + private static final String PATH_SEPARATOR = System.getProperty("path.separator"); + private static final String JAVA_CLASS_PATH = System.getProperty("java.class.path"); + + static String getClassPathForClassLoader(ClassLoader classLoader) { + // Order of JAR files may have some significance. Try to preserve it. + LinkedHashSet<File> files = new LinkedHashSet<File>(); + for (String entry : Splitter.on(PATH_SEPARATOR).split(JAVA_CLASS_PATH)) { + files.add(new File(entry)); + } + files.addAll(getClassPathFiles(classLoader)); + + return Joiner.on(PATH_SEPARATOR).join(files); + } + + private static Set<File> getClassPathFiles(ClassLoader classLoader) { + ImmutableSet.Builder<File> files = ImmutableSet.builder(); + @Nullable ClassLoader parent = classLoader.getParent(); + if (parent != null) { + files.addAll(getClassPathFiles(parent)); + } + if (classLoader instanceof URLClassLoader) { + URLClassLoader urlClassLoader = (URLClassLoader) classLoader; + for (URL url : urlClassLoader.getURLs()) { + try { + files.add(new File(url.toURI())); + } catch (URISyntaxException e) { + // skip it then + } catch (IllegalArgumentException e) { + // skip it then + } + } + } + return files.build(); + } +} diff --git a/caliper/src/main/java/com/google/caliper/ConfigurationException.java b/caliper/src/main/java/com/google/caliper/platform/jvm/JvmModule.java index c4a35ec..6490374 100644 --- a/caliper/src/main/java/com/google/caliper/ConfigurationException.java +++ b/caliper/src/main/java/com/google/caliper/platform/jvm/JvmModule.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009 Google Inc. + * Copyright (C) 2015 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,20 +14,19 @@ * limitations under the License. */ -package com.google.caliper; +package com.google.caliper.platform.jvm; + +import dagger.Module; +import dagger.Provides; /** - * Thrown upon occurrence of a configuration error. + * Provider of the default {@link JvmPlatform}, this is assumed to always be available. */ -final class ConfigurationException extends RuntimeException { - - ConfigurationException(String s) { - super(s); - } +@Module +public final class JvmModule { - ConfigurationException(Throwable cause) { - super(cause); + @Provides + public static JvmPlatform provideJvmPlatform() { + return new JvmPlatform(); } - - private static final long serialVersionUID = 0; } diff --git a/caliper/src/main/java/com/google/caliper/platform/jvm/JvmPlatform.java b/caliper/src/main/java/com/google/caliper/platform/jvm/JvmPlatform.java new file mode 100644 index 0000000..549cdd9 --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/platform/jvm/JvmPlatform.java @@ -0,0 +1,208 @@ +/* + * Copyright (C) 2015 Google Inc. + * + * 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.google.caliper.platform.jvm; + +import static com.google.common.base.Preconditions.checkState; +import static java.lang.Thread.currentThread; + +import com.google.caliper.platform.Platform; +import com.google.caliper.platform.VirtualMachineException; +import com.google.caliper.util.Util; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Preconditions; +import com.google.common.base.Predicate; +import com.google.common.collect.Collections2; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; + +import java.io.File; +import java.lang.management.ManagementFactory; +import java.util.Collection; +import java.util.Map; + +import javax.annotation.Nullable; + +/** + * An abstraction of a standard Java Virtual Machine platform. + */ +public final class JvmPlatform extends Platform { + + /** + * Some default JVM args to keep worker VMs somewhat predictable. + */ + @VisibleForTesting + public static final ImmutableSet<String> INSTRUMENT_JVM_ARGS = ImmutableSet.of( + // do compilation serially + "-Xbatch", + // make sure compilation doesn't run in parallel with itself + "-XX:CICompilerCount=1", + // ensure the parallel garbage collector + "-XX:+UseParallelGC", + // generate classes or don't, but do it immediately + "-Dsun.reflect.inflationThreshold=0"); + + private static final ImmutableSet<String> WORKER_PROCESS_ARGS = ImmutableSet.of( + "-XX:+PrintFlagsFinal", + "-XX:+PrintCompilation", + "-XX:+PrintGC"); + + + private static final Predicate<String> PROPERTIES_TO_RETAIN = new Predicate<String>() { + @Override public boolean apply(String input) { + return input.startsWith("java.vm") + || input.startsWith("java.runtime") + || input.equals("java.version") + || input.equals("java.vendor") + || input.equals("sun.reflect.noInflation") + || input.equals("sun.reflect.inflationThreshold"); + } + }; + + public JvmPlatform() { + super(Type.JVM); + } + + @Override + public File vmExecutable(File javaHome) { + // TODO(gak): support other platforms. This currently supports finding the java executable on + // standard configurations of unix systems and windows. + File bin = new File(javaHome, "bin"); + Preconditions.checkState(bin.exists() && bin.isDirectory(), + "Could not find %s under java home %s", bin, javaHome); + File jvm = new File(bin, "java"); + if (!jvm.exists() || jvm.isDirectory()) { + jvm = new File(bin, "java.exe"); + if (!jvm.exists() || jvm.isDirectory()) { + throw new IllegalStateException( + String.format("Cannot find java binary in %s, looked for java and java.exe", bin)); + } + } + + return jvm; + } + + @Override + public ImmutableSet<String> commonInstrumentVmArgs() { + return INSTRUMENT_JVM_ARGS; + } + + @Override + public ImmutableSet<String> workerProcessArgs() { + return WORKER_PROCESS_ARGS; + } + + @Override + public String workerClassPath() { + return getClassPath(); + } + + private static String getClassPath() { + // Use the effective class path in case this is being invoked in an isolated class loader + String classpath = + EffectiveClassPath.getClassPathForClassLoader(currentThread().getContextClassLoader()); + return classpath; + } + + @Override + public Collection<String> inputArguments() { + return Collections2.filter(ManagementFactory.getRuntimeMXBean().getInputArguments(), + new Predicate<String>() { + @Override + public boolean apply(String input) { + // Exclude the -agentlib:jdwp param which configures the socket debugging protocol. + // If this is set in the parent VM we do not want it to be inherited by the child + // VM. If it is, the child will die immediately on startup because it will fail to + // bind to the debug port (because the parent VM is already bound to it). + return !input.startsWith("-agentlib:jdwp"); + } + }); + } + + @Override + public Predicate<String> vmPropertiesToRetain() { + return PROPERTIES_TO_RETAIN; + } + + @Override + public void checkVmProperties(Map<String, String> options) { + checkState(!options.isEmpty()); + } + + @Override + public File customVmHomeDir(Map<String, String> vmGroupMap, String vmConfigName) + throws VirtualMachineException { + // Configuration can either be: + // vm.<vmConfigName>.home = <homeDir> + // or + // vm.baseDirectory = <baseDir> + // homeDir = <baseDir>/<vmConfigName> + ImmutableMap<String, String> vmMap = Util.subgroupMap(vmGroupMap, vmConfigName); + return getJdkHomeDir(vmGroupMap.get("baseDirectory"), vmMap.get("home"), vmConfigName); + } + + // TODO(gak): check that the directory seems to be a jdk home (with a java binary and all of that) + // TODO(gak): make this work with different directory layouts. I'm looking at you OS X... + public static File getJdkHomeDir(@Nullable String baseDirectoryPath, + @Nullable String homeDirPath, String vmConfigName) + throws VirtualMachineException { + if (homeDirPath == null) { + File baseDirectory = getBaseDirectory(baseDirectoryPath); + File homeDir = new File(baseDirectory, vmConfigName); + checkConfiguration(homeDir.isDirectory(), "%s is not a directory", homeDir); + return homeDir; + } else { + File potentialHomeDir = new File(homeDirPath); + if (potentialHomeDir.isAbsolute()) { + checkConfiguration(potentialHomeDir.isDirectory(), "%s is not a directory", + potentialHomeDir); + return potentialHomeDir; + } else { + File baseDirectory = getBaseDirectory(baseDirectoryPath); + File homeDir = new File(baseDirectory, homeDirPath); + checkConfiguration(homeDir.isDirectory(), "%s is not a directory", potentialHomeDir); + return homeDir; + } + } + } + + private static File getBaseDirectory(@Nullable String baseDirectoryPath) + throws VirtualMachineException { + if (baseDirectoryPath == null) { + throw new VirtualMachineException( + "must set either a home directory or a base directory"); + } else { + File baseDirectory = new File(baseDirectoryPath); + checkConfiguration(baseDirectory.isAbsolute(), "base directory cannot be a relative path"); + checkConfiguration(baseDirectory.isDirectory(), "base directory must be a directory"); + return baseDirectory; + } + } + + private static void checkConfiguration(boolean check, String message) + throws VirtualMachineException { + if (!check) { + throw new VirtualMachineException(message); + } + } + + private static void checkConfiguration(boolean check, String messageFormat, Object... args) + throws VirtualMachineException { + if (!check) { + throw new VirtualMachineException(String.format(messageFormat, args)); + } + } +} diff --git a/caliper/src/main/java/com/google/caliper/runner/AllocationInstrument.java b/caliper/src/main/java/com/google/caliper/runner/AllocationInstrument.java new file mode 100644 index 0000000..7469117 --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/runner/AllocationInstrument.java @@ -0,0 +1,229 @@ +/* + * Copyright (C) 2011 Google Inc. + * + * 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.google.caliper.runner; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Throwables.propagateIfInstanceOf; +import static java.util.logging.Level.SEVERE; + +import com.google.caliper.Benchmark; +import com.google.caliper.api.SkipThisScenarioException; +import com.google.caliper.config.VmConfig; +import com.google.caliper.platform.Platform; +import com.google.caliper.platform.SupportedPlatform; +import com.google.caliper.worker.MacrobenchmarkAllocationWorker; +import com.google.caliper.worker.MicrobenchmarkAllocationWorker; +import com.google.caliper.worker.Worker; +import com.google.common.base.Optional; +import com.google.common.base.Strings; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import com.google.monitoring.runtime.instrumentation.AllocationInstrumenter; + +import java.io.File; +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.jar.JarFile; +import java.util.jar.Manifest; +import java.util.logging.Logger; + +/** + * {@link Instrument} that watches the memory allocations in an invocation of the + * benchmark method and reports some statistic. The benchmark method must accept a + * single int argument 'reps', which is the number of times to execute the guts of + * the benchmark method, and it must be public and non-static. + * + * <p>Note that the allocation instruments reports a "worst case" for allocation in that it reports + * the bytes and objects allocated in interpreted mode (no JIT). + */ +@SupportedPlatform(Platform.Type.JVM) +public final class AllocationInstrument extends Instrument { + private static final String ALLOCATION_AGENT_JAR_OPTION = "allocationAgentJar"; + /** + * If this option is set to {@code true} then every individual allocation will be tracked and + * logged. This will also increase the detail of certain error messages. + */ + private static final String TRACK_ALLOCATIONS_OPTION = "trackAllocations"; + private static final Logger logger = Logger.getLogger(AllocationInstrument.class.getName()); + + @Override + public boolean isBenchmarkMethod(Method method) { + return method.isAnnotationPresent(Benchmark.class) || BenchmarkMethods.isTimeMethod(method); + } + + @Override + public Instrumentation createInstrumentation(Method benchmarkMethod) + throws InvalidBenchmarkException { + checkNotNull(benchmarkMethod); + checkArgument(isBenchmarkMethod(benchmarkMethod)); + try { + switch (BenchmarkMethods.Type.of(benchmarkMethod)) { + case MACRO: + return new MacroAllocationInstrumentation(benchmarkMethod); + case MICRO: + case PICO: + return new MicroAllocationInstrumentation(benchmarkMethod); + default: + throw new AssertionError("unknown type"); + } + } catch (IllegalArgumentException e) { + throw new InvalidBenchmarkException("Benchmark methods must have no arguments or accept " + + "a single int or long parameter: %s", benchmarkMethod.getName()); + } + } + + private final class MicroAllocationInstrumentation extends Instrumentation { + MicroAllocationInstrumentation(Method benchmarkMethod) { + super(benchmarkMethod); + } + + @Override + public void dryRun(Object benchmark) throws UserCodeException { + // execute the benchmark method, but don't try to take any measurements, because this JVM + // may not have the allocation instrumenter agent. + try { + benchmarkMethod.invoke(benchmark, 1); + } catch (IllegalAccessException impossible) { + throw new AssertionError(impossible); + } catch (InvocationTargetException e) { + Throwable userException = e.getCause(); + propagateIfInstanceOf(userException, SkipThisScenarioException.class); + throw new UserCodeException(userException); + } + } + + @Override public ImmutableMap<String, String> workerOptions() { + return ImmutableMap.of(TRACK_ALLOCATIONS_OPTION, options.get(TRACK_ALLOCATIONS_OPTION)); + } + + @Override + public Class<? extends Worker> workerClass() { + return MicrobenchmarkAllocationWorker.class; + } + + @Override + MeasurementCollectingVisitor getMeasurementCollectingVisitor() { + return new Instrument.DefaultMeasurementCollectingVisitor( + ImmutableSet.of("bytes", "objects")); + } + } + + @Override public TrialSchedulingPolicy schedulingPolicy() { + // Assuming there is enough memory it should be fine to run these in parallel. + return TrialSchedulingPolicy.PARALLEL; + } + + private final class MacroAllocationInstrumentation extends Instrumentation { + MacroAllocationInstrumentation(Method benchmarkMethod) { + super(benchmarkMethod); + } + + @Override + public void dryRun(Object benchmark) throws InvalidBenchmarkException { + // execute the benchmark method, but don't try to take any measurements, because this JVM + // may not have the allocation instrumenter agent. + try { + benchmarkMethod.invoke(benchmark); + } catch (IllegalAccessException impossible) { + throw new AssertionError(impossible); + } catch (InvocationTargetException e) { + Throwable userException = e.getCause(); + propagateIfInstanceOf(userException, SkipThisScenarioException.class); + throw new UserCodeException(userException); + } + } + + @Override public ImmutableMap<String, String> workerOptions() { + return ImmutableMap.of(TRACK_ALLOCATIONS_OPTION, options.get(TRACK_ALLOCATIONS_OPTION)); + } + + @Override + public Class<? extends Worker> workerClass() { + return MacrobenchmarkAllocationWorker.class; + } + + @Override + MeasurementCollectingVisitor getMeasurementCollectingVisitor() { + return new Instrument.DefaultMeasurementCollectingVisitor( + ImmutableSet.of("bytes", "objects")); + } + } + + @Override + public ImmutableSet<String> instrumentOptions() { + return ImmutableSet.of(ALLOCATION_AGENT_JAR_OPTION, TRACK_ALLOCATIONS_OPTION); + } + + private static Optional<File> findAllocationInstrumentJarOnClasspath() throws IOException { + ImmutableSet<File> jarFiles = JarFinder.findJarFiles( + Thread.currentThread().getContextClassLoader(), + ClassLoader.getSystemClassLoader()); + for (File file : jarFiles) { + JarFile jarFile = null; + try { + jarFile = new JarFile(file); + Manifest manifest = jarFile.getManifest(); + if ((manifest != null) + && AllocationInstrumenter.class.getName().equals( + manifest.getMainAttributes().getValue("Premain-Class"))) { + return Optional.of(file); + } + } finally { + if (jarFile != null) { + jarFile.close(); + } + } + } + return Optional.absent(); + } + + /** + * This instrument's worker requires the allocationinstrumenter agent jar, specified + * on the worker VM's command line with "-javaagent:[jarfile]". + */ + @Override ImmutableSet<String> getExtraCommandLineArgs(VmConfig vmConfig) { + String agentJar = options.get(ALLOCATION_AGENT_JAR_OPTION); + if (Strings.isNullOrEmpty(agentJar)) { + try { + Optional<File> instrumentJar = findAllocationInstrumentJarOnClasspath(); + // TODO(gak): bundle up the allocation jar and unpack it if it's not on the classpath + if (instrumentJar.isPresent()) { + agentJar = instrumentJar.get().getAbsolutePath(); + } + } catch (IOException e) { + logger.log(SEVERE, + "An exception occurred trying to locate the allocation agent jar on the classpath", e); + } + } + if (Strings.isNullOrEmpty(agentJar) || !new File(agentJar).exists()) { + throw new IllegalStateException("Can't find required allocationinstrumenter agent jar"); + } + // Add microbenchmark args to minimize differences in the output + return new ImmutableSet.Builder<String>() + .addAll(super.getExtraCommandLineArgs(vmConfig)) + // we just run in interpreted mode to ensure that intrinsics don't break the instrumentation + .add("-Xint") + .add("-javaagent:" + agentJar) + // Some environments rename files and use symlinks to improve resource caching, + // if the agent jar path is actually a symlink it will prevent the agent from finding itself + // and adding itself to the bootclasspath, so we do it manually here. + .add("-Xbootclasspath/a:" + agentJar) + .build(); + } +} diff --git a/caliper/src/main/java/com/google/caliper/runner/ArbitraryMeasurementInstrument.java b/caliper/src/main/java/com/google/caliper/runner/ArbitraryMeasurementInstrument.java new file mode 100644 index 0000000..2845cd8 --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/runner/ArbitraryMeasurementInstrument.java @@ -0,0 +1,152 @@ +/* + * Copyright (C) 2011 Google Inc. + * + * 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.google.caliper.runner; + +import static com.google.caliper.runner.CommonInstrumentOptions.GC_BEFORE_EACH_OPTION; +import static com.google.common.base.Throwables.propagateIfInstanceOf; + +import com.google.caliper.api.SkipThisScenarioException; +import com.google.caliper.bridge.AbstractLogMessageVisitor; +import com.google.caliper.bridge.StopMeasurementLogMessage; +import com.google.caliper.model.ArbitraryMeasurement; +import com.google.caliper.model.Measurement; +import com.google.caliper.platform.Platform; +import com.google.caliper.platform.SupportedPlatform; +import com.google.caliper.util.Util; +import com.google.caliper.worker.ArbitraryMeasurementWorker; +import com.google.caliper.worker.Worker; +import com.google.common.base.Optional; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Iterables; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +/** + * Instrument for taking an arbitrary measurement. When using this instrument, the benchmark code + * itself returns the value. See {@link ArbitraryMeasurement}. + */ +@SupportedPlatform(Platform.Type.JVM) +public final class ArbitraryMeasurementInstrument extends Instrument { + @Override public boolean isBenchmarkMethod(Method method) { + return method.isAnnotationPresent(ArbitraryMeasurement.class); + } + + @Override + public Instrumentation createInstrumentation(Method benchmarkMethod) + throws InvalidBenchmarkException { + if (benchmarkMethod.getParameterTypes().length != 0) { + throw new InvalidBenchmarkException( + "Arbitrary measurement methods should take no parameters: " + benchmarkMethod.getName()); + } + + if (benchmarkMethod.getReturnType() != double.class) { + throw new InvalidBenchmarkException( + "Arbitrary measurement methods must have a return type of double: " + + benchmarkMethod.getName()); + } + + // Static technically doesn't hurt anything, but it's just the completely wrong idea + if (Util.isStatic(benchmarkMethod)) { + throw new InvalidBenchmarkException( + "Arbitrary measurement methods must not be static: " + benchmarkMethod.getName()); + } + + if (!Util.isPublic(benchmarkMethod)) { + throw new InvalidBenchmarkException( + "Arbitrary measurement methods must be public: " + benchmarkMethod.getName()); + } + + return new ArbitraryMeasurementInstrumentation(benchmarkMethod); + } + + @Override public TrialSchedulingPolicy schedulingPolicy() { + // We could allow it here but in general it would depend on the particular measurement so it + // should probably be configured by the user. For now we just disable it. + return TrialSchedulingPolicy.SERIAL; + } + + private final class ArbitraryMeasurementInstrumentation extends Instrumentation { + protected ArbitraryMeasurementInstrumentation(Method benchmarkMethod) { + super(benchmarkMethod); + } + + @Override + public void dryRun(Object benchmark) throws InvalidBenchmarkException { + try { + benchmarkMethod.invoke(benchmark); + } catch (IllegalAccessException impossible) { + throw new AssertionError(impossible); + } catch (InvocationTargetException e) { + Throwable userException = e.getCause(); + propagateIfInstanceOf(userException, SkipThisScenarioException.class); + throw new UserCodeException(userException); + } + } + + @Override + public Class<? extends Worker> workerClass() { + return ArbitraryMeasurementWorker.class; + } + + @Override public ImmutableMap<String, String> workerOptions() { + return ImmutableMap.of(GC_BEFORE_EACH_OPTION, options.get(GC_BEFORE_EACH_OPTION)); + } + + @Override + MeasurementCollectingVisitor getMeasurementCollectingVisitor() { + return new SingleMeasurementCollectingVisitor(); + } + } + + @Override + public ImmutableSet<String> instrumentOptions() { + return ImmutableSet.of(GC_BEFORE_EACH_OPTION); + } + + private static final class SingleMeasurementCollectingVisitor extends AbstractLogMessageVisitor + implements MeasurementCollectingVisitor { + Optional<Measurement> measurement = Optional.absent(); + + @Override + public boolean isDoneCollecting() { + return measurement.isPresent(); + } + + @Override + public boolean isWarmupComplete() { + return true; + } + + @Override + public ImmutableList<Measurement> getMeasurements() { + return ImmutableList.copyOf(measurement.asSet()); + } + + @Override + public void visit(StopMeasurementLogMessage logMessage) { + this.measurement = Optional.of(Iterables.getOnlyElement(logMessage.measurements())); + } + + @Override + public ImmutableList<String> getMessages() { + return ImmutableList.of(); + } + } +} diff --git a/caliper/src/main/java/com/google/caliper/runner/BenchmarkClass.java b/caliper/src/main/java/com/google/caliper/runner/BenchmarkClass.java new file mode 100644 index 0000000..2aa6e1a --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/runner/BenchmarkClass.java @@ -0,0 +1,199 @@ +/* + * Copyright (C) 2011 Google Inc. + * + * 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.google.caliper.runner; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Throwables.propagateIfInstanceOf; + +import com.google.caliper.AfterExperiment; +import com.google.caliper.BeforeExperiment; +import com.google.caliper.Param; +import com.google.caliper.api.SkipThisScenarioException; +import com.google.caliper.api.VmOptions; +import com.google.caliper.util.InvalidCommandException; +import com.google.caliper.util.Reflection; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Objects; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.ImmutableSetMultimap; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * An instance of this type represents a user-provided class. It manages creating, setting up and + * destroying instances of that class. + */ +final class BenchmarkClass { + private static final Logger logger = Logger.getLogger(BenchmarkClass.class.getName()); + + static BenchmarkClass forClass(Class<?> theClass) throws InvalidBenchmarkException { + return new BenchmarkClass(theClass); + } + + final Class<?> theClass; + private final ParameterSet userParameters; + private final ImmutableSet<String> benchmarkFlags; + + private BenchmarkClass(Class<?> theClass) throws InvalidBenchmarkException { + this.theClass = checkNotNull(theClass); + + if (!theClass.getSuperclass().equals(Object.class)) { + throw new InvalidBenchmarkException( + "%s must not extend any class other than %s. Prefer composition.", + theClass, Object.class); + } + + if (Modifier.isAbstract(theClass.getModifiers())) { + throw new InvalidBenchmarkException("Class '%s' is abstract", theClass); + } + + // TODO: check for nested, non-static classes (non-abstract, but no constructor?) + // this will fail later anyway (no way to declare parameterless nested constr., but + // maybe signal this better? + + this.userParameters = ParameterSet.create(theClass, Param.class); + + this.benchmarkFlags = getVmOptions(theClass); + } + + ImmutableSet<Method> beforeExperimentMethods() { + return Reflection.getAnnotatedMethods(theClass, BeforeExperiment.class); + } + + ImmutableSet<Method> afterExperimentMethods() { + return Reflection.getAnnotatedMethods(theClass, AfterExperiment.class); + } + + public ParameterSet userParameters() { + return userParameters; + } + + public ImmutableSet<String> vmOptions() { + return benchmarkFlags; + } + + // TODO(gak): use these methods in the worker as well + public void setUpBenchmark(Object benchmarkInstance) throws UserCodeException { + boolean setupSuccess = false; + try { + callSetUp(benchmarkInstance); + setupSuccess = true; + } finally { + // If setUp fails, we should call tearDown. If this method throws an exception, we + // need to call tearDown from here, because no one else has the reference to the + // Benchmark. + if (!setupSuccess) { + try { + callTearDown(benchmarkInstance); + } catch (UserCodeException e) { + // The exception thrown during setUp shouldn't be lost, as it's probably more + // important to the user. + logger.log( + Level.INFO, + "in @AfterExperiment methods called because @BeforeExperiment methods failed", + e); + } + } + } + } + + public void cleanup(Object benchmark) throws UserCodeException { + callTearDown(benchmark); + } + + @VisibleForTesting Class<?> benchmarkClass() { + return theClass; + } + + public String name() { + return theClass.getName(); + } + + @Override public boolean equals(Object obj) { + if (obj == this) { + return true; + } else if (obj instanceof BenchmarkClass) { + BenchmarkClass that = (BenchmarkClass) obj; + return this.theClass.equals(that.theClass); + } else { + return false; + } + } + + @Override public int hashCode() { + return Objects.hashCode(theClass); + } + + @Override public String toString() { + return name(); + } + + private void callSetUp(Object benchmark) throws UserCodeException { + for (Method method : beforeExperimentMethods()) { + try { + method.invoke(benchmark); + } catch (IllegalAccessException e) { + throw new AssertionError(e); + } catch (InvocationTargetException e) { + propagateIfInstanceOf(e.getCause(), SkipThisScenarioException.class); + throw new UserCodeException( + "Exception thrown from a @BeforeExperiment method", e.getCause()); + } + } + } + + private void callTearDown(Object benchmark) throws UserCodeException { + for (Method method : afterExperimentMethods()) { + try { + method.invoke(benchmark); + } catch (IllegalAccessException e) { + throw new AssertionError(e); + } catch (InvocationTargetException e) { + propagateIfInstanceOf(e.getCause(), SkipThisScenarioException.class); + throw new UserCodeException( + "Exception thrown from an @AfterExperiment method", e.getCause()); + } + } + } + + private static ImmutableSet<String> getVmOptions(Class<?> benchmarkClass) { + VmOptions annotation = benchmarkClass.getAnnotation(VmOptions.class); + return (annotation == null) + ? ImmutableSet.<String>of() + : ImmutableSet.copyOf(annotation.value()); + } + + void validateParameters(ImmutableSetMultimap<String, String> parameters) + throws InvalidCommandException { + for (String paramName : parameters.keySet()) { + Parameter parameter = userParameters.get(paramName); + if (parameter == null) { + throw new InvalidCommandException("unrecognized parameter: " + paramName); + } + try { + parameter.validate(parameters.get(paramName)); + } catch (InvalidBenchmarkException e) { + // TODO(kevinb): this is weird. + throw new InvalidCommandException(e.getMessage()); + } + } + } +} diff --git a/caliper/src/main/java/com/google/caliper/runner/BenchmarkClassChecker.java b/caliper/src/main/java/com/google/caliper/runner/BenchmarkClassChecker.java new file mode 100644 index 0000000..a7c777d --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/runner/BenchmarkClassChecker.java @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2015 Google Inc. + * + * 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.google.caliper.runner; + +import com.google.caliper.config.ConfigModule; +import com.google.caliper.options.OptionsModule; +import com.google.caliper.util.OutputModule; +import com.google.common.collect.ImmutableSet; + +import dagger.Component; + +import java.io.PrintWriter; +import java.lang.reflect.Method; +import java.util.List; + +import javax.inject.Singleton; + +/** + * Determines whether a class contains one or more benchmarks or not. + * + * <p>Useful for tools that need to check whether a class contains benchmarks before running them + * by calling an appropriate method on {@link CaliperMain}. + */ +// This should be considered part of the public API alongside {@link CaliperMain}. +public final class BenchmarkClassChecker { + + /** + * Create a new instance of {@link BenchmarkClassChecker}. + * + * @param arguments a list of command line arguments for Caliper, can include any of the + * options supported by Caliper. + * @return a new instance of {@link BenchmarkClassChecker}. + */ + public static BenchmarkClassChecker create(List<String> arguments) { + return new BenchmarkClassChecker(arguments); + } + + /** + * The set of {@link Instrument instruments} that are used to determine whether a class has any + * methods suitable for benchmarking. + */ + private final ImmutableSet<Instrument> instruments; + + private BenchmarkClassChecker(List<String> arguments) { + String[] args = arguments.toArray(new String[arguments.size()]); + InstrumentProvider instrumentProvider = DaggerBenchmarkClassChecker_InstrumentProvider.builder() + .optionsModule(OptionsModule.withoutBenchmarkClass(args)) + .outputModule(new OutputModule(new PrintWriter(System.out), new PrintWriter(System.err))) + .build(); + + instruments = instrumentProvider.instruments(); + } + + /** + * Check to see whether the supplied class contains at least one benchmark method that can be run + * by caliper. + * + * @param theClass the class that may contain one or more benchmark methods. + * @return true if the class does contain a benchmark method, false otherwise. + */ + public boolean isBenchmark(Class<?> theClass) { + for (Method method : theClass.getDeclaredMethods()) { + for (Instrument instrument : instruments) { + if (instrument.isBenchmarkMethod(method)) { + return true; + } + } + } + + return false; + } + + @Singleton + @Component(modules = { + ConfigModule.class, + ExperimentingRunnerModule.class, + OptionsModule.class, + OutputModule.class, + PlatformModule.class, + RunnerModule.class, + }) + /** + * Provides the set of supported {@link Instrument instruments}. + */ + interface InstrumentProvider { + ImmutableSet<Instrument> instruments(); + } +} diff --git a/caliper/src/main/java/com/google/caliper/runner/BenchmarkClassModule.java b/caliper/src/main/java/com/google/caliper/runner/BenchmarkClassModule.java new file mode 100644 index 0000000..35b76be --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/runner/BenchmarkClassModule.java @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2013 Google Inc. + * + * 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.google.caliper.runner; + +import com.google.caliper.runner.Running.AfterExperimentMethods; +import com.google.caliper.runner.Running.BeforeExperimentMethods; +import com.google.common.collect.ImmutableSet; +import dagger.Module; +import dagger.Provides; + +import java.lang.reflect.Method; +import javax.inject.Singleton; + +/** + * Binds objects related to a benchmark class. + */ +// TODO(gak): move more of benchmark class into this module +@Module +public final class BenchmarkClassModule { + + @Provides + @Singleton + static BenchmarkClass provideBenchmarkClass(@Running.BenchmarkClass Class<?> benchmarkClassObject) + throws InvalidBenchmarkException { + return BenchmarkClass.forClass(benchmarkClassObject); + } + + @Provides + @BeforeExperimentMethods + static ImmutableSet<Method> provideBeforeExperimentMethods( + BenchmarkClass benchmarkClass) { + return benchmarkClass.beforeExperimentMethods(); + } + + @Provides + @AfterExperimentMethods + static ImmutableSet<Method> provideAfterExperimentMethods( + BenchmarkClass benchmarkClass) { + return benchmarkClass.afterExperimentMethods(); + } +} diff --git a/caliper/src/main/java/com/google/caliper/runner/BenchmarkCreator.java b/caliper/src/main/java/com/google/caliper/runner/BenchmarkCreator.java new file mode 100644 index 0000000..548d272 --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/runner/BenchmarkCreator.java @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2015 Google Inc. + * + * 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.google.caliper.runner; + +import com.google.caliper.Param; +import com.google.caliper.util.Parser; +import com.google.caliper.util.Parsers; +import com.google.common.collect.ImmutableSortedMap; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.text.ParseException; + +import javax.inject.Inject; + +/** + * Responsible for creating instances of the benchmark class. + */ +final class BenchmarkCreator { + + private static final String BENCHMARK_NO_PUBLIC_DEFAULT_CONSTRUCTOR = + "Benchmark %s does not have a publicly visible default constructor"; + + private final Class<?> benchmarkClass; + private final ImmutableSortedMap<String, String> parameters; + private final Constructor<?> benchmarkClassCtor; + + @Inject + BenchmarkCreator( + @Running.BenchmarkClass Class<?> benchmarkClass, + @Running.Benchmark ImmutableSortedMap<String, String> parameters) { + this.benchmarkClass = benchmarkClass; + this.benchmarkClassCtor = findDefaultConstructor(benchmarkClass); + this.parameters = parameters; + } + + private static Constructor<?> findDefaultConstructor(Class<?> benchmarkClass) { + Constructor<?> defaultConstructor = null; + for (Constructor<?> constructor : benchmarkClass.getDeclaredConstructors()) { + if (constructor.getParameterTypes().length == 0) { + defaultConstructor = constructor; + defaultConstructor.setAccessible(true); + break; + } + } + if (defaultConstructor == null) { + throw new UserCodeException( + String.format(BENCHMARK_NO_PUBLIC_DEFAULT_CONSTRUCTOR, benchmarkClass), null); + } + return defaultConstructor; + } + + Object createBenchmarkInstance() { + Object instance; + try { + instance = benchmarkClassCtor.newInstance(); + } catch (InstantiationException e) { + throw new AssertionError(e); + } catch (IllegalAccessException e) { + throw new AssertionError(e); + } catch (InvocationTargetException e) { + Throwable userException = e.getCause(); + throw new UserCodeException(userException); + } + + // Inject values for the user parameters. + for (Field field : benchmarkClass.getDeclaredFields()) { + if (field.isAnnotationPresent(Param.class)) { + try { + field.setAccessible(true); + Parser<?> parser = Parsers.conventionalParser(field.getType()); + field.set(instance, parser.parse(parameters.get(field.getName()))); + } catch (NoSuchMethodException e) { + throw new RuntimeException(e); + } catch (ParseException e) { + throw new RuntimeException(e); + } catch (IllegalArgumentException e) { + throw new AssertionError("types have been checked"); + } catch (IllegalAccessException e) { + throw new AssertionError("already set access"); + } + } + } + + return instance; + } +} diff --git a/caliper/src/main/java/com/google/caliper/runner/BenchmarkMethods.java b/caliper/src/main/java/com/google/caliper/runner/BenchmarkMethods.java new file mode 100644 index 0000000..f109b50 --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/runner/BenchmarkMethods.java @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2013 Google Inc. + * + * 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.google.caliper.runner; + +import static com.google.common.base.Preconditions.checkArgument; + +import com.google.caliper.Benchmark; +import com.google.caliper.util.Util; + +import java.lang.reflect.Method; +import java.util.Arrays; + +/** + * Utilities for working with methods annotated by {@link Benchmark}. + */ +final class BenchmarkMethods { + private static final Class<?>[] MACROBENCHMARK_PARAMS = new Class<?>[] {}; + private static final Class<?>[] MICROBENCHMARK_PARAMS = new Class<?>[] {int.class}; + private static final Class<?>[] PICOBENCHMARK_PARAMS = new Class<?>[] {long.class}; + + private BenchmarkMethods() {} + + enum Type { + MACRO, + MICRO, + PICO; + + static Type of(Method benchmarkMethod) { + Class<?>[] parameterTypes = benchmarkMethod.getParameterTypes(); + if (Arrays.equals(parameterTypes, MACROBENCHMARK_PARAMS)) { + return MACRO; + } else if (Arrays.equals(parameterTypes, MICROBENCHMARK_PARAMS)) { + return MICRO; + } else if (Arrays.equals(parameterTypes, PICOBENCHMARK_PARAMS)) { + return PICO; + } else { + throw new IllegalArgumentException("invalid method parameters: " + benchmarkMethod); + } + } + } + + /** + * Several instruments look for benchmark methods like {@code timeBlah(int reps)}; this is the + * centralized code that identifies such methods. + * + * <p>This method does not check the correctness of the argument types. + */ + static boolean isTimeMethod(Method method) { + return method.getName().startsWith("time") && Util.isPublic(method); + } + + /** + * For instruments that use {@link #isTimeMethod} to identify their methods, this method checks + * the {@link Method} appropriately. + */ + static Method checkTimeMethod(Method timeMethod) throws InvalidBenchmarkException { + checkArgument(isTimeMethod(timeMethod)); + Class<?>[] parameterTypes = timeMethod.getParameterTypes(); + if (!Arrays.equals(parameterTypes, new Class<?>[] {int.class}) + && !Arrays.equals(parameterTypes, new Class<?>[] {long.class})) { + throw new InvalidBenchmarkException( + "Microbenchmark methods must accept a single int parameter: " + timeMethod.getName()); + } + + // Static technically doesn't hurt anything, but it's just the completely wrong idea + if (Util.isStatic(timeMethod)) { + throw new InvalidBenchmarkException( + "Microbenchmark methods must not be static: " + timeMethod.getName()); + } + return timeMethod; + } +} diff --git a/caliper/src/main/java/com/google/caliper/runner/BenchmarkParameters.java b/caliper/src/main/java/com/google/caliper/runner/BenchmarkParameters.java new file mode 100644 index 0000000..679c2bd --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/runner/BenchmarkParameters.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2012 Google Inc. + * + * 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.google.caliper.runner; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import javax.inject.Qualifier; + +/** Binding annotation for the parameters applied to a benchmark. */ +@Retention(RUNTIME) +@Target({FIELD, PARAMETER, METHOD}) +@Qualifier +@interface BenchmarkParameters {} diff --git a/caliper/src/main/java/com/google/caliper/runner/CaliperMain.java b/caliper/src/main/java/com/google/caliper/runner/CaliperMain.java new file mode 100644 index 0000000..c81e407 --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/runner/CaliperMain.java @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2011 Google Inc. + * + * 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.google.caliper.runner; + +import static com.google.common.collect.ObjectArrays.concat; + +import com.google.caliper.config.InvalidConfigurationException; +import com.google.caliper.options.CaliperOptions; +import com.google.caliper.options.OptionsModule; +import com.google.caliper.util.InvalidCommandException; +import com.google.caliper.util.OutputModule; +import com.google.common.base.Strings; +import com.google.common.collect.ImmutableSortedMap; +import com.google.common.util.concurrent.ServiceManager; + +import java.io.PrintWriter; +import java.util.Map.Entry; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import javax.annotation.Nullable; + +/** + * Primary entry point for the caliper benchmark runner application; run with {@code --help} for + * details. This class's only purpose is to take care of anything that's specific to command-line + * invocation and then hand off to {@code CaliperRun}. That is, a hypothetical GUI benchmark runner + * might still use {@code CaliperRun} but would skip using this class. + */ +public final class CaliperMain { + /** + * Your benchmark classes can implement main() like this: <pre> {@code + * + * public static void main(String[] args) { + * CaliperMain.main(MyBenchmark.class, args); + * }}</pre> + * + * Note that this method does invoke {@link System#exit} when it finishes. Consider {@link + * #exitlessMain} if you don't want that. + * + * <p>Measurement is handled in a subprocess, so it will not use {@code benchmarkClass} itself; + * the class is provided here only as a shortcut for specifying the full class <i>name</i>. The + * class that gets loaded later could be completely different. + */ + public static void main(Class<?> benchmarkClass, String[] args) { + main(concat(args, benchmarkClass.getName())); + } + + /** + * Entry point for the caliper benchmark runner application; run with {@code --help} for details. + */ + public static void main(String[] args) { + PrintWriter stdout = new PrintWriter(System.out, true); + PrintWriter stderr = new PrintWriter(System.err, true); + int code = 1; // pessimism! + + try { + exitlessMain(args, stdout, stderr); + code = 0; + + } catch (InvalidCommandException e) { + e.display(stderr); + code = e.exitCode(); + + } catch (InvalidBenchmarkException e) { + e.display(stderr); + + } catch (InvalidConfigurationException e) { + e.display(stderr); + + } catch (Throwable t) { + t.printStackTrace(stderr); + stdout.println(); + stdout.println("An unexpected exception has been thrown by the caliper runner."); + stdout.println("Please see https://sites.google.com/site/caliperusers/issues"); + } + + stdout.flush(); + stderr.flush(); + System.exit(code); + } + + private static final String LEGACY_ENV = "USE_LEGACY_CALIPER"; + + public static void exitlessMain(String[] args, PrintWriter stdout, PrintWriter stderr) + throws InvalidCommandException, InvalidBenchmarkException, InvalidConfigurationException { + @Nullable String legacyCaliperEnv = System.getenv(LEGACY_ENV); + if (!Strings.isNullOrEmpty(legacyCaliperEnv)) { + System.err.println("Legacy Caliper is no more. " + LEGACY_ENV + " has no effect."); + } + try { + MainComponent mainComponent = DaggerMainComponent.builder() + .optionsModule(OptionsModule.withBenchmarkClass(args)) + .outputModule(new OutputModule(stdout, stderr)) + .build(); + CaliperOptions options = mainComponent.getCaliperOptions(); + if (options.printConfiguration()) { + stdout.println("Configuration:"); + ImmutableSortedMap<String, String> sortedProperties = + ImmutableSortedMap.copyOf(mainComponent.getCaliperConfig().properties()); + for (Entry<String, String> entry : sortedProperties.entrySet()) { + stdout.printf(" %s = %s%n", entry.getKey(), entry.getValue()); + } + } + // check that the parameters are valid + mainComponent.getBenchmarkClass().validateParameters(options.userParameters()); + ServiceManager serviceManager = mainComponent.getServiceManager(); + serviceManager.startAsync().awaitHealthy(); + try { + CaliperRun run = mainComponent.getCaliperRun(); + run.run(); // throws IBE + } finally { + try { + // We have some shutdown logic to ensure that files are cleaned up so give it a chance to + // run + serviceManager.stopAsync().awaitStopped(10, TimeUnit.SECONDS); + } catch (TimeoutException e) { + // That's fine + } + } + } finally { + // courtesy flush + stderr.flush(); + stdout.flush(); + } + } +} diff --git a/caliper/src/main/java/com/google/caliper/runner/CaliperRun.java b/caliper/src/main/java/com/google/caliper/runner/CaliperRun.java new file mode 100644 index 0000000..2555016 --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/runner/CaliperRun.java @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2012 Google Inc. + * + * 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.google.caliper.runner; + +/** + * A single invocation of caliper. + */ +public interface CaliperRun { + void run() throws InvalidBenchmarkException; +} diff --git a/caliper/src/main/java/com/google/caliper/runner/CommonInstrumentOptions.java b/caliper/src/main/java/com/google/caliper/runner/CommonInstrumentOptions.java new file mode 100644 index 0000000..4998601 --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/runner/CommonInstrumentOptions.java @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2012 Google Inc. + * + * 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.google.caliper.runner; + +/** + * Instrument option identifiers shared between instruments. + */ +final class CommonInstrumentOptions { + private CommonInstrumentOptions() {} + + static final String MEASUREMENTS_OPTION = "measurements"; + static final String GC_BEFORE_EACH_OPTION = "gcBeforeEach"; + static final String WARMUP_OPTION = "warmup"; + static final String MAX_WARMUP_WALL_TIME_OPTION = "maxWarmupWallTime"; +} diff --git a/caliper/src/main/java/com/google/caliper/runner/ConsoleOutput.java b/caliper/src/main/java/com/google/caliper/runner/ConsoleOutput.java new file mode 100644 index 0000000..a308c58 --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/runner/ConsoleOutput.java @@ -0,0 +1,149 @@ +/** + * Copyright (C) 2009 Google Inc. + * + * 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.google.caliper.runner; + +import com.google.caliper.model.BenchmarkSpec; +import com.google.caliper.model.InstrumentSpec; +import com.google.caliper.model.Measurement; +import com.google.caliper.model.Scenario; +import com.google.caliper.model.Trial; +import com.google.caliper.model.VmSpec; +import com.google.caliper.util.Stdout; +import com.google.common.base.Function; +import com.google.common.base.Stopwatch; +import com.google.common.collect.FluentIterable; +import com.google.common.collect.ImmutableListMultimap; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Iterables; +import com.google.common.collect.Multimaps; +import com.google.common.collect.Ordering; +import com.google.common.collect.Sets; + +import org.apache.commons.math.stat.descriptive.DescriptiveStatistics; +import org.apache.commons.math.stat.descriptive.rank.Percentile; + +import java.io.Closeable; +import java.io.PrintWriter; +import java.util.Collection; +import java.util.Map.Entry; +import java.util.Set; + +/** + * Prints a brief summary of the results collected. It does not contain the measurements themselves + * as that is the responsibility of the webapp. + */ +final class ConsoleOutput implements Closeable { + private final PrintWriter stdout; + + private final Set<InstrumentSpec> instrumentSpecs = Sets.newHashSet(); + private final Set<VmSpec> vmSpecs = Sets.newHashSet(); + private final Set<BenchmarkSpec> benchmarkSpecs = Sets.newHashSet(); + private int numMeasurements = 0; + private int trialsCompleted = 0; + private final int numberOfTrials; + private final Stopwatch stopwatch; + + + ConsoleOutput(@Stdout PrintWriter stdout, int numberOfTrials, Stopwatch stopwatch) { + this.stdout = stdout; + this.numberOfTrials = numberOfTrials; + this.stopwatch = stopwatch; + } + + /** + * Prints a short message when we observe a trial failure. + */ + void processFailedTrial(TrialFailureException e) { + trialsCompleted++; + // TODO(lukes): it would be nice to print which trial failed. Consider adding Experiment data + // to the TrialFailureException. + stdout.println( + "ERROR: Trial failed to complete (its results will not be included in the run):\n" + + " " + e.getMessage()); + stdout.flush(); + } + + /** + * Prints a summary of a successful trial result. + */ + void processTrial(TrialResult result) { + trialsCompleted++; + stdout.printf("Trial Report (%d of %d):%n Experiment %s%n", + trialsCompleted, numberOfTrials, result.getExperiment()); + if (!result.getTrialMessages().isEmpty()) { + stdout.println(" Messages:"); + for (String message : result.getTrialMessages()) { + stdout.print(" "); + stdout.println(message); + } + } + Trial trial = result.getTrial(); + ImmutableListMultimap<String, Measurement> measurementsIndex = + new ImmutableListMultimap.Builder<String, Measurement>() + .orderKeysBy(Ordering.natural()) + .putAll(Multimaps.index(trial.measurements(), new Function<Measurement, String>() { + @Override public String apply(Measurement input) { + return input.description(); + } + })) + .build(); + stdout.println(" Results:"); + for (Entry<String, Collection<Measurement>> entry : measurementsIndex.asMap().entrySet()) { + Collection<Measurement> measurements = entry.getValue(); + ImmutableSet<String> units = FluentIterable.from(measurements) + .transform(new Function<Measurement, String>() { + @Override public String apply(Measurement input) { + return input.value().unit(); + } + }).toSet(); + double[] weightedValues = new double[measurements.size()]; + int i = 0; + for (Measurement measurement : measurements) { + weightedValues[i] = measurement.value().magnitude() / measurement.weight(); + i++; + } + Percentile percentile = new Percentile(); + percentile.setData(weightedValues); + DescriptiveStatistics descriptiveStatistics = new DescriptiveStatistics(weightedValues); + String unit = Iterables.getOnlyElement(units); + stdout.printf( + " %s%s: min=%.2f, 1st qu.=%.2f, median=%.2f, mean=%.2f, 3rd qu.=%.2f, max=%.2f%n", + entry.getKey(), unit.isEmpty() ? "" : "(" + unit + ")", + descriptiveStatistics.getMin(), percentile.evaluate(25), + percentile.evaluate(50), descriptiveStatistics.getMean(), + percentile.evaluate(75), descriptiveStatistics.getMax()); + } + + instrumentSpecs.add(trial.instrumentSpec()); + Scenario scenario = trial.scenario(); + vmSpecs.add(scenario.vmSpec()); + benchmarkSpecs.add(scenario.benchmarkSpec()); + numMeasurements += trial.measurements().size(); + } + + @Override public void close() { + if (trialsCompleted == numberOfTrials) { // if we finished all the trials + stdout.printf("Collected %d measurements from:%n", numMeasurements); + stdout.printf(" %d instrument(s)%n", instrumentSpecs.size()); + stdout.printf(" %d virtual machine(s)%n", vmSpecs.size()); + stdout.printf(" %d benchmark(s)%n", benchmarkSpecs.size()); + stdout.println(); + stdout.format("Execution complete: %s.%n", stopwatch.stop()); + stdout.flush(); + } + } +} diff --git a/caliper/src/main/java/com/google/caliper/runner/EnvironmentGetter.java b/caliper/src/main/java/com/google/caliper/runner/EnvironmentGetter.java new file mode 100644 index 0000000..798e724 --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/runner/EnvironmentGetter.java @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2010 Google Inc. + * + * 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.google.caliper.runner; + +import com.google.caliper.model.Host; +import com.google.common.collect.ImmutableMultimap; +import com.google.common.collect.ImmutableMultiset; +import com.google.common.collect.Maps; +import com.google.common.collect.Multimap; +import com.google.common.io.Files; + +import java.io.File; +import java.io.IOException; +import java.nio.charset.Charset; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + +/** + * An instance of this class is responsible for returning a Map that describes the environment: + * JVM version, os details, etc. + */ +final class EnvironmentGetter { + Host getHost() { + return new Host.Builder() + .addAllProperies(getProperties()) + .build(); + } + + private Map<String, String> getProperties() { + TreeMap<String, String> propertyMap = Maps.newTreeMap(); + + Map<String, String> sysProps = Maps.fromProperties(System.getProperties()); + + // Sometimes java.runtime.version is more descriptive than java.version + String version = sysProps.get("java.version"); + String alternateVersion = sysProps.get("java.runtime.version"); + if (alternateVersion != null && alternateVersion.length() > version.length()) { + version = alternateVersion; + } + propertyMap.put("host.availableProcessors", + Integer.toString(Runtime.getRuntime().availableProcessors())); + + String osName = sysProps.get("os.name"); + propertyMap.put("os.name", osName); + propertyMap.put("os.version", sysProps.get("os.version")); + propertyMap.put("os.arch", sysProps.get("os.arch")); + + if (osName.equals("Linux")) { + getLinuxEnvironment(propertyMap); + } + + return propertyMap; + } + + private void getLinuxEnvironment(Map<String, String> propertyMap) { + // the following probably doesn't work on ALL linux + Multimap<String, String> cpuInfo = propertiesFromLinuxFile("/proc/cpuinfo"); + propertyMap.put("host.cpus", Integer.toString(cpuInfo.get("processor").size())); + String s = "cpu cores"; + propertyMap.put("host.cpu.cores", describe(cpuInfo, s)); + propertyMap.put("host.cpu.names", describe(cpuInfo, "model name")); + propertyMap.put("host.cpu.cachesize", describe(cpuInfo, "cache size")); + + Multimap<String, String> memInfo = propertiesFromLinuxFile("/proc/meminfo"); + // TODO redo memInfo.toString() so we don't get square brackets + propertyMap.put("host.memory.physical", memInfo.get("MemTotal").toString()); + propertyMap.put("host.memory.swap", memInfo.get("SwapTotal").toString()); + } + + private static String describe(Multimap<String, String> cpuInfo, String s) { + Collection<String> strings = cpuInfo.get(s); + // TODO redo the ImmutableMultiset.toString() call so we don't get square brackets + return (strings.size() == 1) + ? strings.iterator().next() + : ImmutableMultiset.copyOf(strings).toString(); + } + + /** + * Returns the key/value pairs from the specified properties-file like file. + * Unlike standard Java properties files, {@code reader} is allowed to list + * the same property multiple times. Comments etc. are unsupported. + * + * <p>If there's any problem reading the file's contents, we'll return an + * empty Multimap. + */ + private static Multimap<String, String> propertiesFromLinuxFile(String file) { + try { + List<String> lines = Files.readLines(new File(file), Charset.defaultCharset()); + ImmutableMultimap.Builder<String, String> result = ImmutableMultimap.builder(); + for (String line : lines) { + // TODO(schmoe): replace with Splitter (in Guava release 10) + String[] parts = line.split("\\s*\\:\\s*", 2); + if (parts.length == 2) { + result.put(parts[0], parts[1]); + } + } + return result.build(); + } catch (IOException e) { + // If there's any problem reading the file, just return an empty multimap. + return ImmutableMultimap.of(); + } + } +} diff --git a/caliper/src/main/java/com/google/caliper/runner/Experiment.java b/caliper/src/main/java/com/google/caliper/runner/Experiment.java new file mode 100644 index 0000000..f0a877f --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/runner/Experiment.java @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2011 Google Inc. + * + * 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.google.caliper.runner; + +import static com.google.common.base.Preconditions.checkNotNull; + +import com.google.caliper.runner.Instrument.Instrumentation; +import com.google.common.base.MoreObjects; +import com.google.common.base.Objects; +import com.google.common.collect.ImmutableSortedMap; + +import java.util.Map; + +/** + * A single "premise" for making benchmark measurements: which class and method to invoke, which VM + * to use, which choices for user parameters and vmArguments to fill in and which instrument to use + * to measure. A caliper run will compute all possible scenarios using + * {@link FullCartesianExperimentSelector}, and will run one or more trials of each. + */ +final class Experiment { + private final Instrumentation instrumentation; + private final VirtualMachine vm; + private final ImmutableSortedMap<String, String> userParameters; + + Experiment( + Instrumentation instrumentation, + Map<String, String> userParameters, + VirtualMachine vm) { + this.instrumentation = checkNotNull(instrumentation); + this.userParameters = ImmutableSortedMap.copyOf(userParameters); + this.vm = checkNotNull(vm); + } + + Instrumentation instrumentation() { + return instrumentation; + } + + ImmutableSortedMap<String, String> userParameters() { + return userParameters; + } + + VirtualMachine vm() { + return vm; + } + + @Override public boolean equals(Object object) { + if (object instanceof Experiment) { + Experiment that = (Experiment) object; + return this.instrumentation.equals(that.instrumentation) + && this.vm.equals(that.vm) + && this.userParameters.equals(that.userParameters); + } + return false; + } + + @Override public int hashCode() { + return Objects.hashCode(instrumentation, vm, userParameters); + } + + @Override public String toString() { + return MoreObjects.toStringHelper("") + .add("instrument", instrumentation.instrument()) + .add("benchmarkMethod", instrumentation.benchmarkMethod.getName()) + .add("vm", vm.name) + .add("parameters", userParameters) + .toString(); + } +} diff --git a/caliper/src/main/java/com/google/caliper/runner/ExperimentComponent.java b/caliper/src/main/java/com/google/caliper/runner/ExperimentComponent.java new file mode 100644 index 0000000..174c5f5 --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/runner/ExperimentComponent.java @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2015 Google Inc. + * + * 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.google.caliper.runner; + +import static com.google.caliper.runner.Running.Benchmark; + +import dagger.Subcomponent; + +/** + * Provides access to the benchmark instance to use for the experiment. + */ +@Subcomponent(modules = ExperimentModule.class) +public interface ExperimentComponent { + @Benchmark Object getBenchmarkInstance(); +} diff --git a/caliper/src/main/java/com/google/caliper/runner/ExperimentModule.java b/caliper/src/main/java/com/google/caliper/runner/ExperimentModule.java new file mode 100644 index 0000000..79711a1 --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/runner/ExperimentModule.java @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2013 Google Inc. + * + * 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.google.caliper.runner; + +import static com.google.caliper.runner.Running.Benchmark; +import static com.google.caliper.runner.Running.BenchmarkMethod; +import static com.google.common.base.Preconditions.checkNotNull; + +import com.google.caliper.bridge.WorkerSpec; +import com.google.caliper.util.Util; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSortedMap; +import dagger.Module; +import dagger.Provides; + +import java.lang.reflect.Method; + +/** + * A module that binds data specific to a single experiment. + */ +@Module +public final class ExperimentModule { + private final ImmutableSortedMap<String, String> parameters; + private final Method benchmarkMethod; + + private ExperimentModule( + Method benchmarkMethod, + ImmutableSortedMap<String, String> parameters) { + this.parameters = checkNotNull(parameters); + this.benchmarkMethod = checkNotNull(benchmarkMethod); + } + + public static ExperimentModule forExperiment(Experiment experiment) { + Method benchmarkMethod = experiment.instrumentation().benchmarkMethod(); + return new ExperimentModule( + benchmarkMethod, + experiment.userParameters()); + } + + public static ExperimentModule forWorkerSpec(WorkerSpec spec) + throws ClassNotFoundException { + Class<?> benchmarkClass = Util.loadClass(spec.benchmarkSpec.className()); + Method benchmarkMethod = findBenchmarkMethod(benchmarkClass, spec.benchmarkSpec.methodName(), + spec.methodParameterClasses); + benchmarkMethod.setAccessible(true); + return new ExperimentModule(benchmarkMethod, spec.benchmarkSpec.parameters()); + } + + @Provides + @Benchmark + static Object provideRunningBenchmark(BenchmarkCreator creator) { + return creator.createBenchmarkInstance(); + } + + @Provides + @BenchmarkMethod + String provideRunningBenchmarkMethodName() { + return benchmarkMethod.getName(); + } + + @Provides + @BenchmarkMethod + Method provideRunningBenchmarkMethod() { + return benchmarkMethod; + } + + @Provides + @Benchmark + ImmutableSortedMap<String, String> provideUserParameters() { + return parameters; + } + + private static Method findBenchmarkMethod(Class<?> benchmark, String methodName, + ImmutableList<Class<?>> methodParameterClasses) { + Class<?>[] params = methodParameterClasses.toArray(new Class<?>[methodParameterClasses.size()]); + try { + return benchmark.getDeclaredMethod(methodName, params); + } catch (NoSuchMethodException e) { + throw new RuntimeException(e); + } catch (SecurityException e) { + // assertion error? + throw new RuntimeException(e); + } + } +} diff --git a/caliper/src/main/java/com/google/caliper/runner/ExperimentSelector.java b/caliper/src/main/java/com/google/caliper/runner/ExperimentSelector.java new file mode 100644 index 0000000..fe32057 --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/runner/ExperimentSelector.java @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2011 Google Inc. + * + * 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.google.caliper.runner; + +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.ImmutableSetMultimap; + +public interface ExperimentSelector { + ImmutableSet<Instrument> instruments(); + ImmutableSet<VirtualMachine> vms(); + ImmutableSetMultimap<String, String> userParameters(); + + // The important method + ImmutableSet<Experiment> selectExperiments(); + + String selectionType(); +} diff --git a/caliper/src/main/java/com/google/caliper/runner/ExperimentingCaliperRun.java b/caliper/src/main/java/com/google/caliper/runner/ExperimentingCaliperRun.java new file mode 100644 index 0000000..5214193 --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/runner/ExperimentingCaliperRun.java @@ -0,0 +1,307 @@ +/* + * Copyright (C) 2011 Google Inc. + * + * 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.google.caliper.runner; + +import static java.util.logging.Level.WARNING; + +import com.google.caliper.api.ResultProcessor; +import com.google.caliper.api.SkipThisScenarioException; +import com.google.caliper.options.CaliperOptions; +import com.google.caliper.util.Stdout; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Function; +import com.google.common.base.Stopwatch; +import com.google.common.base.Throwables; +import com.google.common.collect.FluentIterable; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Lists; +import com.google.common.collect.Queues; +import com.google.common.util.concurrent.AsyncFunction; +import com.google.common.util.concurrent.FutureFallback; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.ListeningExecutorService; +import com.google.common.util.concurrent.MoreExecutors; +import com.google.common.util.concurrent.SettableFuture; +import com.google.common.util.concurrent.Uninterruptibles; + +import java.io.IOException; +import java.io.PrintWriter; +import java.util.List; +import java.util.UUID; +import java.util.concurrent.CancellationException; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.ExecutionException; +import java.util.logging.Logger; + +import javax.inject.Inject; +import javax.inject.Provider; + +/** + * An execution of each {@link Experiment} for the configured number of trials. + */ +@VisibleForTesting +public final class ExperimentingCaliperRun implements CaliperRun { + + private static final Logger logger = Logger.getLogger(ExperimentingCaliperRun.class.getName()); + + private static final FutureFallback<Object> FALLBACK_TO_NULL = new FutureFallback<Object>() { + final ListenableFuture<Object> nullFuture = Futures.immediateFuture(null); + @Override public ListenableFuture<Object> create(Throwable t) throws Exception { + return nullFuture; + } + }; + + private final MainComponent mainComponent; + private final CaliperOptions options; + private final PrintWriter stdout; + private final BenchmarkClass benchmarkClass; + private final ImmutableSet<Instrument> instruments; + private final ImmutableSet<ResultProcessor> resultProcessors; + private final ExperimentSelector selector; + private final Provider<ListeningExecutorService> executorProvider; + + @Inject @VisibleForTesting + public ExperimentingCaliperRun( + MainComponent mainComponent, + CaliperOptions options, + @Stdout PrintWriter stdout, + BenchmarkClass benchmarkClass, + ImmutableSet<Instrument> instruments, + ImmutableSet<ResultProcessor> resultProcessors, + ExperimentSelector selector, + Provider<ListeningExecutorService> executorProvider) { + this.mainComponent = mainComponent; + this.options = options; + this.stdout = stdout; + this.benchmarkClass = benchmarkClass; + this.instruments = instruments; + this.resultProcessors = resultProcessors; + this.selector = selector; + this.executorProvider = executorProvider; + } + + @Override + public void run() throws InvalidBenchmarkException { + ImmutableSet<Experiment> allExperiments = selector.selectExperiments(); + // TODO(lukes): move this standard-out handling into the ConsoleOutput class? + stdout.println("Experiment selection: "); + stdout.println(" Benchmark Methods: " + FluentIterable.from(allExperiments) + .transform(new Function<Experiment, String>() { + @Override public String apply(Experiment experiment) { + return experiment.instrumentation().benchmarkMethod().getName(); + } + }).toSet()); + stdout.println(" Instruments: " + FluentIterable.from(selector.instruments()) + .transform(new Function<Instrument, String>() { + @Override public String apply(Instrument instrument) { + return instrument.name(); + } + })); + stdout.println(" User parameters: " + selector.userParameters()); + stdout.println(" Virtual machines: " + FluentIterable.from(selector.vms()) + .transform( + new Function<VirtualMachine, String>() { + @Override public String apply(VirtualMachine vm) { + return vm.name; + } + })); + stdout.println(" Selection type: " + selector.selectionType()); + stdout.println(); + + if (allExperiments.isEmpty()) { + throw new InvalidBenchmarkException( + "There were no experiments to be performed for the class %s using the instruments %s", + benchmarkClass.benchmarkClass().getSimpleName(), instruments); + } + + stdout.format("This selection yields %s experiments.%n", allExperiments.size()); + stdout.flush(); + + // always dry run first. + ImmutableSet<Experiment> experimentsToRun = dryRun(allExperiments); + if (experimentsToRun.size() != allExperiments.size()) { + stdout.format("%d experiments were skipped.%n", + allExperiments.size() - experimentsToRun.size()); + } + + if (experimentsToRun.isEmpty()) { + throw new InvalidBenchmarkException("All experiments were skipped."); + } + + if (options.dryRun()) { + return; + } + + stdout.flush(); + + int totalTrials = experimentsToRun.size() * options.trialsPerScenario(); + Stopwatch stopwatch = Stopwatch.createStarted(); + List<ScheduledTrial> trials = createScheduledTrials(experimentsToRun, totalTrials); + + final ListeningExecutorService executor = executorProvider.get(); + List<ListenableFuture<TrialResult>> pendingTrials = scheduleTrials(trials, executor); + ConsoleOutput output = new ConsoleOutput(stdout, totalTrials, stopwatch); + try { + // Process results as they complete. + for (ListenableFuture<TrialResult> trialFuture : inCompletionOrder(pendingTrials)) { + try { + TrialResult result = trialFuture.get(); + output.processTrial(result); + for (ResultProcessor resultProcessor : resultProcessors) { + resultProcessor.processTrial(result.getTrial()); + } + } catch (ExecutionException e) { + if (e.getCause() instanceof TrialFailureException) { + output.processFailedTrial((TrialFailureException) e.getCause()); + } else { + for (ListenableFuture<?> toCancel : pendingTrials) { + toCancel.cancel(true); + } + throw Throwables.propagate(e.getCause()); + } + } catch (InterruptedException e) { + // be responsive to interruption, cancel outstanding work and exit + for (ListenableFuture<?> toCancel : pendingTrials) { + // N.B. TrialRunLoop is responsive to interruption. + toCancel.cancel(true); + } + throw new RuntimeException(e); + } + } + } finally { + executor.shutdown(); + output.close(); + } + + for (ResultProcessor resultProcessor : resultProcessors) { + try { + resultProcessor.close(); + } catch (IOException e) { + logger.log(WARNING, "Could not close a result processor: " + resultProcessor, e); + } + } + } + + /** + * Schedule all the trials. + * + * <p>This method arranges all the {@link ScheduledTrial trials} to run according to their + * scheduling criteria. The executor instance is responsible for enforcing max parallelism. + */ + private List<ListenableFuture<TrialResult>> scheduleTrials(List<ScheduledTrial> trials, + final ListeningExecutorService executor) { + List<ListenableFuture<TrialResult>> pendingTrials = Lists.newArrayList(); + List<ScheduledTrial> serialTrials = Lists.newArrayList(); + for (final ScheduledTrial scheduledTrial : trials) { + if (scheduledTrial.policy() == TrialSchedulingPolicy.PARALLEL) { + pendingTrials.add(executor.submit(scheduledTrial.trialTask())); + } else { + serialTrials.add(scheduledTrial); + } + } + // A future representing the completion of all prior tasks. Futures.successfulAsList allows us + // to ignore failure. + ListenableFuture<?> previous = Futures.successfulAsList(pendingTrials); + for (final ScheduledTrial scheduledTrial : serialTrials) { + // each of these trials can only start after all prior trials have finished, so we use + // Futures.transform to force the sequencing. + ListenableFuture<TrialResult> current = + Futures.transform( + previous, + new AsyncFunction<Object, TrialResult>() { + @Override public ListenableFuture<TrialResult> apply(Object ignored) { + return executor.submit(scheduledTrial.trialTask()); + } + }); + pendingTrials.add(current); + // ignore failure of the prior task. + previous = Futures.withFallback(current, FALLBACK_TO_NULL); + } + return pendingTrials; + } + + /** Returns all the ScheduledTrials for this run. */ + private List<ScheduledTrial> createScheduledTrials(ImmutableSet<Experiment> experimentsToRun, + int totalTrials) { + List<ScheduledTrial> trials = Lists.newArrayListWithCapacity(totalTrials); + /** This is 1-indexed because it's only used for display to users. E.g. "Trial 1 of 27" */ + int trialNumber = 1; + for (int i = 0; i < options.trialsPerScenario(); i++) { + for (Experiment experiment : experimentsToRun) { + try { + TrialScopeComponent trialScopeComponent = mainComponent.newTrialComponent( + new TrialModule(UUID.randomUUID(), trialNumber, experiment)); + + trials.add(trialScopeComponent.getScheduledTrial()); + } finally { + trialNumber++; + } + } + } + return trials; + } + + /** + * Attempts to run each given scenario once, in the current VM. Returns a set of all of the + * scenarios that didn't throw a {@link SkipThisScenarioException}. + */ + ImmutableSet<Experiment> dryRun(Iterable<Experiment> experiments) + throws InvalidBenchmarkException { + ImmutableSet.Builder<Experiment> builder = ImmutableSet.builder(); + for (Experiment experiment : experiments) { + try { + ExperimentComponent experimentComponent = + mainComponent.newExperimentComponent(ExperimentModule.forExperiment(experiment)); + Object benchmark = experimentComponent.getBenchmarkInstance(); + benchmarkClass.setUpBenchmark(benchmark); + try { + experiment.instrumentation().dryRun(benchmark); + builder.add(experiment); + } finally { + // discard 'benchmark' now; the worker will have to instantiate its own anyway + benchmarkClass.cleanup(benchmark); + } + } catch (SkipThisScenarioException innocuous) {} + } + return builder.build(); + } + + public static <T> ImmutableList<ListenableFuture<T>> inCompletionOrder( + Iterable<? extends ListenableFuture<? extends T>> futures) { + final ConcurrentLinkedQueue<SettableFuture<T>> delegates = Queues.newConcurrentLinkedQueue(); + ImmutableList.Builder<ListenableFuture<T>> listBuilder = ImmutableList.builder(); + for (final ListenableFuture<? extends T> future : futures) { + SettableFuture<T> delegate = SettableFuture.create(); + // Must make sure to add the delegate to the queue first in case the future is already done + delegates.add(delegate); + future.addListener(new Runnable() { + @Override public void run() { + SettableFuture<T> delegate = delegates.remove(); + try { + delegate.set(Uninterruptibles.getUninterruptibly(future)); + } catch (ExecutionException e) { + delegate.setException(e.getCause()); + } catch (CancellationException e) { + delegate.cancel(true); + } + } + }, MoreExecutors.directExecutor()); + listBuilder.add(delegate); + } + return listBuilder.build(); + } +} diff --git a/caliper/src/main/java/com/google/caliper/runner/ExperimentingRunnerModule.java b/caliper/src/main/java/com/google/caliper/runner/ExperimentingRunnerModule.java new file mode 100644 index 0000000..7abcbd9 --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/runner/ExperimentingRunnerModule.java @@ -0,0 +1,284 @@ +/* + * Copyright (C) 2012 Google Inc. + * + * 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.google.caliper.runner; + +import com.google.caliper.api.ResultProcessor; +import com.google.caliper.config.CaliperConfig; +import com.google.caliper.config.InstrumentConfig; +import com.google.caliper.model.Host; +import com.google.caliper.options.CaliperOptions; +import com.google.caliper.platform.Platform; +import com.google.caliper.runner.Instrument.Instrumentation; +import com.google.caliper.util.InvalidCommandException; +import com.google.caliper.util.ShortDuration; +import com.google.caliper.util.Stderr; +import com.google.caliper.util.Util; +import com.google.common.base.Function; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.ImmutableSetMultimap; +import com.google.common.collect.ImmutableSortedSet; +import com.google.common.collect.Ordering; +import com.google.common.util.concurrent.ListeningExecutorService; +import com.google.common.util.concurrent.MoreExecutors; +import com.google.common.util.concurrent.Service; + +import dagger.MapKey; +import dagger.Module; +import dagger.Provides; +import dagger.Provides.Type; + +import java.io.PrintWriter; +import java.lang.reflect.Method; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.TreeSet; +import java.util.UUID; +import java.util.concurrent.Executors; + +import javax.inject.Provider; +import javax.inject.Singleton; + +/** + * Configures a {@link CaliperRun} that performs experiments. + */ +@Module +final class ExperimentingRunnerModule { + private static final String RUNNER_MAX_PARALLELISM_OPTION = "runner.maxParallelism"; + + @Provides(type = Type.SET) + static Service provideServerSocketService(ServerSocketService impl) { + return impl; + } + + @Provides(type = Type.SET) + static Service provideTrialOutputFactoryService(TrialOutputFactoryService impl) { + return impl; + } + + @Provides + static TrialOutputFactory provideTrialOutputFactory(TrialOutputFactoryService impl) { + return impl; + } + + @Provides + static ExperimentSelector provideExperimentSelector(FullCartesianExperimentSelector impl) { + return impl; + } + + @Provides + static ListeningExecutorService provideExecutorService(CaliperConfig config) { + int poolSize = Integer.parseInt(config.properties().get(RUNNER_MAX_PARALLELISM_OPTION)); + return MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(poolSize)); + } + + @LocalPort + @Provides + static int providePortNumber(ServerSocketService serverSocketService) { + return serverSocketService.getPort(); + } + + /** + * Specifies the {@link Class} object to use as a key in the map of available + * {@link ResultProcessor result processors} passed to + * {@link #provideResultProcessors(CaliperConfig, Map)}. + */ + @MapKey(unwrapValue = true) + public @interface ResultProcessorClassKey { + Class<? extends ResultProcessor> value(); + } + + @Provides(type = Type.MAP) + @ResultProcessorClassKey(OutputFileDumper.class) + static ResultProcessor provideOutputFileDumper(OutputFileDumper impl) { + return impl; + } + + @Provides(type = Type.MAP) + @ResultProcessorClassKey(HttpUploader.class) + static ResultProcessor provideHttpUploader(HttpUploader impl) { + return impl; + } + + @Provides static ImmutableSet<ResultProcessor> provideResultProcessors( + CaliperConfig config, + Map<Class<? extends ResultProcessor>, Provider<ResultProcessor>> availableProcessors) { + ImmutableSet.Builder<ResultProcessor> builder = ImmutableSet.builder(); + for (Class<? extends ResultProcessor> processorClass : config.getConfiguredResultProcessors()) { + Provider<ResultProcessor> resultProcessorProvider = availableProcessors.get(processorClass); + ResultProcessor resultProcessor = resultProcessorProvider == null + ? ResultProcessorCreator.createResultProcessor(processorClass) + : resultProcessorProvider.get(); + builder.add(resultProcessor); + } + return builder.build(); + } + + @Provides static UUID provideUuid() { + return UUID.randomUUID(); + } + + @Provides @BenchmarkParameters + static ImmutableSetMultimap<String, String> provideBenchmarkParameters( + BenchmarkClass benchmarkClass, CaliperOptions options) throws InvalidBenchmarkException { + return benchmarkClass.userParameters().fillInDefaultsFor(options.userParameters()); + } + + @Provides @Singleton + static Host provideHost(EnvironmentGetter environmentGetter) { + return environmentGetter.getHost(); + } + + @Provides @Singleton + static EnvironmentGetter provideEnvironmentGetter() { + return new EnvironmentGetter(); + } + + /** + * Specifies the {@link Class} object to use as a key in the map of available + * {@link Instrument instruments} passed to {@link #provideInstruments}, + */ + @MapKey(unwrapValue = true) + public @interface InstrumentClassKey { + Class<? extends Instrument> value(); + } + + @Provides(type = Type.MAP) + @InstrumentClassKey(ArbitraryMeasurementInstrument.class) + static Instrument provideArbitraryMeasurementInstrument() { + return new ArbitraryMeasurementInstrument(); + } + + @Provides(type = Type.MAP) + @InstrumentClassKey(AllocationInstrument.class) + static Instrument provideAllocationInstrument() { + return new AllocationInstrument(); + } + + @Provides(type = Type.MAP) + @InstrumentClassKey(RuntimeInstrument.class) + static Instrument provideRuntimeInstrument( + @NanoTimeGranularity ShortDuration nanoTimeGranularity) { + return new RuntimeInstrument(nanoTimeGranularity); + } + + @Provides + static ImmutableSet<Instrument> provideInstruments( + CaliperOptions options, + final CaliperConfig config, + Map<Class<? extends Instrument>, Provider<Instrument>> availableInstruments, + Platform platform, + @Stderr PrintWriter stderr) + throws InvalidCommandException { + + ImmutableSet.Builder<Instrument> builder = ImmutableSet.builder(); + ImmutableSet<String> configuredInstruments = config.getConfiguredInstruments(); + for (final String instrumentName : options.instrumentNames()) { + if (!configuredInstruments.contains(instrumentName)) { + throw new InvalidCommandException("%s is not a configured instrument (%s). " + + "use --print-config to see the configured instruments.", + instrumentName, configuredInstruments); + } + final InstrumentConfig instrumentConfig = config.getInstrumentConfig(instrumentName); + String className = instrumentConfig.className(); + try { + Class<? extends Instrument> clazz = + Util.lenientClassForName(className).asSubclass(Instrument.class); + Provider<Instrument> instrumentProvider = availableInstruments.get(clazz); + if (instrumentProvider == null) { + throw new InvalidInstrumentException("Instrument %s not supported", className); + } + + // Make sure that the instrument is supported on the platform. + if (platform.supports(clazz)) { + Instrument instrument = instrumentProvider.get(); + InstrumentInjectorModule injectorModule = + new InstrumentInjectorModule(instrumentConfig, instrumentName); + InstrumentComponent instrumentComponent = DaggerInstrumentComponent.builder() + .instrumentInjectorModule(injectorModule) + .build(); + instrumentComponent.injectInstrument(instrument); + builder.add(instrument); + } else { + stderr.format("Instrument %s not supported on %s, ignoring\n", + className, platform.name()); + } + } catch (ClassNotFoundException e) { + throw new InvalidCommandException("Cannot find instrument class '%s'", className); + } + } + return builder.build(); + } + + @Provides @Singleton static NanoTimeGranularityTester provideNanoTimeGranularityTester() { + return new NanoTimeGranularityTester(); + } + + @Provides @Singleton @NanoTimeGranularity static ShortDuration provideNanoTimeGranularity( + NanoTimeGranularityTester tester) { + return tester.testNanoTimeGranularity(); + } + + @Provides static ImmutableSet<Instrumentation> provideInstrumentations(CaliperOptions options, + BenchmarkClass benchmarkClass, ImmutableSet<Instrument> instruments) + throws InvalidBenchmarkException { + ImmutableSet.Builder<Instrumentation> builder = ImmutableSet.builder(); + ImmutableSet<String> benchmarkMethodNames = options.benchmarkMethodNames(); + Set<String> unusedBenchmarkNames = new HashSet<String>(benchmarkMethodNames); + for (Instrument instrument : instruments) { + for (Method method : findAllBenchmarkMethods(benchmarkClass.benchmarkClass(), instrument)) { + if (benchmarkMethodNames.isEmpty() || benchmarkMethodNames.contains(method.getName())) { + builder.add(instrument.createInstrumentation(method)); + unusedBenchmarkNames.remove(method.getName()); + } + } + } + if (!unusedBenchmarkNames.isEmpty()) { + throw new InvalidBenchmarkException( + "Invalid benchmark method(s) specified in options: " + unusedBenchmarkNames); + } + return builder.build(); + } + + private static ImmutableSortedSet<Method> findAllBenchmarkMethods(Class<?> benchmarkClass, + Instrument instrument) throws InvalidBenchmarkException { + ImmutableSortedSet.Builder<Method> result = ImmutableSortedSet.orderedBy( + Ordering.natural().onResultOf(new Function<Method, String>() { + @Override public String apply(Method method) { + return method.getName(); + } + })); + Set<String> benchmarkMethodNames = new HashSet<String>(); + Set<String> overloadedMethodNames = new TreeSet<String>(); + for (Method method : benchmarkClass.getDeclaredMethods()) { + if (instrument.isBenchmarkMethod(method)) { + method.setAccessible(true); + result.add(method); + if (!benchmarkMethodNames.add(method.getName())) { + overloadedMethodNames.add(method.getName()); + } + } + } + if (!overloadedMethodNames.isEmpty()) { + throw new InvalidBenchmarkException( + "Overloads are disallowed for benchmark methods, found overloads of %s in benchmark %s", + overloadedMethodNames, + benchmarkClass); + } + return result.build(); + } +} diff --git a/caliper/src/main/java/com/google/caliper/runner/FullCartesianExperimentSelector.java b/caliper/src/main/java/com/google/caliper/runner/FullCartesianExperimentSelector.java new file mode 100644 index 0000000..a972ca4 --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/runner/FullCartesianExperimentSelector.java @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2011 Google Inc. + * + * 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.google.caliper.runner; + +import com.google.caliper.runner.Instrument.Instrumentation; +import com.google.common.base.Function; +import com.google.common.collect.FluentIterable; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.ImmutableSetMultimap; +import com.google.common.collect.Lists; +import com.google.common.collect.SetMultimap; +import com.google.common.collect.Sets; + +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.Set; + +import javax.inject.Inject; + +/** + * A set of {@link Experiment experiments} constructed by taking all possible combinations of + * instruments, benchmark methods, user parameters, VM specs and VM arguments. + */ +public final class FullCartesianExperimentSelector implements ExperimentSelector { + private ImmutableSet<Instrumentation> instrumentations; + private final ImmutableSet<VirtualMachine> vms; + private final ImmutableSetMultimap<String, String> userParameters; + + @Inject FullCartesianExperimentSelector( + ImmutableSet<Instrumentation> instrumentations, + ImmutableSet<VirtualMachine> vms, + @BenchmarkParameters ImmutableSetMultimap<String, String> userParameters) { + this.instrumentations = instrumentations; + this.vms = vms; + this.userParameters = userParameters; + } + + // TODO(gak): put this someplace more sensible + @Override public ImmutableSet<Instrument> instruments() { + return FluentIterable.from(instrumentations) + .transform(new Function<Instrumentation, Instrument>() { + @Override public Instrument apply(Instrumentation input) { + return input.instrument(); + } + }) + .toSet(); + } + + @Override public ImmutableSet<VirtualMachine> vms() { + return vms; + } + + @Override public ImmutableSetMultimap<String, String> userParameters() { + return userParameters; + } + + @Override public ImmutableSet<Experiment> selectExperiments() { + List<Experiment> experiments = Lists.newArrayList(); + for (Instrumentation instrumentation : instrumentations) { + for (VirtualMachine vm : vms) { + for (List<String> userParamsChoice : cartesian(userParameters)) { + ImmutableMap<String, String> theseUserParams = + zip(userParameters.keySet(), userParamsChoice); + experiments.add(new Experiment(instrumentation, theseUserParams, vm)); + } + } + } + return ImmutableSet.copyOf(experiments); + } + + protected static <T> Set<List<T>> cartesian(SetMultimap<String, T> multimap) { + @SuppressWarnings({"unchecked", "rawtypes"}) // promised by spec + ImmutableMap<String, Set<T>> paramsAsMap = (ImmutableMap) multimap.asMap(); + return Sets.cartesianProduct(paramsAsMap.values().asList()); + } + + protected static <K, V> ImmutableMap<K, V> zip(Set<K> keys, Collection<V> values) { + ImmutableMap.Builder<K, V> builder = ImmutableMap.builder(); + + Iterator<K> keyIterator = keys.iterator(); + Iterator<V> valueIterator = values.iterator(); + + while (keyIterator.hasNext() && valueIterator.hasNext()) { + builder.put(keyIterator.next(), valueIterator.next()); + } + + if (keyIterator.hasNext() || valueIterator.hasNext()) { + throw new AssertionError(); // I really screwed up, then. + } + return builder.build(); + } + + @Override public String selectionType() { + return "Full cartesian product"; + } +} diff --git a/caliper/src/main/java/com/google/caliper/runner/HttpUploader.java b/caliper/src/main/java/com/google/caliper/runner/HttpUploader.java new file mode 100644 index 0000000..2f99a50 --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/runner/HttpUploader.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2012 Google Inc. + * + * 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.google.caliper.runner; + +import com.google.caliper.api.ResultProcessor; +import com.google.caliper.config.CaliperConfig; +import com.google.caliper.config.InvalidConfigurationException; +import com.google.caliper.util.Stdout; +import com.google.gson.Gson; + +import com.sun.jersey.api.client.Client; + +import java.io.PrintWriter; + +import javax.inject.Inject; + +/** + * A {@link ResultProcessor} implementation that publishes results to the webapp using an HTTP + * request. + */ +public class HttpUploader extends ResultsUploader { + @Inject HttpUploader(@Stdout PrintWriter stdout, Gson gson, CaliperConfig config) + throws InvalidConfigurationException { + super(stdout, gson, Client.create(), config.getResultProcessorConfig(HttpUploader.class)); + } +} diff --git a/caliper/src/main/java/com/google/caliper/runner/Instrument.java b/caliper/src/main/java/com/google/caliper/runner/Instrument.java new file mode 100644 index 0000000..0d6bc73 --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/runner/Instrument.java @@ -0,0 +1,231 @@ +/* + * Copyright (C) 2011 Google Inc. + * + * 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.google.caliper.runner; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +import com.google.caliper.bridge.AbstractLogMessageVisitor; +import com.google.caliper.bridge.LogMessageVisitor; +import com.google.caliper.bridge.StopMeasurementLogMessage; +import com.google.caliper.config.VmConfig; +import com.google.caliper.model.InstrumentSpec; +import com.google.caliper.model.Measurement; +import com.google.caliper.worker.Worker; +import com.google.common.base.MoreObjects; +import com.google.common.base.Objects; +import com.google.common.base.Predicates; +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.ListMultimap; +import com.google.common.collect.Maps; + +import java.lang.reflect.Method; + +import javax.inject.Inject; + +public abstract class Instrument { + protected ImmutableMap<String, String> options; + private String name = getClass().getSimpleName(); + + @Inject void setOptions(@InstrumentOptions ImmutableMap<String, String> options) { + this.options = ImmutableMap.copyOf( + Maps.filterKeys(options, Predicates.in(instrumentOptions()))); + } + + @Inject void setInstrumentName(@InstrumentName String name) { + this.name = name; + } + + String name() { + return name; + } + + @Override public String toString() { + return name(); + } + + public abstract boolean isBenchmarkMethod(Method method); + + public abstract Instrumentation createInstrumentation(Method benchmarkMethod) + throws InvalidBenchmarkException; + + /** + * Indicates that trials using this instrument can be run in parallel with other trials. + */ + public abstract TrialSchedulingPolicy schedulingPolicy(); + + /** + * The application of an instrument to a particular benchmark method. + */ + // TODO(gak): consider passing in Instrument explicitly for DI + public abstract class Instrumentation { + protected Method benchmarkMethod; + + protected Instrumentation(Method benchmarkMethod) { + this.benchmarkMethod = checkNotNull(benchmarkMethod); + } + + Instrument instrument() { + return Instrument.this; + } + + Method benchmarkMethod() { + return benchmarkMethod; + } + + @Override + public final boolean equals(Object obj) { + if (obj == this) { + return true; + } else if (obj instanceof Instrumentation) { + Instrumentation that = (Instrumentation) obj; + return Instrument.this.equals(that.instrument()) + && this.benchmarkMethod.equals(that.benchmarkMethod); + } + return super.equals(obj); + } + + @Override + public final int hashCode() { + return Objects.hashCode(Instrument.this, benchmarkMethod); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(Instrumentation.class) + .add("instrument", Instrument.this) + .add("benchmarkMethod", benchmarkMethod) + .toString(); + } + + public abstract void dryRun(Object benchmark) throws InvalidBenchmarkException; + + public abstract Class<? extends Worker> workerClass(); + + /** + * Return the subset of options (and possibly a transformation thereof) to be used in the + * worker. Returns all instrument options by default. + */ + public ImmutableMap<String, String> workerOptions() { + return options; + } + + abstract MeasurementCollectingVisitor getMeasurementCollectingVisitor(); + } + + public final ImmutableMap<String, String> options() { + return options; + } + + final InstrumentSpec getSpec() { + return new InstrumentSpec.Builder() + .instrumentClass(getClass()) + .addAllOptions(options()) + .build(); + } + + /** + * Defines the list of options applicable to this instrument. Implementations that use options + * will need to override this method. + */ + protected ImmutableSet<String> instrumentOptions() { + return ImmutableSet.of(); + } + + /** + * Returns some arguments that should be added to the command line when invoking + * this instrument's worker. + * + * @param vmConfig the configuration for the VM on which this is running. + */ + ImmutableSet<String> getExtraCommandLineArgs(VmConfig vmConfig) { + return vmConfig.commonInstrumentVmArgs(); + } + + interface MeasurementCollectingVisitor extends LogMessageVisitor { + boolean isDoneCollecting(); + boolean isWarmupComplete(); + ImmutableList<Measurement> getMeasurements(); + /** + * Returns all the messages created while collecting measurments. + * + * <p>A message is some piece of user visible data that should be displayed to the user along + * with the trial results. + * + * <p>TODO(lukes): should we model these as anything more than strings. These messages already + * have a concept of 'level' based on the prefix. + */ + ImmutableList<String> getMessages(); + } + + /** + * A default implementation of {@link MeasurementCollectingVisitor} that collects measurements for + * pre-specified descriptions. + */ + protected static final class DefaultMeasurementCollectingVisitor + extends AbstractLogMessageVisitor implements MeasurementCollectingVisitor { + static final int DEFAULT_NUMBER_OF_MEASUREMENTS = 9; + final ImmutableSet<String> requiredDescriptions; + final ListMultimap<String, Measurement> measurementsByDescription; + final int requiredMeasurements; + + DefaultMeasurementCollectingVisitor(ImmutableSet<String> requiredDescriptions) { + this(requiredDescriptions, DEFAULT_NUMBER_OF_MEASUREMENTS); + } + + DefaultMeasurementCollectingVisitor(ImmutableSet<String> requiredDescriptions, + int requiredMeasurements) { + this.requiredDescriptions = requiredDescriptions; + checkArgument(!requiredDescriptions.isEmpty()); + this.requiredMeasurements = requiredMeasurements; + checkArgument(requiredMeasurements > 0); + this.measurementsByDescription = + ArrayListMultimap.create(requiredDescriptions.size(), requiredMeasurements); + } + + @Override public void visit(StopMeasurementLogMessage logMessage) { + for (Measurement measurement : logMessage.measurements()) { + measurementsByDescription.put(measurement.description(), measurement); + } + } + + @Override public boolean isDoneCollecting() { + for (String description : requiredDescriptions) { + if (measurementsByDescription.get(description).size() < requiredMeasurements) { + return false; + } + } + return true; + } + + @Override + public boolean isWarmupComplete() { + return true; + } + + @Override public ImmutableList<Measurement> getMeasurements() { + return ImmutableList.copyOf(measurementsByDescription.values()); + } + + @Override public ImmutableList<String> getMessages() { + return ImmutableList.of(); + } + } +} diff --git a/caliper/src/main/java/com/google/caliper/runner/InstrumentComponent.java b/caliper/src/main/java/com/google/caliper/runner/InstrumentComponent.java new file mode 100644 index 0000000..55157e7 --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/runner/InstrumentComponent.java @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2015 Google Inc. + * + * 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.google.caliper.runner; + +import dagger.Component; + +/** + * Component to use to inject values into an {@link Instrument}. + */ +@Component(modules = InstrumentInjectorModule.class) +interface InstrumentComponent { + + void injectInstrument(Instrument instrument); +} diff --git a/caliper/src/main/java/com/google/caliper/runner/InstrumentInjectorModule.java b/caliper/src/main/java/com/google/caliper/runner/InstrumentInjectorModule.java new file mode 100644 index 0000000..dc159e3 --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/runner/InstrumentInjectorModule.java @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2015 Google Inc. + * + * 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.google.caliper.runner; + +import com.google.caliper.config.InstrumentConfig; +import com.google.common.collect.ImmutableMap; +import dagger.Module; +import dagger.Provides; + +/** + * The set of bindings available for injecting into {@link Instrument} instances. + */ +@Module +final class InstrumentInjectorModule { + + private final InstrumentConfig instrumentConfig; + + private final String instrumentName; + + InstrumentInjectorModule(InstrumentConfig instrumentConfig, String instrumentName) { + this.instrumentConfig = instrumentConfig; + this.instrumentName = instrumentName; + } + + @Provides + InstrumentConfig provideInstrumentConfig() { + return instrumentConfig; + } + + @Provides + @InstrumentOptions + static ImmutableMap<String, String> provideInstrumentOptions(InstrumentConfig config) { + return config.options(); + } + + @Provides + @InstrumentName + String provideInstrumentName() { + return instrumentName; + } +} diff --git a/caliper/src/main/java/com/google/caliper/runner/InstrumentName.java b/caliper/src/main/java/com/google/caliper/runner/InstrumentName.java new file mode 100644 index 0000000..bef02c7 --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/runner/InstrumentName.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2013 Google Inc. + * + * 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.google.caliper.runner; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import javax.inject.Qualifier; + +/** Binding annotation for the name used to configure an instrument. */ +@Retention(RUNTIME) +@Target({FIELD, PARAMETER, METHOD}) +@Qualifier +@interface InstrumentName {} diff --git a/caliper/src/main/java/com/google/caliper/runner/InstrumentOptions.java b/caliper/src/main/java/com/google/caliper/runner/InstrumentOptions.java new file mode 100644 index 0000000..d6f0681 --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/runner/InstrumentOptions.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2012 Google Inc. + * + * 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.google.caliper.runner; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import javax.inject.Qualifier; + +/** Binding annotation for the options applied to an instrument. */ +@Retention(RUNTIME) +@Target({FIELD, PARAMETER, METHOD}) +@Qualifier +@interface InstrumentOptions {} diff --git a/caliper/src/main/java/com/google/caliper/runner/InvalidBenchmarkException.java b/caliper/src/main/java/com/google/caliper/runner/InvalidBenchmarkException.java new file mode 100644 index 0000000..71aaec2 --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/runner/InvalidBenchmarkException.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2011 Google Inc. + * + * 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.google.caliper.runner; + +import java.io.PrintWriter; + +/** + * + */ +@SuppressWarnings("serial") +public class InvalidBenchmarkException extends RuntimeException { + public InvalidBenchmarkException(String message, Object... args) { + super(String.format(message, fixArgs(args))); + } + + private static Object[] fixArgs(Object[] args) { + for (int i = 0; i < args.length; i++) { + if (args[i] instanceof Class) { + args[i] = ((Class<?>) args[i]).getSimpleName(); + } + } + return args; + } + + public void display(PrintWriter writer) { + writer.println(getMessage()); + } +} diff --git a/caliper/src/main/java/com/google/caliper/runner/InvalidInstrumentException.java b/caliper/src/main/java/com/google/caliper/runner/InvalidInstrumentException.java new file mode 100644 index 0000000..83db165 --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/runner/InvalidInstrumentException.java @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2011 Google Inc. + * + * 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.google.caliper.runner; + +import java.io.PrintWriter; + +/** + * + */ +@SuppressWarnings("serial") +public class InvalidInstrumentException extends RuntimeException { + public InvalidInstrumentException(String message, Object... args) { + super(String.format(message, args)); + } + + public void display(PrintWriter writer) { + printStackTrace(writer); + } +} diff --git a/caliper/src/main/java/com/google/caliper/runner/JarFinder.java b/caliper/src/main/java/com/google/caliper/runner/JarFinder.java new file mode 100644 index 0000000..174ef81 --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/runner/JarFinder.java @@ -0,0 +1,207 @@ +/* + * Copyright (C) 2013 The Guava 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 com.google.caliper.runner; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Splitter; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +import com.google.common.reflect.ClassPath; + +import java.io.File; +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.Map; +import java.util.Set; +import java.util.jar.Attributes; +import java.util.jar.JarFile; +import java.util.jar.Manifest; +import java.util.logging.Logger; + +import javax.annotation.Nullable; + +/** + * Scans the source of a {@link ClassLoader} and finds all jar files. This is a modified version + * of {@link ClassPath} that finds jars instead of resources. + */ +final class JarFinder { + private static final Logger logger = Logger.getLogger(JarFinder.class.getName()); + + /** Separator for the Class-Path manifest attribute value in jar files. */ + private static final Splitter CLASS_PATH_ATTRIBUTE_SEPARATOR = + Splitter.on(' ').omitEmptyStrings(); + + /** + * Returns a list of jar files reachable from the given class loaders. + * + * <p>Currently only {@link URLClassLoader} and only {@code file://} urls are supported. + * + * @throws IOException if the attempt to read class path resources (jar files or directories) + * failed. + */ + public static ImmutableSet<File> findJarFiles(ClassLoader first, ClassLoader... rest) + throws IOException { + Scanner scanner = new Scanner(); + Map<URI, ClassLoader> map = Maps.newLinkedHashMap(); + for (ClassLoader classLoader : Lists.asList(first, rest)) { + map.putAll(getClassPathEntries(classLoader)); + } + for (Map.Entry<URI, ClassLoader> entry : map.entrySet()) { + scanner.scan(entry.getKey(), entry.getValue()); + } + return scanner.jarFiles(); + } + + @VisibleForTesting static ImmutableMap<URI, ClassLoader> getClassPathEntries( + ClassLoader classloader) { + Map<URI, ClassLoader> entries = Maps.newLinkedHashMap(); + // Search parent first, since it's the order ClassLoader#loadClass() uses. + ClassLoader parent = classloader.getParent(); + if (parent != null) { + entries.putAll(getClassPathEntries(parent)); + } + if (classloader instanceof URLClassLoader) { + URLClassLoader urlClassLoader = (URLClassLoader) classloader; + for (URL entry : urlClassLoader.getURLs()) { + URI uri; + try { + uri = entry.toURI(); + } catch (URISyntaxException e) { + throw new IllegalArgumentException(e); + } + if (!entries.containsKey(uri)) { + entries.put(uri, classloader); + } + } + } + return ImmutableMap.copyOf(entries); + } + + @VisibleForTesting static final class Scanner { + private final ImmutableSet.Builder<File> jarFiles = new ImmutableSet.Builder<File>(); + private final Set<URI> scannedUris = Sets.newHashSet(); + + ImmutableSet<File> jarFiles() { + return jarFiles.build(); + } + + void scan(URI uri, ClassLoader classloader) throws IOException { + if (uri.getScheme().equals("file") && scannedUris.add(uri)) { + scanFrom(new File(uri), classloader); + } + } + + @VisibleForTesting void scanFrom(File file, ClassLoader classloader) + throws IOException { + if (!file.exists()) { + return; + } + if (file.isDirectory()) { + scanDirectory(file, classloader); + } else { + scanJar(file, classloader); + } + } + + private void scanDirectory(File directory, ClassLoader classloader) { + scanDirectory(directory, classloader, ""); + } + + private void scanDirectory( + File directory, ClassLoader classloader, String packagePrefix) { + for (File file : directory.listFiles()) { + String name = file.getName(); + if (file.isDirectory()) { + scanDirectory(file, classloader, packagePrefix + name + "/"); + } + // do we need to look for jars here? + } + } + + private void scanJar(File file, ClassLoader classloader) throws IOException { + JarFile jarFile; + try { + jarFile = new JarFile(file); + } catch (IOException e) { + // Not a jar file + return; + } + jarFiles.add(file); + try { + for (URI uri : getClassPathFromManifest(file, jarFile.getManifest())) { + scan(uri, classloader); + } + } finally { + try { + jarFile.close(); + } catch (IOException ignored) {} + } + } + + /** + * Returns the class path URIs specified by the {@code Class-Path} manifest attribute, according + * to <a + * href="http://docs.oracle.com/javase/6/docs/technotes/guides/jar/jar.html#Main%20Attributes"> + * JAR File Specification</a>. If {@code manifest} is null, it means the jar file has no + * manifest, and an empty set will be returned. + */ + @VisibleForTesting static ImmutableSet<URI> getClassPathFromManifest( + File jarFile, @Nullable Manifest manifest) { + if (manifest == null) { + return ImmutableSet.of(); + } + ImmutableSet.Builder<URI> builder = ImmutableSet.builder(); + String classpathAttribute = manifest.getMainAttributes() + .getValue(Attributes.Name.CLASS_PATH.toString()); + if (classpathAttribute != null) { + for (String path : CLASS_PATH_ATTRIBUTE_SEPARATOR.split(classpathAttribute)) { + URI uri; + try { + uri = getClassPathEntry(jarFile, path); + } catch (URISyntaxException e) { + // Ignore bad entry + logger.warning("Invalid Class-Path entry: " + path); + continue; + } + builder.add(uri); + } + } + return builder.build(); + } + + /** + * Returns the absolute uri of the Class-Path entry value as specified in + * <a + * href="http://docs.oracle.com/javase/6/docs/technotes/guides/jar/jar.html#Main%20Attributes"> + * JAR File Specification</a>. Even though the specification only talks about relative urls, + * absolute urls are actually supported too (for example, in Maven surefire plugin). + */ + @VisibleForTesting static URI getClassPathEntry(File jarFile, String path) + throws URISyntaxException { + URI uri = new URI(path); + return uri.isAbsolute() + ? uri + : new File(jarFile.getParentFile(), path.replace('/', File.separatorChar)).toURI(); + } + } +} diff --git a/caliper/src/main/java/com/google/caliper/runner/LocalPort.java b/caliper/src/main/java/com/google/caliper/runner/LocalPort.java new file mode 100644 index 0000000..08f7314 --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/runner/LocalPort.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2013 Google Inc. + * + * 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.google.caliper.runner; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import javax.inject.Qualifier; + +/** Binding annotation for the port to which the worker has bound. */ +@Retention(RUNTIME) +@Target({FIELD, PARAMETER, METHOD}) +@Qualifier +@interface LocalPort {} diff --git a/caliper/src/main/java/com/google/caliper/runner/MainComponent.java b/caliper/src/main/java/com/google/caliper/runner/MainComponent.java new file mode 100644 index 0000000..22bcd75 --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/runner/MainComponent.java @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2015 Google Inc. + * + * 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.google.caliper.runner; + +import com.google.caliper.bridge.BridgeModule; +import com.google.caliper.config.CaliperConfig; +import com.google.caliper.config.ConfigModule; +import com.google.caliper.json.GsonModule; +import com.google.caliper.options.CaliperOptions; +import com.google.caliper.options.OptionsModule; +import com.google.caliper.util.OutputModule; +import com.google.common.util.concurrent.ServiceManager; + +import dagger.Component; + +import javax.inject.Singleton; + +/** + * The main component used when running caliper. + */ +@Singleton +@Component(modules = { + BenchmarkClassModule.class, + BridgeModule.class, + ConfigModule.class, + ExperimentingRunnerModule.class, + GsonModule.class, + MainModule.class, + OptionsModule.class, + OutputModule.class, + PlatformModule.class, + RunnerModule.class, + ServiceModule.class, +}) +interface MainComponent { + + BenchmarkClass getBenchmarkClass(); + + CaliperConfig getCaliperConfig(); + + CaliperOptions getCaliperOptions(); + + CaliperRun getCaliperRun(); + + ServiceManager getServiceManager(); + + TrialScopeComponent newTrialComponent(TrialModule trialModule); + + ExperimentComponent newExperimentComponent(ExperimentModule experimentModule); +} diff --git a/caliper/src/main/java/com/google/caliper/runner/MainModule.java b/caliper/src/main/java/com/google/caliper/runner/MainModule.java new file mode 100644 index 0000000..86ab6f9 --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/runner/MainModule.java @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2015 Google Inc. + * + * 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.google.caliper.runner; + +import com.google.caliper.options.CaliperOptions; +import com.google.caliper.util.InvalidCommandException; +import com.google.caliper.util.Util; +import dagger.Module; +import dagger.Provides; +import javax.inject.Singleton; + +/** + * Provides bindings to integrate other modules into the {@link MainComponent}. + */ +@Module +class MainModule { + + private static Class<?> benchmarkClassForName(String className) + throws InvalidCommandException, UserCodeException { + try { + return Util.lenientClassForName(className); + } catch (ClassNotFoundException e) { + throw new InvalidCommandException("Benchmark class not found: " + className); + } catch (ExceptionInInitializerError e) { + throw new UserCodeException( + "Exception thrown while initializing class '" + className + "'", e.getCause()); + } catch (NoClassDefFoundError e) { + throw new UserCodeException("Unable to load " + className, e); + } + } + + @Provides + @Singleton + @Running.BenchmarkClass + static Class<?> provideBenchmarkClass(CaliperOptions options) { + return benchmarkClassForName(options.benchmarkClassName()); + } +} diff --git a/caliper/src/main/java/com/google/caliper/runner/NanoTimeGranularity.java b/caliper/src/main/java/com/google/caliper/runner/NanoTimeGranularity.java new file mode 100644 index 0000000..737b0c7 --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/runner/NanoTimeGranularity.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2013 Google Inc. + * + * 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.google.caliper.runner; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import javax.inject.Qualifier; + +/** Binding annotation for the granularity of {@link System#nanoTime()}. */ +@Retention(RUNTIME) +@Target({FIELD, PARAMETER, METHOD}) +@Qualifier +@interface NanoTimeGranularity {} diff --git a/caliper/src/main/java/com/google/caliper/runner/NanoTimeGranularityTester.java b/caliper/src/main/java/com/google/caliper/runner/NanoTimeGranularityTester.java new file mode 100644 index 0000000..ee135bf --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/runner/NanoTimeGranularityTester.java @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2013 Google Inc. + * + * 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.google.caliper.runner; + +import static java.math.RoundingMode.CEILING; +import static java.util.concurrent.TimeUnit.NANOSECONDS; + +import com.google.caliper.util.ShortDuration; +import com.google.common.base.Ticker; +import com.google.common.math.LongMath; + +/** + * A utility that calculates the finest granularity that can be expected from subsequent calls to + * {@link System#nanoTime()}. Note that this utility necessarily invokes {@link System#nanoTime()} + * directly rather than using {@link Ticker} because the extra indirection might cause additional + * overhead. + */ +final class NanoTimeGranularityTester { + private static final int TRIALS = 1000; + + ShortDuration testNanoTimeGranularity() { + long total = 0L; + for (int i = 0; i < TRIALS; i++) { + long first = System.nanoTime(); + long second = System.nanoTime(); + long third = System.nanoTime(); + long fourth = System.nanoTime(); + long fifth = System.nanoTime(); + long sixth = System.nanoTime(); + long seventh = System.nanoTime(); + long eighth = System.nanoTime(); + long ninth = System.nanoTime(); + total += second - first; + total += third - second; + total += fourth - third; + total += fifth - fourth; + total += sixth - fifth; + total += seventh - sixth; + total += eighth - seventh; + total += ninth - eighth; + } + return ShortDuration.of(LongMath.divide(total, TRIALS * 8, CEILING), NANOSECONDS); + } +} diff --git a/caliper/src/main/java/com/google/caliper/runner/OutputFileDumper.java b/caliper/src/main/java/com/google/caliper/runner/OutputFileDumper.java new file mode 100644 index 0000000..6a9e892 --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/runner/OutputFileDumper.java @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2011 Google Inc. + * + * 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.google.caliper.runner; + +import static java.util.logging.Level.SEVERE; + +import com.google.caliper.api.ResultProcessor; +import com.google.caliper.config.CaliperConfig; +import com.google.caliper.config.InvalidConfigurationException; +import com.google.caliper.config.ResultProcessorConfig; +import com.google.caliper.model.Run; +import com.google.caliper.model.Trial; +import com.google.caliper.options.CaliperDirectory; +import com.google.common.base.Charsets; +import com.google.common.base.Optional; +import com.google.common.io.Files; +import com.google.gson.Gson; +import com.google.gson.stream.JsonWriter; + +import org.joda.time.format.ISODateTimeFormat; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.util.logging.Logger; + +import javax.inject.Inject; + +/** + * {@link ResultProcessor} that dumps the output data to a file in JSON format. By default, the + * output will be dumped to a file called + * {@code ~/.caliper/results/[benchmark classname].[timestamp].json}; if it exists and is a file, + * the file will be overwritten. The location can be overridden as either a file or a directory + * using either the {@code file} or {@code dir} options respectively. + */ +final class OutputFileDumper implements ResultProcessor { + private static final Logger logger = Logger.getLogger(OutputFileDumper.class.getName()); + + private final Run run; + private final Gson gson; + private final File resultFile; + private final File workFile; + + private Optional<JsonWriter> writer = Optional.absent(); + + @Inject OutputFileDumper(Run run, + BenchmarkClass benchmarkClass, + Gson gson, + CaliperConfig caliperConfig, + @CaliperDirectory File caliperDirectory) throws InvalidConfigurationException { + this.run = run; + ResultProcessorConfig config = caliperConfig.getResultProcessorConfig(OutputFileDumper.class); + if (config.options().containsKey("file")) { + this.resultFile = new File(config.options().get("file")); + logger.finer("found an output file in the configuration"); + } else if (config.options().containsKey("dir")) { + File dir = new File(config.options().get("dir")); + if (dir.isFile()) { + throw new InvalidConfigurationException("specified a directory, but it's a file"); + } + this.resultFile = new File(dir, createFileName(benchmarkClass.name())); + logger.finer("found an output directory in the configuration"); + } else { + this.resultFile = + new File(new File(caliperDirectory, "results"), createFileName(benchmarkClass.name())); + logger.fine("found no configuration"); + } + logger.fine(String.format("using %s for results", resultFile)); + this.gson = gson; + this.workFile = new File(resultFile.getPath() + ".tmp"); + } + + private String createFileName(String benchmarkName) { + return String.format("%s.%s.json", benchmarkName, createTimestamp()); + } + + private String createTimestamp() { + return ISODateTimeFormat.dateTimeNoMillis().print(run.startTime()); + } + + @Override public void processTrial(Trial trial) { + if (!writer.isPresent()) { + try { + Files.createParentDirs(workFile); + JsonWriter writer = + new JsonWriter(new OutputStreamWriter(new FileOutputStream(workFile), Charsets.UTF_8)); + writer.setIndent(" "); // always pretty print + writer.beginArray(); + this.writer = Optional.of(writer); + } catch (IOException e) { + logger.log(SEVERE, String.format( + "An error occured writing trial %s. Results in %s will be incomplete.", trial.id(), + resultFile), e); + } + } + if (writer.isPresent()) { + gson.toJson(trial, Trial.class, writer.get()); + } + } + + @Override public void close() throws IOException { + if (writer.isPresent()) { + writer.get().endArray().close(); + } + if (workFile.exists()) { + Files.move(workFile, resultFile); + } + } +} diff --git a/caliper/src/main/java/com/google/caliper/runner/Parameter.java b/caliper/src/main/java/com/google/caliper/runner/Parameter.java new file mode 100644 index 0000000..25d9484 --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/runner/Parameter.java @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2011 Google Inc. + * + * 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.google.caliper.runner; + +import com.google.caliper.Param; +import com.google.caliper.util.Parser; +import com.google.caliper.util.Parsers; +import com.google.caliper.util.Util; +import com.google.common.collect.ImmutableCollection; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; +import com.google.common.primitives.Primitives; + +import java.lang.reflect.Field; +import java.text.ParseException; + +/** + * Represents an injectable parameter, marked with one of @Param, @VmParam. Has nothing to do with + * particular choices of <i>values</i> for this parameter (except that it knows how to find the + * <i>default</i> values). + */ +public final class Parameter { + public static Parameter create(Field field) throws InvalidBenchmarkException { + return new Parameter(field); + } + + private final Field field; + private final Parser<?> parser; + private final ImmutableList<String> defaults; + + public Parameter(Field field) throws InvalidBenchmarkException { + if (Util.isStatic(field)) { + throw new InvalidBenchmarkException("Parameter field '%s' must not be static", + field.getName()); + } + if (RESERVED_NAMES.contains(field.getName())) { + throw new InvalidBenchmarkException("Class '%s' uses reserved parameter name '%s'", + field.getDeclaringClass(), field.getName()); + } + + this.field = field; + field.setAccessible(true); + + Class<?> type = Primitives.wrap(field.getType()); + try { + this.parser = Parsers.conventionalParser(type); + } catch (NoSuchMethodException e) { + throw new InvalidBenchmarkException("Type '%s' of parameter field '%s' has no recognized " + + "String-converting method; see <TODO> for details", type, field.getName()); + } + + this.defaults = findDefaults(field); + validate(defaults); + } + + void validate(ImmutableCollection<String> values) throws InvalidBenchmarkException { + for (String valueAsString : values) { + try { + parser.parse(valueAsString); + } catch (ParseException e) { + throw new InvalidBenchmarkException( + "Cannot convert value '%s' to type '%s': %s", + valueAsString, field.getType(), e.getMessage()); + } + } + } + + static final ImmutableSet<String> RESERVED_NAMES = ImmutableSet.of( + "benchmark", + "environment", + "instrument", + "measurement", // e.g. runtime, allocation, etc. + "run", + "trial", // currently unused, but we might need it + "vm"); + + String name() { + return field.getName(); + } + + ImmutableList<String> defaults() { + return defaults; + } + + void inject(Object benchmark, String value) { + try { + Object o = parser.parse(value); + field.set(benchmark, o); + } catch (ParseException impossible) { + // already validated both defaults and command-line + throw new AssertionError(impossible); + } catch (IllegalAccessException impossible) { + throw new AssertionError(impossible); + } + } + + private static ImmutableList<String> findDefaults(Field field) { + String[] defaultsAsStrings = field.getAnnotation(Param.class).value(); + if (defaultsAsStrings.length > 0) { + return ImmutableList.copyOf(defaultsAsStrings); + } + + Class<?> type = field.getType(); + if (type == boolean.class) { + return ALL_BOOLEANS; + } + + if (type.isEnum()) { + ImmutableList.Builder<String> builder = ImmutableList.builder(); + for (Object enumConstant : type.getEnumConstants()) { + builder.add(enumConstant.toString()); + } + return builder.build(); + } + return ImmutableList.of(); + } + + private static final ImmutableList<String> ALL_BOOLEANS = ImmutableList.of("true", "false"); +} diff --git a/caliper/src/main/java/com/google/caliper/runner/ParameterSet.java b/caliper/src/main/java/com/google/caliper/runner/ParameterSet.java new file mode 100644 index 0000000..640e52f --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/runner/ParameterSet.java @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2011 Google Inc. + * + * 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.google.caliper.runner; + +import com.google.common.collect.ImmutableCollection; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSetMultimap; +import com.google.common.collect.ImmutableSortedMap; +import com.google.common.collect.Ordering; +import com.google.common.collect.Sets; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Field; +import java.util.Map; +import java.util.Set; + +/** + * Represents all the injectable parameter fields of a single kind (@Param or @VmParam) found in a + * benchmark class. Has nothing to do with particular choices of <i>values</i> for these parameters + * (except that it knows how to find the <i>default</i> values). + */ +public final class ParameterSet { + public static ParameterSet create(Class<?> theClass, Class<? extends Annotation> annotationClass) + throws InvalidBenchmarkException { + // deterministic order, not reflection order + ImmutableMap.Builder<String, Parameter> parametersBuilder = + ImmutableSortedMap.naturalOrder(); + + for (Field field : theClass.getDeclaredFields()) { + if (field.isAnnotationPresent(annotationClass)) { + Parameter parameter = Parameter.create(field); + parametersBuilder.put(field.getName(), parameter); + } + } + return new ParameterSet(parametersBuilder.build()); + } + + final ImmutableMap<String, Parameter> map; + + private ParameterSet(ImmutableMap<String, Parameter> map) { + this.map = map; + } + + public Set<String> names() { + return map.keySet(); + } + + public Parameter get(String name) { + return map.get(name); + } + + public ImmutableSetMultimap<String, String> fillInDefaultsFor( + ImmutableSetMultimap<String, String> explicitValues) throws InvalidBenchmarkException { + ImmutableSetMultimap.Builder<String, String> combined = ImmutableSetMultimap.builder(); + + // For user parameters, this'll actually be the same as fromClass.keySet(), since any extras + // given at the command line are treated as errors; for VM parameters this is not the case. + for (String name : Sets.union(map.keySet(), explicitValues.keySet())) { + Parameter parameter = map.get(name); + ImmutableCollection<String> values = explicitValues.containsKey(name) + ? explicitValues.get(name) + : parameter.defaults(); + + combined.putAll(name, values); + if (values.isEmpty()) { + throw new InvalidBenchmarkException("ERROR: No default value provided for " + name); + } + } + return combined.orderKeysBy(Ordering.natural()).build(); + } + + public void injectAll(Object benchmark, Map<String, String> actualValues) { + for (Parameter parameter : map.values()) { + String value = actualValues.get(parameter.name()); + parameter.inject(benchmark, value); + } + } +} diff --git a/caliper/src/main/java/com/google/caliper/runner/PlatformModule.java b/caliper/src/main/java/com/google/caliper/runner/PlatformModule.java new file mode 100644 index 0000000..2e61989 --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/runner/PlatformModule.java @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2015 Google Inc. + * + * 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.google.caliper.runner; + +import com.google.caliper.platform.Platform; +import com.google.caliper.platform.dalvik.DalvikModule; +import com.google.caliper.platform.dalvik.DalvikPlatform; +import com.google.caliper.platform.jvm.JvmModule; +import com.google.caliper.platform.jvm.JvmPlatform; +import com.google.common.base.Optional; + +import dagger.Module; +import dagger.Provides; + +import javax.inject.Provider; + +/** + * Provider of a {@link Platform} instance appropriate for the current platform. + */ +@Module(includes = {JvmModule.class, DalvikModule.class}) +public final class PlatformModule { + + /** + * Chooses the {@link DalvikPlatform} if available, otherwise uses the default + * {@link JvmPlatform}. + */ + @Provides + static Platform providePlatform( + Optional<DalvikPlatform> optionalDalvikPlatform, + Provider<JvmPlatform> jvmPlatformProvider) { + if (optionalDalvikPlatform.isPresent()) { + return optionalDalvikPlatform.get(); + } else { + return jvmPlatformProvider.get(); + } + } +} diff --git a/caliper/src/main/java/com/google/caliper/runner/ProxyWorkerException.java b/caliper/src/main/java/com/google/caliper/runner/ProxyWorkerException.java new file mode 100644 index 0000000..7b814d0 --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/runner/ProxyWorkerException.java @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2012 Google Inc. + * + * 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.google.caliper.runner; + +import com.google.common.base.Joiner; +import com.google.common.base.Splitter; + + +/** + * An exception created on the runner with the same stack trace as one thrown on the worker that + * reports the actual exception class and message in its message. + */ +final class ProxyWorkerException extends RuntimeException { + ProxyWorkerException(String stackTrace) { + super(formatMesssage(stackTrace)); + } + + private static String formatMesssage(String stackTrace) { + StringBuilder builder = new StringBuilder(stackTrace.length() + 512) + .append("An exception occurred in a worker process. The stack trace is as follows:\n\t"); + Joiner.on("\n\t").appendTo(builder, Splitter.on('\n').split(stackTrace)); + return builder.toString(); + } +} diff --git a/caliper/src/main/java/com/google/caliper/runner/ResultProcessorCreator.java b/caliper/src/main/java/com/google/caliper/runner/ResultProcessorCreator.java new file mode 100644 index 0000000..fc278c5 --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/runner/ResultProcessorCreator.java @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2015 Google Inc. + * + * 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.google.caliper.runner; + +import com.google.caliper.api.ResultProcessor; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; + +/** + * Responsible for creating instances of configured {@link ResultProcessor}. + */ +final class ResultProcessorCreator { + + public static final String NO_PUBLIC_DEFAULT_CONSTRUCTOR = + "ResultProcessor %s not supported as it does not have a public default constructor"; + + private ResultProcessorCreator() { + } + + static ResultProcessor createResultProcessor(Class<? extends ResultProcessor> processorClass) { + ResultProcessor resultProcessor; + + try { + Constructor<? extends ResultProcessor> constructor = processorClass.getConstructor(); + resultProcessor = constructor.newInstance(); + } catch (NoSuchMethodException e) { + throw new UserCodeException(String.format(NO_PUBLIC_DEFAULT_CONSTRUCTOR, processorClass), e); + } catch (InvocationTargetException e) { + throw new UserCodeException("ResultProcessor %s could not be instantiated", e.getCause()); + } catch (InstantiationException e) { + throw new UserCodeException("ResultProcessor %s could not be instantiated", e); + } catch (IllegalAccessException e) { + throw new UserCodeException("ResultProcessor %s could not be instantiated", e); + } + + return resultProcessor; + } +} diff --git a/caliper/src/main/java/com/google/caliper/runner/ResultsUploader.java b/caliper/src/main/java/com/google/caliper/runner/ResultsUploader.java new file mode 100644 index 0000000..8e654d5 --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/runner/ResultsUploader.java @@ -0,0 +1,137 @@ +/* + * Copyright (C) 2011 Google Inc. + * + * 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.google.caliper.runner; + +import static java.util.logging.Level.SEVERE; +import static javax.ws.rs.core.MediaType.APPLICATION_JSON_TYPE; + +import com.google.caliper.api.ResultProcessor; +import com.google.caliper.config.InvalidConfigurationException; +import com.google.caliper.config.ResultProcessorConfig; +import com.google.caliper.model.Trial; +import com.google.common.base.Optional; +import com.google.common.base.Strings; +import com.google.common.collect.ImmutableList; +import com.google.gson.Gson; + +import com.sun.jersey.api.client.Client; +import com.sun.jersey.api.client.ClientHandlerException; +import com.sun.jersey.api.client.UniformInterfaceException; +import com.sun.jersey.api.client.WebResource; + +import java.io.PrintWriter; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.UUID; +import java.util.logging.Logger; + +import javax.annotation.Nullable; + +/** + * {@link ResultProcessor} implementation that uploads the JSON-serialized results to the Caliper + * webapp. + */ +abstract class ResultsUploader implements ResultProcessor { + private static final Logger logger = Logger.getLogger(ResultsUploader.class.getName()); + private static final String POST_PATH = "/data/trials"; + private static final String RESULTS_PATH_PATTERN = "/runs/%s"; + + private final PrintWriter stdout; + private final Client client; + private final Gson gson; + private final Optional<UUID> apiKey; + private final Optional<URI> uploadUri; + private Optional<UUID> runId = Optional.absent(); + private boolean failure = false; + + ResultsUploader(PrintWriter stdout, Gson gson, Client client, + ResultProcessorConfig resultProcessorConfig) throws InvalidConfigurationException { + this.stdout = stdout; + this.client = client; + this.gson = gson; + @Nullable String apiKeyString = resultProcessorConfig.options().get("key"); + Optional<UUID> apiKey = Optional.absent(); + if (Strings.isNullOrEmpty(apiKeyString)) { + logger.info("No api key specified. Uploading results anonymously."); + } else { + try { + apiKey = Optional.of(UUID.fromString(apiKeyString)); + } catch (IllegalArgumentException e) { + throw new InvalidConfigurationException(String.format( + "The specified API key (%s) is not valid. API keys are UUIDs and should look like %s.", + apiKeyString, new UUID(0L, 0L))); + } + } + this.apiKey = apiKey; + + @Nullable String urlString = resultProcessorConfig.options().get("url"); + if (Strings.isNullOrEmpty(urlString)) { + logger.info("No upload URL was specified. Results will not be uploaded."); + this.uploadUri = Optional.absent(); + } else { + try { + this.uploadUri = Optional.of(new URI(urlString).resolve(POST_PATH)); + } catch (URISyntaxException e) { + throw new InvalidConfigurationException(urlString + " is an invalid upload url", e); + } + } + } + + @Override public final void processTrial(Trial trial) { + if (uploadUri.isPresent()) { + WebResource resource = client.resource(uploadUri.get()); + if (apiKey.isPresent()) { + resource = resource.queryParam("key", apiKey.get().toString()); + } + boolean threw = true; + try { + // TODO(gak): make the json part happen automagically + resource.type(APPLICATION_JSON_TYPE).post(gson.toJson(ImmutableList.of(trial))); + // only set the run id if a result has been successfully uploaded + runId = Optional.of(trial.run().id()); + threw = false; + } catch (ClientHandlerException e) { + logUploadFailure(trial, e); + } catch (UniformInterfaceException e) { + logUploadFailure(trial, e); + logger.fine("Failed upload response: " + e.getResponse().getStatus()); + } finally { + failure |= threw; + } + } + } + + private static void logUploadFailure(Trial trial, Exception e) { + logger.log(SEVERE, String.format( + "Could not upload trial %s. Consider uploading it manually.", trial.id()), e); + } + + @Override public final void close() { + if (uploadUri.isPresent()) { + if (runId.isPresent()) { + stdout.printf("Results have been uploaded. View them at: %s%n", + uploadUri.get().resolve(String.format(RESULTS_PATH_PATTERN, runId.get()))); + } + if (failure) { + // TODO(gak): implement some retry + stdout.println("Some trials failed to upload. Consider uploading them manually."); + } + } else { + logger.fine("No upload URL was provided, so results were not uploaded."); + } + } +} diff --git a/caliper/src/main/java/com/google/caliper/runner/RunnerModule.java b/caliper/src/main/java/com/google/caliper/runner/RunnerModule.java new file mode 100644 index 0000000..a55fb03 --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/runner/RunnerModule.java @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2012 Google Inc. + * + * 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.google.caliper.runner; + +import com.google.caliper.config.CaliperConfig; +import com.google.caliper.config.InvalidConfigurationException; +import com.google.caliper.config.VmConfig; +import com.google.caliper.model.Run; +import com.google.caliper.options.CaliperOptions; +import com.google.caliper.platform.Platform; +import com.google.common.collect.ImmutableSet; + +import dagger.Module; +import dagger.Provides; + +import org.joda.time.Instant; + +import java.util.UUID; + +import javax.inject.Singleton; + +/** + * A Dagger module that configures bindings common to all {@link CaliperRun} implementations. + */ +// TODO(gak): throwing providers for all of the things that throw +@Module +final class RunnerModule { + @Provides + static ImmutableSet<VirtualMachine> provideVirtualMachines( + CaliperOptions options, + CaliperConfig config, + Platform platform) + throws InvalidConfigurationException { + ImmutableSet<String> vmNames = options.vmNames(); + ImmutableSet.Builder<VirtualMachine> builder = ImmutableSet.builder(); + if (vmNames.isEmpty()) { + builder.add(new VirtualMachine("default", config.getDefaultVmConfig(platform))); + } else { + for (String vmName : vmNames) { + VmConfig vmConfig = config.getVmConfig(platform, vmName); + builder.add(new VirtualMachine(vmName, vmConfig)); + } + } + return builder.build(); + } + + @Provides + static Instant provideInstant() { + return Instant.now(); + } + + @Provides static CaliperRun provideCaliperRun(ExperimentingCaliperRun experimentingCaliperRun) { + return experimentingCaliperRun; + } + + @Provides @Singleton + static Run provideRun(UUID uuid, CaliperOptions caliperOptions, Instant startTime) { + return new Run.Builder(uuid).label(caliperOptions.runName()).startTime(startTime).build(); + } +} diff --git a/caliper/src/main/java/com/google/caliper/runner/Running.java b/caliper/src/main/java/com/google/caliper/runner/Running.java new file mode 100644 index 0000000..0ff4c7e --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/runner/Running.java @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2013 Google Inc. + * + * 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.google.caliper.runner; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import javax.inject.Qualifier; + +/** + * A collection of annotations for bindings pertaining to the currently running experiment. + */ +public class Running { + private Running() {} + + @Retention(RUNTIME) + @Target({FIELD, PARAMETER, METHOD}) + @Qualifier + public @interface Benchmark {} + + @Retention(RUNTIME) + @Target({FIELD, PARAMETER, METHOD}) + @Qualifier + public @interface BenchmarkMethod {} + + @Retention(RUNTIME) + @Target({FIELD, PARAMETER, METHOD}) + @Qualifier + public @interface BenchmarkClass {} + + @Retention(RUNTIME) + @Target({FIELD, PARAMETER, METHOD}) + @Qualifier + public @interface BeforeExperimentMethods {} + + @Retention(RUNTIME) + @Target({FIELD, PARAMETER, METHOD}) + @Qualifier + public @interface AfterExperimentMethods {} +} diff --git a/caliper/src/main/java/com/google/caliper/runner/RuntimeInstrument.java b/caliper/src/main/java/com/google/caliper/runner/RuntimeInstrument.java new file mode 100644 index 0000000..3187dfd --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/runner/RuntimeInstrument.java @@ -0,0 +1,469 @@ +/* + * Copyright (C) 2013 Google Inc. + * + * 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.google.caliper.runner; + +import static com.google.caliper.runner.CommonInstrumentOptions.GC_BEFORE_EACH_OPTION; +import static com.google.caliper.runner.CommonInstrumentOptions.MAX_WARMUP_WALL_TIME_OPTION; +import static com.google.caliper.runner.CommonInstrumentOptions.MEASUREMENTS_OPTION; +import static com.google.caliper.runner.CommonInstrumentOptions.WARMUP_OPTION; +import static com.google.caliper.util.Reflection.getAnnotatedMethods; +import static com.google.caliper.util.Util.isStatic; +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.checkState; +import static com.google.common.base.Throwables.propagateIfInstanceOf; +import static java.util.concurrent.TimeUnit.MILLISECONDS; +import static java.util.concurrent.TimeUnit.NANOSECONDS; + +import com.google.caliper.Benchmark; +import com.google.caliper.api.AfterRep; +import com.google.caliper.api.BeforeRep; +import com.google.caliper.api.Macrobenchmark; +import com.google.caliper.api.SkipThisScenarioException; +import com.google.caliper.bridge.AbstractLogMessageVisitor; +import com.google.caliper.bridge.GcLogMessage; +import com.google.caliper.bridge.HotspotLogMessage; +import com.google.caliper.bridge.StartMeasurementLogMessage; +import com.google.caliper.bridge.StopMeasurementLogMessage; +import com.google.caliper.model.Measurement; +import com.google.caliper.platform.Platform; +import com.google.caliper.platform.SupportedPlatform; +import com.google.caliper.util.ShortDuration; +import com.google.caliper.worker.MacrobenchmarkWorker; +import com.google.caliper.worker.RuntimeWorker; +import com.google.caliper.worker.Worker; +import com.google.common.base.Stopwatch; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Lists; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.math.BigDecimal; +import java.util.List; +import java.util.logging.Logger; + +import javax.annotation.Nullable; +import javax.inject.Inject; + +/** + * The instrument responsible for measuring the runtime of {@link Benchmark} methods. + */ +@SupportedPlatform({Platform.Type.JVM, Platform.Type.DALVIK}) +class RuntimeInstrument extends Instrument { + private static final String SUGGEST_GRANULARITY_OPTION = "suggestGranularity"; + private static final String TIMING_INTERVAL_OPTION = "timingInterval"; + private static final int DRY_RUN_REPS = 1; + + private static final Logger logger = Logger.getLogger(RuntimeInstrument.class.getName()); + + private final ShortDuration nanoTimeGranularity; + + @Inject + RuntimeInstrument(@NanoTimeGranularity ShortDuration nanoTimeGranularity) { + this.nanoTimeGranularity = nanoTimeGranularity; + } + + @Override + public boolean isBenchmarkMethod(Method method) { + return method.isAnnotationPresent(Benchmark.class) + || BenchmarkMethods.isTimeMethod(method) + || method.isAnnotationPresent(Macrobenchmark.class); + } + + @Override + protected ImmutableSet<String> instrumentOptions() { + return ImmutableSet.of( + WARMUP_OPTION, MAX_WARMUP_WALL_TIME_OPTION, TIMING_INTERVAL_OPTION, MEASUREMENTS_OPTION, + GC_BEFORE_EACH_OPTION, SUGGEST_GRANULARITY_OPTION); + } + + @Override + public Instrumentation createInstrumentation(Method benchmarkMethod) + throws InvalidBenchmarkException { + checkNotNull(benchmarkMethod); + checkArgument(isBenchmarkMethod(benchmarkMethod)); + if (isStatic(benchmarkMethod)) { + throw new InvalidBenchmarkException("Benchmark methods must not be static: %s", + benchmarkMethod.getName()); + } + try { + switch (BenchmarkMethods.Type.of(benchmarkMethod)) { + case MACRO: + return new MacrobenchmarkInstrumentation(benchmarkMethod); + case MICRO: + return new MicrobenchmarkInstrumentation(benchmarkMethod); + case PICO: + return new PicobenchmarkInstrumentation(benchmarkMethod); + default: + throw new AssertionError("unknown type"); + } + } catch (IllegalArgumentException e) { + throw new InvalidBenchmarkException("Benchmark methods must have no arguments or accept " + + "a single int or long parameter: %s", benchmarkMethod.getName()); + } + } + + private class MacrobenchmarkInstrumentation extends Instrumentation { + MacrobenchmarkInstrumentation(Method benchmarkMethod) { + super(benchmarkMethod); + } + + @Override + public void dryRun(Object benchmark) throws UserCodeException { + ImmutableSet<Method> beforeRepMethods = + getAnnotatedMethods(benchmarkMethod.getDeclaringClass(), BeforeRep.class); + ImmutableSet<Method> afterRepMethods = + getAnnotatedMethods(benchmarkMethod.getDeclaringClass(), AfterRep.class); + try { + for (Method beforeRepMethod : beforeRepMethods) { + beforeRepMethod.invoke(benchmark); + } + try { + benchmarkMethod.invoke(benchmark); + } finally { + for (Method afterRepMethod : afterRepMethods) { + afterRepMethod.invoke(benchmark); + } + } + } catch (IllegalAccessException e) { + throw new AssertionError(e); + } catch (InvocationTargetException e) { + Throwable userException = e.getCause(); + propagateIfInstanceOf(userException, SkipThisScenarioException.class); + throw new UserCodeException(userException); + } + } + + @Override + public Class<? extends Worker> workerClass() { + return MacrobenchmarkWorker.class; + } + + @Override + MeasurementCollectingVisitor getMeasurementCollectingVisitor() { + return new SingleInvocationMeasurementCollector( + Integer.parseInt(options.get(MEASUREMENTS_OPTION)), + ShortDuration.valueOf(options.get(WARMUP_OPTION)), + ShortDuration.valueOf(options.get(MAX_WARMUP_WALL_TIME_OPTION)), + nanoTimeGranularity); + } + } + + @Override public TrialSchedulingPolicy schedulingPolicy() { + // Runtime measurements are currently believed to be too sensitive to system performance to + // allow parallel runners. + // TODO(lukes): investigate this, on a multicore system it seems like we should be able to + // allow some parallelism without corrupting results. + return TrialSchedulingPolicy.SERIAL; + } + + private abstract class RuntimeInstrumentation extends Instrumentation { + RuntimeInstrumentation(Method method) { + super(method); + } + + @Override public void dryRun(Object benchmark) throws UserCodeException { + try { + benchmarkMethod.invoke(benchmark, DRY_RUN_REPS); + } catch (IllegalAccessException impossible) { + throw new AssertionError(impossible); + } catch (InvocationTargetException e) { + Throwable userException = e.getCause(); + propagateIfInstanceOf(userException, SkipThisScenarioException.class); + throw new UserCodeException(userException); + } + } + + @Override public ImmutableMap<String, String> workerOptions() { + return ImmutableMap.of( + TIMING_INTERVAL_OPTION + "Nanos", toNanosString(TIMING_INTERVAL_OPTION), + GC_BEFORE_EACH_OPTION, options.get(GC_BEFORE_EACH_OPTION)); + } + + private String toNanosString(String optionName) { + return String.valueOf( + ShortDuration.valueOf(options.get(optionName)).to(NANOSECONDS)); + } + + @Override MeasurementCollectingVisitor getMeasurementCollectingVisitor() { + return new RepBasedMeasurementCollector( + getMeasurementsPerTrial(), + ShortDuration.valueOf(options.get(WARMUP_OPTION)), + ShortDuration.valueOf(options.get(MAX_WARMUP_WALL_TIME_OPTION)), + Boolean.parseBoolean(options.get(SUGGEST_GRANULARITY_OPTION)), + nanoTimeGranularity); + } + } + + private class MicrobenchmarkInstrumentation extends RuntimeInstrumentation { + MicrobenchmarkInstrumentation(Method benchmarkMethod) { + super(benchmarkMethod); + } + + @Override public Class<? extends Worker> workerClass() { + return RuntimeWorker.Micro.class; + } + } + + private int getMeasurementsPerTrial() { + @Nullable + String measurementsString = options.get(MEASUREMENTS_OPTION); + int measurementsPerTrial = + (measurementsString == null) ? 1 : Integer.parseInt(measurementsString); + // TODO(gak): fail faster + checkState(measurementsPerTrial > 0); + return measurementsPerTrial; + } + + private class PicobenchmarkInstrumentation extends RuntimeInstrumentation { + PicobenchmarkInstrumentation(Method benchmarkMethod) { + super(benchmarkMethod); + } + + @Override public Class<? extends Worker> workerClass() { + return RuntimeWorker.Pico.class; + } + } + + private abstract static class RuntimeMeasurementCollector extends AbstractLogMessageVisitor + implements MeasurementCollectingVisitor { + final int targetMeasurements; + final ShortDuration warmup; + final ShortDuration maxWarmupWallTime; + final List<Measurement> measurements = Lists.newArrayList(); + ShortDuration elapsedWarmup = ShortDuration.zero(); + boolean measuring = false; + boolean invalidateMeasurements = false; + boolean notifiedAboutGc = false; + boolean notifiedAboutJit = false; + boolean notifiedAboutMeasuringJit = false; + Stopwatch timeSinceStartOfTrial = Stopwatch.createUnstarted(); + final List<String> messages = Lists.newArrayList(); + final ShortDuration nanoTimeGranularity; + + RuntimeMeasurementCollector( + int targetMeasurements, + ShortDuration warmup, + ShortDuration maxWarmupWallTime, + ShortDuration nanoTimeGranularity) { + this.targetMeasurements = targetMeasurements; + this.warmup = warmup; + this.maxWarmupWallTime = maxWarmupWallTime; + this.nanoTimeGranularity = nanoTimeGranularity; + } + + @Override + public void visit(GcLogMessage logMessage) { + if (measuring && isWarmupComplete() && !notifiedAboutGc) { + gcWhileMeasuring(); + notifiedAboutGc = true; + } + } + + abstract void gcWhileMeasuring(); + + @Override + public void visit(HotspotLogMessage logMessage) { + if (isWarmupComplete()) { + if (measuring && notifiedAboutMeasuringJit) { + hotspotWhileMeasuring(); + notifiedAboutMeasuringJit = true; + } else if (notifiedAboutJit) { + hotspotWhileNotMeasuring(); + notifiedAboutJit = true; + } + } + } + + abstract void hotspotWhileMeasuring(); + + abstract void hotspotWhileNotMeasuring(); + + @Override + public void visit(StartMeasurementLogMessage logMessage) { + checkState(!measuring); + measuring = true; + if (!timeSinceStartOfTrial.isRunning()) { + timeSinceStartOfTrial.start(); + } + } + + @Override + public void visit(StopMeasurementLogMessage logMessage) { + checkState(measuring); + ImmutableList<Measurement> newMeasurements = logMessage.measurements(); + if (!isWarmupComplete()) { + for (Measurement measurement : newMeasurements) { + // TODO(gak): eventually we will need to resolve different units + checkArgument("ns".equals(measurement.value().unit())); + elapsedWarmup = elapsedWarmup.plus( + ShortDuration.of(BigDecimal.valueOf(measurement.value().magnitude()), NANOSECONDS)); + validateMeasurement(measurement); + } + } else { + if (!measuredWarmupDurationReached()) { + messages.add(String.format( + "WARNING: Warmup was interrupted because it took longer than %s of wall-clock time. " + + "%s was spent in the benchmark method for warmup " + + "(normal warmup duration should be %s).", + maxWarmupWallTime, elapsedWarmup, warmup)); + } + + if (invalidateMeasurements) { + logger.fine(String.format("Discarding %s as they were marked invalid.", newMeasurements)); + } else { + this.measurements.addAll(newMeasurements); + } + } + invalidateMeasurements = false; + measuring = false; + } + + abstract void validateMeasurement(Measurement measurement); + + @Override + public ImmutableList<Measurement> getMeasurements() { + return ImmutableList.copyOf(measurements); + } + + boolean measuredWarmupDurationReached() { + return elapsedWarmup.compareTo(warmup) >= 0; + } + + @Override + public boolean isWarmupComplete() { + // Fast macro-benchmarks (up to tens of ms) need lots of measurements to reach 10s of + // measured warmup time. Because of the per-measurement overhead of running @BeforeRep and + // @AfterRep, warmup can take very long. + // + // To prevent this, we enforce a cap on the wall-clock time here. + return measuredWarmupDurationReached() + || timeSinceStartOfTrial.elapsed(MILLISECONDS) > maxWarmupWallTime.to(MILLISECONDS); + } + + @Override + public boolean isDoneCollecting() { + return measurements.size() >= targetMeasurements; + } + + @Override + public ImmutableList<String> getMessages() { + return ImmutableList.copyOf(messages); + } + } + + private static final class RepBasedMeasurementCollector extends RuntimeMeasurementCollector { + final boolean suggestGranularity; + boolean notifiedAboutGranularity = false; + + RepBasedMeasurementCollector( + int measurementsPerTrial, + ShortDuration warmup, + ShortDuration maxWarmupWallTime, + boolean suggestGranularity, + ShortDuration nanoTimeGranularity) { + super(measurementsPerTrial, warmup, maxWarmupWallTime, nanoTimeGranularity); + this.suggestGranularity = suggestGranularity; + } + + @Override + void gcWhileMeasuring() { + invalidateMeasurements = true; + messages.add("ERROR: GC occurred during timing. Measurements were discarded."); + } + + @Override + void hotspotWhileMeasuring() { + invalidateMeasurements = true; + messages.add( + "ERROR: Hotspot compilation occurred during timing: warmup is likely insufficent. " + + "Measurements were discarded."); + } + + @Override + void hotspotWhileNotMeasuring() { + messages.add( + "WARNING: Hotspot compilation occurred after warmup, but outside of timing. " + + "Results may be affected. Run with --verbose to see which method was compiled."); + } + + @Override + void validateMeasurement(Measurement measurement) { + if (suggestGranularity) { + double nanos = measurement.value().magnitude() / measurement.weight(); + if (!notifiedAboutGranularity && ((nanos / 1000) > nanoTimeGranularity.to(NANOSECONDS))) { + notifiedAboutGranularity = true; + ShortDuration reasonableUpperBound = nanoTimeGranularity.times(1000); + messages.add(String.format("INFO: This experiment does not require a microbenchmark. " + + "The granularity of the timer (%s) is less than 0.1%% of the measured runtime. " + + "If all experiments for this benchmark have runtimes greater than %s, " + + "consider the macrobenchmark instrument.", nanoTimeGranularity, + reasonableUpperBound)); + } + } + } + } + + private static final class SingleInvocationMeasurementCollector + extends RuntimeMeasurementCollector { + + SingleInvocationMeasurementCollector( + int measurementsPerTrial, + ShortDuration warmup, + ShortDuration maxWarmupWallTime, + ShortDuration nanoTimeGranularity) { + super(measurementsPerTrial, warmup, maxWarmupWallTime, nanoTimeGranularity); + } + + @Override + void gcWhileMeasuring() { + messages.add("WARNING: GC occurred during timing. " + + "Depending on the scope of the benchmark, this might significantly impact results. " + + "Consider running with a larger heap size."); + } + + @Override + void hotspotWhileMeasuring() { + messages.add("WARNING: Hotspot compilation occurred during timing. " + + "Depending on the scope of the benchmark, this might significantly impact results. " + + "Consider running with a longer warmup."); + } + + @Override + void hotspotWhileNotMeasuring() { + messages.add( + "WARNING: Hotspot compilation occurred after warmup, but outside of timing. " + + "Depending on the scope of the benchmark, this might significantly impact results. " + + "Consider running with a longer warmup."); + } + + @Override + void validateMeasurement(Measurement measurement) { + double nanos = measurement.value().magnitude() / measurement.weight(); + if ((nanos / 1000) < nanoTimeGranularity.to(NANOSECONDS)) { + ShortDuration runtime = ShortDuration.of(BigDecimal.valueOf(nanos), NANOSECONDS); + throw new TrialFailureException(String.format( + "This experiment requires a microbenchmark. " + + "The granularity of the timer (%s) " + + "is greater than 0.1%% of the measured runtime (%s). " + + "Use the microbenchmark instrument for accurate measurements.", + nanoTimeGranularity, runtime)); + } + } + } +} + diff --git a/caliper/src/main/java/com/google/caliper/runner/RuntimeShutdownHookRegistrar.java b/caliper/src/main/java/com/google/caliper/runner/RuntimeShutdownHookRegistrar.java new file mode 100644 index 0000000..9caea04 --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/runner/RuntimeShutdownHookRegistrar.java @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2013 Google Inc. + * + * 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.google.caliper.runner; + +/** + * A {@link ShutdownHookRegistrar} that delegates to {@link Runtime}. + */ +class RuntimeShutdownHookRegistrar implements ShutdownHookRegistrar { + @Override public void addShutdownHook(Thread hook) { + Runtime.getRuntime().addShutdownHook(hook); + } + + @Override public boolean removeShutdownHook(Thread hook) { + return Runtime.getRuntime().removeShutdownHook(hook); + } +} diff --git a/caliper/src/main/java/com/google/caliper/runner/ScheduledTrial.java b/caliper/src/main/java/com/google/caliper/runner/ScheduledTrial.java new file mode 100644 index 0000000..fda696b --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/runner/ScheduledTrial.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2014 Google Inc. + * + * 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.google.caliper.runner; + +import java.util.concurrent.Callable; + +import javax.inject.Inject; + +/** + * A ScheduledTrial is a simple pair of a {@link TrialRunLoop} and a + * {@link TrialSchedulingPolicy}. + */ +@TrialScoped final class ScheduledTrial { + private final TrialRunLoop runLoop; + private final Experiment experiment; + private final TrialSchedulingPolicy policy; + + @Inject ScheduledTrial(Experiment experiment, TrialRunLoop runLoop, + TrialSchedulingPolicy policy) { + this.runLoop = runLoop; + this.experiment = experiment; + this.policy = policy; + } + + TrialSchedulingPolicy policy() { + return policy; + } + + Experiment experiment() { + return experiment; + } + + Callable<TrialResult> trialTask() { + return runLoop; + } +} diff --git a/caliper/src/main/java/com/google/caliper/runner/ServerSocketService.java b/caliper/src/main/java/com/google/caliper/runner/ServerSocketService.java new file mode 100644 index 0000000..ed93dd5 --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/runner/ServerSocketService.java @@ -0,0 +1,218 @@ +/* + * Copyright (C) 2013 Google Inc. + * + * 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.google.caliper.runner; + +import static com.google.common.base.Preconditions.checkState; + +import com.google.caliper.bridge.OpenedSocket; +import com.google.caliper.bridge.StartupAnnounceMessage; +import com.google.common.base.Supplier; +import com.google.common.collect.Maps; +import com.google.common.collect.Multimaps; +import com.google.common.collect.SetMultimap; +import com.google.common.collect.Sets; +import com.google.common.util.concurrent.AbstractExecutionThreadService; +import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.Service; +import com.google.common.util.concurrent.SettableFuture; + +import java.io.IOException; +import java.net.ServerSocket; +import java.net.Socket; +import java.net.SocketException; +import java.util.Collection; +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +import javax.annotation.concurrent.GuardedBy; +import javax.inject.Inject; +import javax.inject.Singleton; + +/** + * A {@link Service} that manages a {@link ServerSocket}. + * + * <p> This service provides two pieces of functionality: + * <ol> + * <li>It adapts {@link ServerSocket#accept()} to a {@link ListenableFuture} of an opened socket. + * <li>It demultiplexes incoming connections based on a {@link StartupAnnounceMessage} that is + * sent over the socket. + * </ol> + * + * <p>The {@linkplain State states} of this service are as follows: + * <ul> + * <li>{@linkplain State#NEW NEW} : Idle state, the {@link ServerSocket} is not open yet. + * <li>{@linkplain State#STARTING STARTING} : {@link ServerSocket} is opened + * <li>{@linkplain State#RUNNING RUNNING} : We are continuously accepting and parsing connections + * from the socket. + * <li>{@linkplain State#STOPPING STOPPING} : The server socket is closing and all pending + * connection requests are terminated, connection requests will fail immediately. + * <li>{@linkplain State#TERMINATED TERMINATED} : Idle state, the socket is closed. + * <li>{@linkplain State#FAILED FAILED} : The service will transition to failed if it encounters + * any errors while accepting connections or reading from connections. + * </ul> + * + * <p>Note to future self. There have been a few attempts to make it so that it is no longer + * necessary to dedicate a thread to this service (basically turn it into an AbstractIdleService). + * The general idea has been to make callers to getConnection invoke accept, here is why it didn't + * work. + * <ul> + * <li>If you make getConnection a blocking method that calls accept until it finds the + * connection with its id, then there is no way to deal with connections that never arrive. + * For example, if the worker crashes before connecting then the thread calling accept will + * block forever waiting for it. The only way to unblock a thread stuck on accept() is to + * close the socket (this holds for ServerSocketChannels and normal ServerSockets), but we + * cannot do that in this case because the socket is a shared resource. + * <li>If you make getConnection a non-blocking, polling based method then you expose yourself + * to potential deadlocks (due to missed signals) depending on what thread you poll from. + * If the polling thread is any of the threads that are involved with processing messages + * from the worker I believe there to be a deadlock risk. Basically, if the worker sends + * messages over its output streams and then calls Socket.connect, and no printing to stdout + * or stderr occurs while connecting. Then if the runner polls, but misses the connection + * and then tries to read again, it will deadlock. + * </ul> + */ +@Singleton +final class ServerSocketService extends AbstractExecutionThreadService { + private enum Source { REQUEST, ACCEPT} + + private final Lock lock = new ReentrantLock(); + + /** + * Contains futures that have either only been accepted or requested. Once both occur they are + * removed from this map. + */ + @GuardedBy("lock") + private final Map<UUID, SettableFuture<OpenedSocket>> halfFinishedConnections = Maps.newHashMap(); + + /** + * Contains the history of connections so we can ensure that each id is only accepted once and + * requested once. + */ + @GuardedBy("lock") + private final SetMultimap<Source, UUID> connectionState = Multimaps.newSetMultimap( + Maps.<Source, Collection<UUID>>newEnumMap(Source.class), + new Supplier<Set<UUID>>(){ + @Override public Set<UUID> get() { + return Sets.newHashSet(); + } + }); + + private ServerSocket serverSocket; + + @Inject ServerSocketService() {} + + int getPort() { + awaitRunning(); + checkState(serverSocket != null, "Socket has not been opened yet"); + return serverSocket.getLocalPort(); + } + + /** + * Returns a {@link ListenableFuture} for an open connection corresponding to the given id. + * + * <p>N.B. calling this method 'consumes' the connection and as such calling it twice with the + * same id will not work, the second future returned will never complete. Similarly calling it + * with an id that does not correspond to a worker trying to connect will also fail. + */ + public ListenableFuture<OpenedSocket> getConnection(UUID id) { + checkState(isRunning(), "You can only get connections from a running service: %s", this); + return getConnectionImpl(id, Source.REQUEST); + } + + @Override protected void startUp() throws Exception { + serverSocket = new ServerSocket(0 /* bind to any available port */); + } + + @Override protected void run() throws Exception { + while (isRunning()) { + Socket socket; + try { + socket = serverSocket.accept(); + } catch (SocketException e) { + // we were closed + return; + } + OpenedSocket openedSocket = OpenedSocket.fromSocket(socket); + + UUID id = ((StartupAnnounceMessage) openedSocket.reader().read()).trialId(); + // N.B. you should not call set with the lock held, to prevent same thread executors from + // running with the lock. + getConnectionImpl(id, Source.ACCEPT).set(openedSocket); + } + } + + /** + * Returns a {@link SettableFuture} from the map of connections. + * + * <p>This method has the following properties: + * <ul> + * <li>If the id is present in {@link #connectionState}, this will throw an + * {@link IllegalStateException}. + * <li>The id and source are recorded in {@link #connectionState} + * <li>If the future is already in {@link #halfFinishedConnections}, it is removed and + * returned. + * <li>If the future is not in {@link #halfFinishedConnections}, a new {@link SettableFuture} + * is added and then returned. + * + * <p>These features together ensure that each connection can only be accepted once, only + * requested once and once both have happened it will be removed from + * {@link #halfFinishedConnections}. + */ + private SettableFuture<OpenedSocket> getConnectionImpl(UUID id, Source source) { + lock.lock(); + try { + checkState(connectionState.put(source, id), "Connection for %s has already been %s", + id, source); + SettableFuture<OpenedSocket> future = halfFinishedConnections.get(id); + if (future == null) { + future = SettableFuture.create(); + halfFinishedConnections.put(id, future); + } else { + halfFinishedConnections.remove(id); + } + return future; + } finally { + lock.unlock(); + } + } + + @Override protected void triggerShutdown() { + try { + serverSocket.close(); + } catch (IOException e) { + // best effort... + } + } + + @Override protected void shutDown() throws Exception { + serverSocket.close(); + // Now we have either been asked to stop or have failed with some kind of exception, we want to + // notify all pending requests, so if there are any references outside of this class they will + // notice. + lock.lock(); + try { + for (SettableFuture<OpenedSocket> future : halfFinishedConnections.values()) { + future.setException(new Exception("The socket has been closed")); + } + halfFinishedConnections.clear(); + connectionState.clear(); + } finally { + lock.unlock(); + } + } +} diff --git a/caliper/src/main/java/com/google/caliper/runner/ServiceModule.java b/caliper/src/main/java/com/google/caliper/runner/ServiceModule.java new file mode 100644 index 0000000..92f179d --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/runner/ServiceModule.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2013 Google Inc. + * + * 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.google.caliper.runner; + +import com.google.common.util.concurrent.Service; +import com.google.common.util.concurrent.ServiceManager; +import dagger.Module; +import dagger.Provides; + +import java.util.Set; +import javax.inject.Singleton; + +/** Configures the {@link ServiceManager}. */ +@Module +final class ServiceModule { + @Provides + @Singleton + static ServiceManager provideServiceManager(Set<Service> services) { + return new ServiceManager(services); + } +} diff --git a/caliper/src/main/java/com/google/caliper/runner/ShutdownHookRegistrar.java b/caliper/src/main/java/com/google/caliper/runner/ShutdownHookRegistrar.java new file mode 100644 index 0000000..b0f8a75 --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/runner/ShutdownHookRegistrar.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2013 Google Inc. + * + * 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.google.caliper.runner; + +/** + * A simple interface for registering and deregistering shutdown hooks. + */ +interface ShutdownHookRegistrar { + /** + * Adds a hook to run at process shutdown. + * + * <p>See {@link Runtime#addShutdownHook(Thread)}. + */ + void addShutdownHook(Thread hook); + /** + * Removes a shutdown hook that was previously registered via {@link #addShutdownHook(Thread)}. + * + * <p>See {@link Runtime#removeShutdownHook(Thread)}. + */ + boolean removeShutdownHook(Thread hook); +} + diff --git a/caliper/src/main/java/com/google/caliper/runner/StreamService.java b/caliper/src/main/java/com/google/caliper/runner/StreamService.java new file mode 100644 index 0000000..a5852d0 --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/runner/StreamService.java @@ -0,0 +1,434 @@ +/* + * Copyright (C) 2013 Google Inc. + * + * 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.google.caliper.runner; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.checkState; + +import com.google.caliper.bridge.LogMessage; +import com.google.caliper.bridge.OpenedSocket; +import com.google.caliper.bridge.StopMeasurementLogMessage; +import com.google.caliper.model.Measurement; +import com.google.caliper.runner.StreamService.StreamItem.Kind; +import com.google.caliper.util.Parser; +import com.google.common.base.MoreObjects; +import com.google.common.base.MoreObjects.ToStringHelper; +import com.google.common.collect.Queues; +import com.google.common.io.Closeables; +import com.google.common.io.LineReader; +import com.google.common.util.concurrent.AbstractService; +import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.ListeningExecutorService; +import com.google.common.util.concurrent.MoreExecutors; +import com.google.common.util.concurrent.Service; // for javadoc +import com.google.common.util.concurrent.Service.State; // for javadoc +import com.google.common.util.concurrent.ThreadFactoryBuilder; +import com.google.common.util.concurrent.Uninterruptibles; + +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.Reader; +import java.io.Serializable; +import java.nio.charset.Charset; +import java.text.ParseException; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.logging.Logger; + +import javax.annotation.Nullable; +import javax.inject.Inject; + +/** + * A {@link Service} that establishes a connection over a socket to a process and then allows + * multiplexed access to the processes' line oriented output over the socket and the standard + * process streams (stdout and stderr) as well as allowing data to be written over the socket. + * + * <p>The {@linkplain State states} of this service are as follows: + * <ul> + * <li>{@linkplain State#NEW NEW} : Idle state, no reading or writing is allowed. + * <li>{@linkplain State#STARTING STARTING} : Streams are being opened + * <li>{@linkplain State#RUNNING RUNNING} : At least one stream is still open or the writer has + * not been closed yet. + * <li>{@linkplain State#STOPPING STOPPING} : All streams have closed but some threads may still + * be running. + * <li>{@linkplain State#TERMINATED TERMINATED} : Idle state, all streams are closed + * <li>{@linkplain State#FAILED FAILED} : The service will transition to failed if it encounters + * any errors while reading from or writing to the streams, service failure will also cause + * the worker process to be forcibly shutdown and {@link #readItem(long, TimeUnit)}, + * {@link #closeWriter()} and {@link #sendMessage(Serializable)} will start throwing + * IllegalStateExceptions. + * </ul> + */ +@TrialScoped final class StreamService extends AbstractService { + /** How long to wait for a process that should be exiting to actually exit. */ + private static final int SHUTDOWN_WAIT_MILLIS = 10; + + private static final Logger logger = Logger.getLogger(StreamService.class.getName()); + private static final StreamItem TIMEOUT_ITEM = new StreamItem(Kind.TIMEOUT, null); + + /** The final item that will be sent down the stream. */ + static final StreamItem EOF_ITEM = new StreamItem(Kind.EOF, null); + + private final ListeningExecutorService streamExecutor = MoreExecutors.listeningDecorator( + Executors.newCachedThreadPool(new ThreadFactoryBuilder().setDaemon(true).build())); + private final BlockingQueue<StreamItem> outputQueue = Queues.newLinkedBlockingQueue(); + private final WorkerProcess worker; + private volatile Process process; + private final Parser<LogMessage> logMessageParser; + private final TrialOutputLogger trialOutput; + + /** + * This represents the number of open streams from the users perspective. i.e. can you still + * write to the socket and read items. + * + * <p>This is decremented when either the socket is closed for writing or the EOF_ITEM has been + * read by the user. + */ + private final AtomicInteger openStreams = new AtomicInteger(); + + /** + * Used to track how many read streams are open so we can correctly set the EOF_ITEM onto the + * queue. + */ + private final AtomicInteger runningReadStreams = new AtomicInteger(); + private OpenedSocket.Writer socketWriter; + + @Inject StreamService(WorkerProcess worker, + Parser<LogMessage> logMessageParser, + TrialOutputLogger trialOutput) { + this.worker = worker; + this.logMessageParser = logMessageParser; + this.trialOutput = trialOutput; + } + + @Override protected void doStart() { + try { + // TODO(lukes): write the commandline to the trial output file? + process = worker.startWorker(); + } catch (IOException e) { + notifyFailed(e); + return; + } + // Failsafe kill the process and the executor service. + // If the process has already exited cleanly, this will be a no-op. + addListener(new Listener() { + @Override public void starting() {} + @Override public void running() {} + @Override public void stopping(State from) {} + @Override public void terminated(State from) { + cleanup(); + } + @Override public void failed(State from, Throwable failure) { + cleanup(); + } + + void cleanup() { + streamExecutor.shutdown(); + process.destroy(); + try { + streamExecutor.awaitTermination(10, TimeUnit.MILLISECONDS); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + streamExecutor.shutdownNow(); + } + }, MoreExecutors.directExecutor()); + // You may be thinking as you read this "Yo dawg, what if IOExceptions rain from the sky?" + // If a stream we are reading from throws an IOException then we fail the entire Service. This + // will cause the worker to be killed (if its not dead already) and the various StreamReaders to + // be interrupted (eventually). + + // use the default charset because worker streams will use the default for output + Charset processCharset = Charset.defaultCharset(); + runningReadStreams.addAndGet(2); + openStreams.addAndGet(1); + streamExecutor.submit( + threadRenaming("worker-stderr", + new StreamReader("stderr", + new InputStreamReader(process.getErrorStream(), processCharset)))); + streamExecutor.submit( + threadRenaming("worker-stdout", + new StreamReader("stdout", + new InputStreamReader(process.getInputStream(), processCharset)))); + worker.socketFuture().addListener( + new Runnable() { + @Override public void run() { + try { + OpenedSocket openedSocket = + Uninterruptibles.getUninterruptibly(worker.socketFuture()); + logger.fine("successfully opened the pipe from the worker"); + socketWriter = openedSocket.writer(); + runningReadStreams.addAndGet(1); + openStreams.addAndGet(1); + streamExecutor.submit(threadRenaming("worker-socket", + new SocketStreamReader(openedSocket.reader()))); + } catch (ExecutionException e) { + notifyFailed(e.getCause()); + } + } + }, + MoreExecutors.directExecutor()); + notifyStarted(); + } + + /** + * Reads a {@link StreamItem} from one of the streams waiting for one to become available if + * necessary. + */ + StreamItem readItem(long timeout, TimeUnit unit) throws InterruptedException { + checkState(isRunning(), "Cannot read items from a %s StreamService", state()); + StreamItem line = outputQueue.poll(timeout, unit); + if (line == EOF_ITEM) { + closeStream(); + } + return (line == null) ? TIMEOUT_ITEM : line; + } + + /** + * Write a line of data to the worker process over the socket. + * + * <p>N.B. Writing data via {@link #sendMessage(Serializable)} is only valid once the underlying + * socket has been opened. This should be fine assuming that socket writes are only in response + * to socket reads (which is currently the case), so there is no way that a write could happen + * prior to the socket being opened. + */ + void sendMessage(Serializable message) throws IOException { + checkState(isRunning(), "Cannot read items from a %s StreamService", state()); + checkState(socketWriter != null, "Attempted to write to the socket before it was opened."); + try { + socketWriter.write(message); + // We need to flush since this is a back and forth lockstep protocol, buffering can cause + // deadlock! + socketWriter.flush(); + } catch (IOException e) { + Closeables.close(socketWriter, true); + notifyFailed(e); + throw e; + } + } + + /** Closes the socket writer. */ + void closeWriter() throws IOException { + checkState(isRunning(), "Cannot read items from a %s StreamService", state()); + checkState(socketWriter != null, "Attempted to close the socket before it was opened."); + try { + socketWriter.close(); + } catch (IOException e) { + notifyFailed(e); + throw e; + } + closeStream(); + } + + @Override protected void doStop() { + if (openStreams.get() > 0) { + // This means stop was called on us externally and we are still reading/writing, just log a + // warning and do nothing + logger.warning("Attempting to stop the stream service with streams still open"); + } + final ListenableFuture<Integer> processFuture = streamExecutor.submit(new Callable<Integer>() { + @Override public Integer call() throws Exception { + return process.waitFor(); + } + }); + // Experimentally, even with well behaved processes there is some time between when all streams + // are closed as part of process shutdown and when the process has exited. So to not fail + // flakily when shutting down normally we need to do a timed wait + streamExecutor.submit(new Callable<Void>() { + @Override public Void call() throws Exception { + boolean threw = true; + try { + if (processFuture.get(SHUTDOWN_WAIT_MILLIS, TimeUnit.MILLISECONDS) == 0) { + notifyStopped(); + } else { + notifyFailed( + new Exception("Process failed to stop cleanly. Exit code: " + process.waitFor())); + } + threw = false; + } finally { + processFuture.cancel(true); // we don't need it anymore + if (threw) { + process.destroy(); + notifyFailed( + new Exception("Process failed to stop cleanly and was forcibly killed. Exit code: " + + process.waitFor())); + } + } + return null; + } + }); + } + + private void closeStream() { + if (openStreams.decrementAndGet() == 0) { + stopAsync(); + } + } + + private void closeReadStream() { + if (runningReadStreams.decrementAndGet() == 0) { + outputQueue.add(EOF_ITEM); + } + } + + /** An item read from one of the streams. */ + static class StreamItem { + enum Kind { + /** This indicates that it is the last item. */ + EOF, + /** This indicates that reading the item timed out. */ + TIMEOUT, + /** This indicates that this item has content. */ + DATA; + } + + @Nullable private final LogMessage logMessage; + private final Kind kind; + + private StreamItem(LogMessage line) { + this(Kind.DATA, checkNotNull(line)); + } + + private StreamItem(Kind state, @Nullable LogMessage logMessage) { + this.logMessage = logMessage; + this.kind = state; + } + + /** Returns the content. This is only valid if {@link #kind()} return {@link Kind#DATA}. */ + LogMessage content() { + checkState(kind == Kind.DATA, "Only data lines have content: %s", this); + return logMessage; + } + + Kind kind() { + return kind; + } + + @Override public String toString() { + ToStringHelper helper = MoreObjects.toStringHelper(StreamItem.class); + if (kind == Kind.DATA) { + helper.addValue(logMessage); + } else { + helper.addValue(kind); + } + return helper.toString(); + } + } + + /** Returns a callable that renames the the thread that the given callable runs in. */ + private static <T> Callable<T> threadRenaming(final String name, final Callable<T> callable) { + checkNotNull(name); + checkNotNull(callable); + return new Callable<T>() { + @Override public T call() throws Exception { + Thread currentThread = Thread.currentThread(); + String oldName = currentThread.getName(); + currentThread.setName(name); + try { + return callable.call(); + } finally { + currentThread.setName(oldName); + } + } + }; + } + + /** + * A background task that reads lines of text from a {@link Reader} and puts them onto a + * {@link BlockingQueue}. + */ + private final class StreamReader implements Callable<Void> { + final Reader reader; + final String streamName; + + StreamReader(String streamName, Reader reader) { + this.streamName = streamName; + this.reader = reader; + } + + @Override public Void call() throws IOException, InterruptedException, ParseException { + LineReader lineReader = new LineReader(reader); + boolean threw = true; + try { + String line; + while ((line = lineReader.readLine()) != null) { + trialOutput.log(streamName, line); + LogMessage logMessage = logMessageParser.parse(line); + if (logMessage != null) { + outputQueue.put(new StreamItem(logMessage)); + } + } + threw = false; + } catch (Exception e) { + notifyFailed(e); + } finally { + closeReadStream(); + Closeables.close(reader, threw); + } + return null; + } + } + + /** + * A background task that reads lines of text from a {@link OpenedSocket.Reader} and puts them + * onto a {@link BlockingQueue}. + */ + private final class SocketStreamReader implements Callable<Void> { + final OpenedSocket.Reader reader; + + SocketStreamReader(OpenedSocket.Reader reader) { + this.reader = reader; + } + + @Override public Void call() throws IOException, InterruptedException, ParseException { + boolean threw = true; + try { + Object obj; + while ((obj = reader.read()) != null) { + if (obj instanceof String) { + log(obj.toString()); + continue; + } + LogMessage message = (LogMessage) obj; + if (message instanceof StopMeasurementLogMessage) { + // TODO(lukes): how useful are these messages? They seem like leftover debugging info + for (Measurement measurement : ((StopMeasurementLogMessage) message).measurements()) { + log(String.format("I got a result! %s: %f%s%n", + measurement.description(), + measurement.value().magnitude() / measurement.weight(), + measurement.value().unit())); + } + } + outputQueue.put(new StreamItem(message)); + } + threw = false; + } catch (Exception e) { + notifyFailed(e); + } finally { + closeReadStream(); + Closeables.close(reader, threw); + } + return null; + } + + private void log(String text) { + trialOutput.log("socket", text); + } + } +} diff --git a/caliper/src/main/java/com/google/caliper/runner/TrialFailureException.java b/caliper/src/main/java/com/google/caliper/runner/TrialFailureException.java new file mode 100644 index 0000000..923750b --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/runner/TrialFailureException.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2013 Google Inc. + * + * 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.google.caliper.runner; + +/** + * An exception representing the failure of an individual trial. Throwing this exception will + * invalidate the trial, but allow the run to continue. Both the runner and individual instruments + * are free to throw this exception. + * + * <p>The exception message is used to convey the nature of the failure to the user. + */ +final class TrialFailureException extends RuntimeException { + public TrialFailureException(String message) { + super(message); + } +} diff --git a/caliper/src/main/java/com/google/caliper/runner/TrialId.java b/caliper/src/main/java/com/google/caliper/runner/TrialId.java new file mode 100644 index 0000000..9a7c6fe --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/runner/TrialId.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2013 Google Inc. + * + * 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.google.caliper.runner; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import javax.inject.Qualifier; + +/** Binding annotation for the current trial id. */ +@Retention(RUNTIME) +@Target({FIELD, PARAMETER, METHOD}) +@Qualifier +@interface TrialId {} diff --git a/caliper/src/main/java/com/google/caliper/runner/TrialModule.java b/caliper/src/main/java/com/google/caliper/runner/TrialModule.java new file mode 100644 index 0000000..73d0044 --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/runner/TrialModule.java @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2013 Google Inc. + * + * 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.google.caliper.runner; + +import static com.google.common.base.Preconditions.checkState; + +import com.google.caliper.bridge.LogMessage; +import com.google.caliper.bridge.OpenedSocket; +import com.google.caliper.model.BenchmarkSpec; +import com.google.caliper.model.Host; +import com.google.caliper.model.Run; +import com.google.caliper.model.Scenario; +import com.google.caliper.model.Trial; +import com.google.caliper.runner.Instrument.MeasurementCollectingVisitor; +import com.google.caliper.util.Parser; +import com.google.common.util.concurrent.ListenableFuture; +import dagger.Module; +import dagger.Provides; + +import java.util.UUID; + +/** + * Configuration for a {@link TrialRunLoop}. + */ +@Module +final class TrialModule { + + private final UUID trialId; + private final int trialNumber; + private final Experiment experiment; + + TrialModule(UUID trialId, int trialNumber, Experiment experiment) { + this.trialId = trialId; + this.trialNumber = trialNumber; + this.experiment = experiment; + } + + @TrialScoped + @Provides + @TrialId + UUID provideTrialId() { + return trialId; + } + + @TrialScoped + @Provides + @TrialNumber + int provideTrialNumber() { + return trialNumber; + } + + @TrialScoped + @Provides + Experiment provideExperiment() { + return experiment; + } + + @TrialScoped + @Provides + static BenchmarkSpec provideBenchmarkSpec(Experiment experiment) { + return new BenchmarkSpec.Builder() + .className(experiment.instrumentation().benchmarkMethod().getDeclaringClass().getName()) + .methodName(experiment.instrumentation().benchmarkMethod().getName()) + .addAllParameters(experiment.userParameters()) + .build(); + } + + @Provides + @TrialScoped + static ListenableFuture<OpenedSocket> provideTrialSocket( + @TrialId UUID trialId, + ServerSocketService serverSocketService) { + return serverSocketService.getConnection(trialId); + } + + @Provides + static MeasurementCollectingVisitor provideMeasurementCollectingVisitor(Experiment experiment) { + return experiment.instrumentation().getMeasurementCollectingVisitor(); + } + + @Provides + @TrialScoped + static TrialSchedulingPolicy provideTrialSchedulingPolicy(Experiment experiment) { + return experiment.instrumentation().instrument().schedulingPolicy(); + } + + @Provides + @TrialScoped + static StreamService provideStreamService( + WorkerProcess worker, + Parser<LogMessage> logMessageParser, + TrialOutputLogger trialOutput) { + return new StreamService(worker, logMessageParser, trialOutput); + } + + // TODO(user): make this a singleton in a higher level module. + @Provides + @TrialScoped + static ShutdownHookRegistrar provideShutdownHook() { + return new RuntimeShutdownHookRegistrar(); + } + + @Provides static TrialResultFactory provideTrialFactory( + @TrialId final UUID trialId, + final Run run, + final Host host, + final Experiment experiment, + final BenchmarkSpec benchmarkSpec) { + return new TrialResultFactory() { + @Override public TrialResult newTrialResult( + VmDataCollectingVisitor dataCollectingVisitor, + MeasurementCollectingVisitor measurementCollectingVisitor) { + checkState(measurementCollectingVisitor.isDoneCollecting()); + // TODO(lukes): should the trial messages be part of the Trial datastructure? It seems like + // the web UI could make use of them. + return new TrialResult( + new Trial.Builder(trialId) + .run(run) + .instrumentSpec(experiment.instrumentation().instrument().getSpec()) + .scenario(new Scenario.Builder() + .host(host) + .vmSpec(dataCollectingVisitor.vmSpec()) + .benchmarkSpec(benchmarkSpec)) + .addAllMeasurements(measurementCollectingVisitor.getMeasurements()) + .build(), + experiment, + measurementCollectingVisitor.getMessages()); + } + }; + } +} diff --git a/caliper/src/main/java/com/google/caliper/runner/TrialNumber.java b/caliper/src/main/java/com/google/caliper/runner/TrialNumber.java new file mode 100644 index 0000000..1437749 --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/runner/TrialNumber.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2013 Google Inc. + * + * 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.google.caliper.runner; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import javax.inject.Qualifier; + +/** Binding annotation the trial number, used for debugging purposes. */ +@Retention(RUNTIME) +@Target({FIELD, PARAMETER, METHOD}) +@Qualifier +@interface TrialNumber {} diff --git a/caliper/src/main/java/com/google/caliper/runner/TrialOutputFactory.java b/caliper/src/main/java/com/google/caliper/runner/TrialOutputFactory.java new file mode 100644 index 0000000..588a330 --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/runner/TrialOutputFactory.java @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2014 Google Inc. + * + * 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.google.caliper.runner; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.PrintWriter; + +/** + * A factory for trial log files. + * + * <p>The log files may be configured to be deleted on exit of the runner process. If the files + * should not be deleted then call {@link #persistFile(File)} to ensure that they survive. + */ +interface TrialOutputFactory { + + /** A simple tuple of a {@link File} and a {@link PrintWriter} for writing to that file. */ + final class FileAndWriter { + final File file; + final PrintWriter writer; + + FileAndWriter(File file, PrintWriter writer) { + this.file = file; + this.writer = writer; + } + } + + /** Returns the file to write trial output to. */ + FileAndWriter getTrialOutputFile(int trialNumber) throws FileNotFoundException; + + /** + * Ensures that the given file will not be deleted after the run. The file provided must be equal + * to a file returned by {@link #getTrialOutputFile(int)}. + */ + void persistFile(File f); +} diff --git a/caliper/src/main/java/com/google/caliper/runner/TrialOutputFactoryService.java b/caliper/src/main/java/com/google/caliper/runner/TrialOutputFactoryService.java new file mode 100644 index 0000000..31175dd --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/runner/TrialOutputFactoryService.java @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2014 Google Inc. + * + * 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.google.caliper.runner; + +import static com.google.common.base.Preconditions.checkArgument; + +import com.google.caliper.model.Run; +import com.google.caliper.options.CaliperOptions; +import com.google.common.base.Charsets; +import com.google.common.io.Files; +import com.google.common.util.concurrent.AbstractIdleService; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.util.LinkedHashSet; +import java.util.Set; + +import javax.annotation.concurrent.GuardedBy; +import javax.inject.Inject; +import javax.inject.Singleton; + +/** + * A {@link TrialOutputFactory} implemented as a service that manages a directory either under + * {@code /tmp} or in a user configured directory. + * + * <p>If there is a user configured directory, then no files will be deleted on service shutdown. + * Otherwise the only way to ensure that the log files survive service shutdown is to explicitly + * call {@link #persistFile(File)} with each file that should not be deleted. + */ +@Singleton +final class TrialOutputFactoryService + extends AbstractIdleService implements TrialOutputFactory { + private static final String LOG_DIRECTORY_PROPERTY = "worker.output"; + + private final CaliperOptions options; + private final Run run; + + @GuardedBy("this") + private final Set<String> toDelete = new LinkedHashSet<String>(); + + @GuardedBy("this") + private File directory; + + @GuardedBy("this") + private boolean persistFiles; + + @Inject TrialOutputFactoryService(Run run, CaliperOptions options) { + this.run = run; + this.options = options; + } + + /** Returns the file to write trial output to. */ + @Override public FileAndWriter getTrialOutputFile(int trialNumber) throws FileNotFoundException { + File dir; + synchronized (this) { + if (directory == null) { + throw new RuntimeException( + String.format("The output manager %s has not been started yet", this)); + } + dir = directory; + } + File trialFile = new File(dir, String.format("trial-%d.log", trialNumber)); + synchronized (this) { + if (!persistFiles) { + toDelete.add(trialFile.getPath()); + } + } + return new FileAndWriter(trialFile, + new PrintWriter( + new BufferedWriter( + new OutputStreamWriter( + new FileOutputStream(trialFile), + Charsets.UTF_8)))); + } + + /** + * Ensures that the given file will not be deleted on exit of the JVM, possibly by copying to a + * new file. + */ + @Override public synchronized void persistFile(File f) { + if (!persistFiles) { + checkArgument(toDelete.remove(f.getPath()), "%s was not created by the output manager", f); + } + } + + @Override protected synchronized void startUp() throws Exception { + File directory; + String dirName = options.configProperties().get(LOG_DIRECTORY_PROPERTY); + boolean persistFiles = true; + if (dirName != null) { + directory = new File(dirName); + if (!directory.exists()) { + if (!directory.mkdirs()) { + throw new Exception( + String.format("Unable to create directory %s indicated by property %s", + dirName, LOG_DIRECTORY_PROPERTY)); + } + } else if (!directory.isDirectory()) { + throw new Exception( + String.format("Configured directory %s indicated by property %s is not a directory", + dirName, LOG_DIRECTORY_PROPERTY)); + } + // The directory exists and is a directory + directory = new File(directory, String.format("run-%s", run.id())); + if (!directory.mkdir()) { + throw new Exception("Unable to create a run directory " + directory); + } + } else { + // If none is configured then we don't care, just make a temp dir + // TODO(lukes): it would be nice to use jdk7 java.nio.file.Files.createTempDir() which allows + // us to specify a name, but caliper is still on jdk6. + directory = Files.createTempDir(); + persistFiles = false; + } + this.directory = directory; + this.persistFiles = persistFiles; + } + + @Override protected synchronized void shutDown() throws Exception { + if (!persistFiles) { + // This is best effort, files to be deleted are already in a tmp directory. + for (String f : toDelete) { + new File(f).delete(); + } + // This will only succeed if the directory is empty which is what we want. + directory.delete(); + } + } +} diff --git a/caliper/src/main/java/com/google/caliper/runner/TrialOutputLogger.java b/caliper/src/main/java/com/google/caliper/runner/TrialOutputLogger.java new file mode 100644 index 0000000..1803fab --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/runner/TrialOutputLogger.java @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2014 Google Inc. + * + * 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.google.caliper.runner; + +import static com.google.common.base.Preconditions.checkState; + +import com.google.caliper.runner.TrialOutputFactory.FileAndWriter; + +import java.io.Closeable; +import java.io.File; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.UUID; + +import javax.annotation.concurrent.GuardedBy; +import javax.inject.Inject; + +/** + * A logger to write trial output to a file. + */ +@TrialScoped final class TrialOutputLogger implements Closeable { + @GuardedBy("this") + private File file; + + @GuardedBy("this") + private PrintWriter writer; + + private final int trialNumber; + private final Experiment experiment; + private final UUID trialId; + private final TrialOutputFactory outputManager; + + @Inject TrialOutputLogger(TrialOutputFactory outputManager, @TrialNumber int trialNumber, + @TrialId UUID trialId, Experiment experiment) { + this.outputManager = outputManager; + this.trialNumber = trialNumber; + this.trialId = trialId; + this.experiment = experiment; + } + + /** Opens the trial output file. */ + synchronized void open() throws IOException { + if (writer == null) { + FileAndWriter fileAndWriter = outputManager.getTrialOutputFile(trialNumber); + file = fileAndWriter.file; + writer = fileAndWriter.writer; + } + } + + /** + * Ensures that the writer has been opened. also creates a happens-before edge that ensures that + * writer is visible (and non-null) after a non-exceptional return from this method. + */ + private synchronized void checkOpened() { + checkState(writer != null, "The logger is not open"); + } + + /** Prints header information to the file. */ + synchronized void printHeader() { + checkOpened(); + // make the file self describing + // TODO(lukes): we could print the command line here. The user wouldn't be able to run it again + // since there would be no runner sending continue messages, but it might be useful to debug + // classpath issues. + writer.println("Trial Number: " + trialNumber); + writer.println("Trial Id: " + trialId); + writer.println("Experiment: " + experiment); + writer.println(); + } + + /** + * Logs a line of output to the logger. + * + * @param source The source of the line (e.g. 'stderr') + * @param line The output + */ + synchronized void log(String source, String line) { + checkOpened(); + writer.printf("[%s] %s%n", source, line); + } + + @Override public synchronized void close() { + if (writer != null) { + writer.close(); + } + } + + /** Marks the log file so that it will not be deleted at the end of the benchmark. */ + synchronized void ensureFileIsSaved() { + checkOpened(); + outputManager.persistFile(file); + } + + /** Returns the log file path. */ + synchronized File trialOutputFile() { + checkOpened(); + return file; + } +} diff --git a/caliper/src/main/java/com/google/caliper/runner/TrialResult.java b/caliper/src/main/java/com/google/caliper/runner/TrialResult.java new file mode 100644 index 0000000..a870b27 --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/runner/TrialResult.java @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2011 Google Inc. + * + * 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.google.caliper.runner; + +import com.google.caliper.model.Trial; +import com.google.common.collect.ImmutableList; + +/** + * A simple tuple of the data + */ +final class TrialResult { + private final Trial trial; + private final Experiment experiment; + private final ImmutableList<String> trialMessages; + + TrialResult(Trial trial, Experiment experiment, ImmutableList<String> trialMessages) { + this.trial = trial; + this.experiment = experiment; + this.trialMessages = trialMessages; + } + + Experiment getExperiment() { + return experiment; + } + + Trial getTrial() { + return trial; + } + + ImmutableList<String> getTrialMessages() { + return trialMessages; + } +} diff --git a/caliper/src/main/java/com/google/caliper/runner/TrialResultFactory.java b/caliper/src/main/java/com/google/caliper/runner/TrialResultFactory.java new file mode 100644 index 0000000..2f8e2a2 --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/runner/TrialResultFactory.java @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2013 Google Inc. + * + * 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.google.caliper.runner; + +import com.google.caliper.model.Trial; +import com.google.caliper.runner.Instrument.MeasurementCollectingVisitor; + +/** + * A factory for producing {@link TrialResult TrialResults} based on data collected from visitors. + */ +interface TrialResultFactory { + /** Returns a new {@link Trial}. */ + TrialResult newTrialResult(VmDataCollectingVisitor vmData, + MeasurementCollectingVisitor measurementData); +} diff --git a/caliper/src/main/java/com/google/caliper/runner/TrialRunLoop.java b/caliper/src/main/java/com/google/caliper/runner/TrialRunLoop.java new file mode 100644 index 0000000..587f3d2 --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/runner/TrialRunLoop.java @@ -0,0 +1,199 @@ +/* + * Copyright (C) 2011 Google Inc. + * + * 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.google.caliper.runner; + +import static java.util.concurrent.TimeUnit.MILLISECONDS; +import static java.util.concurrent.TimeUnit.NANOSECONDS; + +import com.google.caliper.bridge.LogMessage; +import com.google.caliper.bridge.ShouldContinueMessage; +import com.google.caliper.bridge.StopMeasurementLogMessage; +import com.google.caliper.model.Trial; +import com.google.caliper.options.CaliperOptions; +import com.google.caliper.runner.Instrument.MeasurementCollectingVisitor; +import com.google.caliper.runner.StreamService.StreamItem; +import com.google.caliper.util.ShortDuration; +import com.google.common.base.Stopwatch; +import com.google.common.base.Throwables; +import com.google.common.util.concurrent.Service.State; + +import org.joda.time.Duration; + +import java.io.IOException; +import java.util.concurrent.Callable; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.inject.Inject; + +/** + * The main data gather control loop for a Trial. + * + * <p>This class starts the worker process, reads all the data from it and constructs the + * {@link Trial} while enforcing the trial timeout. + */ +@TrialScoped class TrialRunLoop implements Callable<TrialResult> { + private static final Logger logger = Logger.getLogger(TrialRunLoop.class.getName()); + + /** The time that the worker has to clean up after an experiment. */ + private static final Duration WORKER_CLEANUP_DURATION = Duration.standardSeconds(2); + + private final CaliperOptions options; + private final StreamService streamService; + private final TrialResultFactory trialFactory; + + // TODO(lukes): The VmDataCollectingVisitor should be able to tell us when it has collected all + // its data. + private final VmDataCollectingVisitor dataCollectingVisitor; + private final Stopwatch trialStopwatch = Stopwatch.createUnstarted(); + private final MeasurementCollectingVisitor measurementCollectingVisitor; + private final TrialOutputLogger trialOutput; + + @Inject TrialRunLoop( + MeasurementCollectingVisitor measurementCollectingVisitor, + CaliperOptions options, + TrialResultFactory trialFactory, + TrialOutputLogger trialOutput, + StreamService streamService, + VmDataCollectingVisitor dataCollectingVisitor) { + this.options = options; + this.trialFactory = trialFactory; + this.streamService = streamService; + this.measurementCollectingVisitor = measurementCollectingVisitor; + this.trialOutput = trialOutput; + this.dataCollectingVisitor = dataCollectingVisitor; + } + + @Override public TrialResult call() throws TrialFailureException, IOException { + if (streamService.state() != State.NEW) { + throw new IllegalStateException("You can only invoke the run loop once"); + } + trialOutput.open(); + trialOutput.printHeader(); + streamService.startAsync().awaitRunning(); + try { + long timeLimitNanos = getTrialTimeLimitTrialNanos(); + boolean doneCollecting = false; + boolean done = false; + while (!done) { + StreamItem item; + try { + item = streamService.readItem( + timeLimitNanos - trialStopwatch.elapsed(NANOSECONDS), + NANOSECONDS); + } catch (InterruptedException e) { + trialOutput.ensureFileIsSaved(); + // Someone has asked us to stop (via Futures.cancel?). + if (doneCollecting) { + logger.log(Level.WARNING, "Trial cancelled before completing normally (but after " + + "collecting sufficient data). Inspect {0} to see any worker output", + trialOutput.trialOutputFile()); + done = true; + break; + } + // We were asked to stop but we didn't actually finish (the normal case). Fail the trial. + throw new TrialFailureException( + String.format("Trial cancelled. Inspect %s to see any worker output.", + trialOutput.trialOutputFile())); + } + switch (item.kind()) { + case DATA: + LogMessage logMessage = item.content(); + logMessage.accept(measurementCollectingVisitor); + logMessage.accept(dataCollectingVisitor); + if (!doneCollecting && measurementCollectingVisitor.isDoneCollecting()) { + doneCollecting = true; + // We have received all the measurements we need and are about to tell the worker to + // shut down. At this point the worker should shutdown soon, but we don't want to + // wait too long, so decrease the time limit so that we wait no more than + // WORKER_CLEANUP_DURATION. + long cleanupTimeNanos = MILLISECONDS.toNanos(WORKER_CLEANUP_DURATION.getMillis()); + // TODO(lukes): Does the min operation make sense here? should we just use the + // cleanupTimeNanos? + timeLimitNanos = trialStopwatch.elapsed(NANOSECONDS) + cleanupTimeNanos; + } + // If it is a stop measurement message we need to tell the worker to either stop or keep + // going with a WorkerContinueMessage. This needs to be done after the + // measurementCollecting visitor sees the message so that isDoneCollection will be up to + // date. + if (logMessage instanceof StopMeasurementLogMessage) { + // TODO(lukes): this is a blocking write, perhaps we should perform it in a non + // blocking manner to keep this thread only blocking in one place. This would + // complicate error handling, but may increase performance since it would free this + // thread up to handle other messages + streamService.sendMessage( + new ShouldContinueMessage( + !doneCollecting, + measurementCollectingVisitor.isWarmupComplete())); + if (doneCollecting) { + streamService.closeWriter(); + } + } + break; + case EOF: + // We consider EOF to be synonymous with worker shutdown + if (!doneCollecting) { + trialOutput.ensureFileIsSaved(); + throw new TrialFailureException(String.format("The worker exited without producing " + + "data. It has likely crashed. Inspect %s to see any worker output.", + trialOutput.trialOutputFile())); + } + done = true; + break; + case TIMEOUT: + trialOutput.ensureFileIsSaved(); + if (doneCollecting) { + // Should this be an error? + logger.log(Level.WARNING, "Worker failed to exit cleanly within the alloted time. " + + "Inspect {0} to see any worker output", trialOutput.trialOutputFile()); + done = true; + } else { + throw new TrialFailureException(String.format( + "Trial exceeded the total allowable runtime (%s). " + + "The limit may be adjusted using the --time-limit flag. Inspect %s to " + + "see any worker output", + options.timeLimit(), trialOutput.trialOutputFile())); + } + break; + default: + throw new AssertionError("Impossible item: " + item); + } + } + return trialFactory.newTrialResult(dataCollectingVisitor, measurementCollectingVisitor); + } catch (Throwable e) { + Throwables.propagateIfInstanceOf(e, TrialFailureException.class); + // This is some failure that is not a TrialFailureException, let the exception propagate but + // log the filename for the user. + trialOutput.ensureFileIsSaved(); + logger.severe( + String.format( + "Unexpected error while executing trial. Inspect %s to see any worker output.", + trialOutput.trialOutputFile())); + throw Throwables.propagate(e); + } finally { + trialStopwatch.reset(); + streamService.stopAsync(); + trialOutput.close(); + } + } + + private long getTrialTimeLimitTrialNanos() { + ShortDuration timeLimit = options.timeLimit(); + if (ShortDuration.zero().equals(timeLimit)) { + return Long.MAX_VALUE; + } + return timeLimit.to(NANOSECONDS); + } +} diff --git a/caliper/src/main/java/com/google/caliper/runner/TrialSchedulingPolicy.java b/caliper/src/main/java/com/google/caliper/runner/TrialSchedulingPolicy.java new file mode 100644 index 0000000..604ca9c --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/runner/TrialSchedulingPolicy.java @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2014 Google Inc. + * + * 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.google.caliper.runner; + +/** + * The scheduling policy for a particular trial. + * + * <p>TODO(lukes): Currently this is extremely simple. Trials can be scheduled in parallel with + * other trials or not. In the future, this should use some kind of cost modeling. + */ +enum TrialSchedulingPolicy { + PARALLEL, + SERIAL; +} diff --git a/caliper/src/main/java/com/google/caliper/runner/TrialScopeComponent.java b/caliper/src/main/java/com/google/caliper/runner/TrialScopeComponent.java new file mode 100644 index 0000000..5f23549 --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/runner/TrialScopeComponent.java @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2015 Google Inc. + * + * 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.google.caliper.runner; + +import dagger.Subcomponent; + +/** + * Component for creating {@link ScheduledTrial} in the {@link TrialScoped}. + */ +@TrialScoped +@Subcomponent(modules = {TrialModule.class}) +interface TrialScopeComponent { + ScheduledTrial getScheduledTrial(); +} diff --git a/caliper/src/main/java/com/google/caliper/runner/TrialScoped.java b/caliper/src/main/java/com/google/caliper/runner/TrialScoped.java new file mode 100644 index 0000000..81a465a --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/runner/TrialScoped.java @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2013 Google Inc. + * + * 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.google.caliper.runner; + +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.TYPE; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import javax.inject.Scope; + +/** + * Scope annotation for the TrialScope. + * + * <p>Apply this to binding for which there can only be one per trial. + */ +@Target({ TYPE, METHOD }) +@Retention(RetentionPolicy.RUNTIME) +@Scope +@interface TrialScoped {} diff --git a/caliper/src/main/java/com/google/caliper/runner/UserCodeException.java b/caliper/src/main/java/com/google/caliper/runner/UserCodeException.java new file mode 100644 index 0000000..1aa6337 --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/runner/UserCodeException.java @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2011 Google Inc. + * + * 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.google.caliper.runner; + +import java.io.PrintWriter; + +/** + * Signifies that the user's benchmark code threw an exception. + */ +@SuppressWarnings("serial") +public class UserCodeException extends InvalidBenchmarkException { + public UserCodeException(String message, Throwable cause) { + super(message); + initCause(cause); + } + + public UserCodeException(Throwable cause) { + this("An exception was thrown from the benchmark code", cause); + } + + @Override public void display(PrintWriter writer) { + printStackTrace(writer); + } +} diff --git a/caliper/src/main/java/com/google/caliper/runner/VirtualMachine.java b/caliper/src/main/java/com/google/caliper/runner/VirtualMachine.java new file mode 100644 index 0000000..f48020b --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/runner/VirtualMachine.java @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2011 Google Inc. + * + * 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.google.caliper.runner; + +import com.google.caliper.config.VmConfig; +import com.google.common.base.MoreObjects; +import com.google.common.base.Objects; + +/** + * A named virtual machine configuration. + */ +final class VirtualMachine { + final String name; + final VmConfig config; + + VirtualMachine(String name, VmConfig config) { + this.name = name; + this.config = config; + } + + @Override public boolean equals(Object object) { + if (object instanceof VirtualMachine) { + VirtualMachine that = (VirtualMachine) object; + return this.name.equals(that.name) + && this.config.equals(that.config); + } + return false; + } + + @Override public int hashCode() { + return Objects.hashCode(name, config); + } + + @Override public String toString() { + return MoreObjects.toStringHelper(this) + .add("name", name) + .add("config", config) + .toString(); + } +} diff --git a/caliper/src/main/java/com/google/caliper/runner/VmDataCollectingVisitor.java b/caliper/src/main/java/com/google/caliper/runner/VmDataCollectingVisitor.java new file mode 100644 index 0000000..3b56d53 --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/runner/VmDataCollectingVisitor.java @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2013 Google Inc. + * + * 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.google.caliper.runner; + +import com.google.caliper.bridge.AbstractLogMessageVisitor; +import com.google.caliper.bridge.FailureLogMessage; +import com.google.caliper.bridge.VmOptionLogMessage; +import com.google.caliper.bridge.VmPropertiesLogMessage; +import com.google.caliper.model.VmSpec; +import com.google.caliper.platform.Platform; +import com.google.common.base.Optional; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Maps; + +import javax.inject.Inject; + +/** An {@link AbstractLogMessageVisitor} that collects data about JVM properties and options. */ +@TrialScoped +final class VmDataCollectingVisitor extends AbstractLogMessageVisitor { + private final ImmutableMap.Builder<String, String> vmOptionsBuilder = ImmutableMap.builder(); + private final Platform platform; + private Optional<ImmutableMap<String, String>> vmProperties = Optional.absent(); + + @Inject VmDataCollectingVisitor(Platform platform) { + this.platform = platform; + } + + /** + * Returns a {@link VmSpec} based on the data gathered by this visitor. + * + * @throws IllegalStateException if not all the data has been gathered. + */ + VmSpec vmSpec() { + ImmutableMap<String, String> options = vmOptionsBuilder.build(); + platform.checkVmProperties(options); + return new VmSpec.Builder() + .addAllProperties(vmProperties.get()) + .addAllOptions(options) + .build(); + } + + @Override + public void visit(FailureLogMessage logMessage) { + throw new ProxyWorkerException(logMessage.stackTrace()); + } + + @Override + public void visit(VmOptionLogMessage logMessage) { + vmOptionsBuilder.put(logMessage.name(), logMessage.value()); + } + + @Override + public void visit(VmPropertiesLogMessage logMessage) { + vmProperties = Optional.of(ImmutableMap.copyOf( + Maps.filterKeys(logMessage.properties(), platform.vmPropertiesToRetain()))); + } +} diff --git a/caliper/src/main/java/com/google/caliper/runner/WorkerProcess.java b/caliper/src/main/java/com/google/caliper/runner/WorkerProcess.java new file mode 100644 index 0000000..144a21c --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/runner/WorkerProcess.java @@ -0,0 +1,209 @@ +/* + * Copyright (C) 2012 Google Inc. + * + * 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.google.caliper.runner; + +import com.google.caliper.bridge.CommandLineSerializer; +import com.google.caliper.bridge.OpenedSocket; +import com.google.caliper.bridge.WorkerSpec; +import com.google.caliper.config.VmConfig; +import com.google.caliper.model.BenchmarkSpec; +import com.google.caliper.runner.Instrument.Instrumentation; +import com.google.caliper.worker.WorkerMain; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; +import com.google.common.util.concurrent.ListenableFuture; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Collections; +import java.util.List; +import java.util.UUID; +import java.util.logging.Logger; + +import javax.annotation.concurrent.GuardedBy; +import javax.inject.Inject; + +/** + * A representation of an unstarted worker. + * + * <p>A worker is a sub process that runs a benchmark trial. Specifically it is a JVM running + * {@link com.google.caliper.worker.WorkerMain}. Because of this we can make certain assumptions + * about its behavior, including but not limited to: + * + * <ul> + * <li>The worker will connect back to us over a socket connection and send us UTF-8 json + * messages in a line oriented protocol. + * <li>TODO(lukes,gak): This is probably as good a place as any to specify the entire protocol. + * </ul> + */ +@TrialScoped final class WorkerProcess { + private static final Logger logger = Logger.getLogger(WorkerProcess.class.getName()); + + @GuardedBy("this") + private Process worker; + private final ProcessBuilder workerBuilder; + private final ShutdownHookRegistrar shutdownHookRegistrar; + private final ListenableFuture<OpenedSocket> openedSocket; + private final UUID trialId; + + @VisibleForTesting WorkerProcess(ProcessBuilder workerBuilder, + UUID trialId, + ListenableFuture<OpenedSocket> openedSocket, + ShutdownHookRegistrar shutdownHookRegistrar) { + this.trialId = trialId; + this.workerBuilder = workerBuilder; + this.openedSocket = openedSocket; + this.shutdownHookRegistrar = shutdownHookRegistrar; + } + + @Inject WorkerProcess(@TrialId UUID trialId, + ListenableFuture<OpenedSocket> openedSocket, + Experiment experiment, + BenchmarkSpec benchmarkSpec, + @LocalPort int localPort, + BenchmarkClass benchmarkClass, + ShutdownHookRegistrar shutdownHookRegistrar) { + this.trialId = trialId; + this.workerBuilder = + buildProcess(trialId, experiment, benchmarkSpec, localPort, benchmarkClass); + this.openedSocket = openedSocket; + this.shutdownHookRegistrar = shutdownHookRegistrar; + } + + ListenableFuture<OpenedSocket> socketFuture() { + return openedSocket; + } + + /** + * Returns a {@link Process} representing this worker. The process will be started if it hasn't + * already. + */ + synchronized Process startWorker() throws IOException { + if (worker == null) { + final Process delegate = workerBuilder.start(); + final Thread shutdownHook = new Thread("worker-shutdown-hook-" + trialId) { + @Override public void run() { + delegate.destroy(); + } + }; + shutdownHookRegistrar.addShutdownHook(shutdownHook); + worker = new Process() { + @Override public OutputStream getOutputStream() { + return delegate.getOutputStream(); + } + + @Override public InputStream getInputStream() { + return delegate.getInputStream(); + } + + @Override public InputStream getErrorStream() { + return delegate.getErrorStream(); + } + + @Override public int waitFor() throws InterruptedException { + int waitFor = delegate.waitFor(); + shutdownHookRegistrar.removeShutdownHook(shutdownHook); + return waitFor; + } + + @Override public int exitValue() { + int exitValue = delegate.exitValue(); + // if it hasn't thrown, the process is done + shutdownHookRegistrar.removeShutdownHook(shutdownHook); + return exitValue; + } + + @Override public void destroy() { + delegate.destroy(); + shutdownHookRegistrar.removeShutdownHook(shutdownHook); + } + }; + } + return worker; + } + + @VisibleForTesting static ProcessBuilder buildProcess( + UUID trialId, + Experiment experiment, + BenchmarkSpec benchmarkSpec, + int localPort, + BenchmarkClass benchmarkClass) { + // TODO(lukes): it would be nice to split this method into a few smaller more targeted methods + Instrumentation instrumentation = experiment.instrumentation(); + Instrument instrument = instrumentation.instrument(); + WorkerSpec request = new WorkerSpec( + trialId, + instrumentation.workerClass(), + instrumentation.workerOptions(), + benchmarkSpec, + ImmutableList.copyOf(instrumentation.benchmarkMethod.getParameterTypes()), + localPort); + + ProcessBuilder processBuilder = new ProcessBuilder().redirectErrorStream(false); + + List<String> args = processBuilder.command(); + + VirtualMachine vm = experiment.vm(); + VmConfig vmConfig = vm.config; + args.addAll(getJvmArgs(vm, benchmarkClass)); + + Iterable<String> instrumentJvmOptions = instrument.getExtraCommandLineArgs(vmConfig); + logger.fine(String.format("Instrument(%s) Java args: %s", instrument.getClass().getName(), + instrumentJvmOptions)); + Iterables.addAll(args, instrumentJvmOptions); + + // last to ensure that they're always applied + args.addAll(vmConfig.workerProcessArgs()); + + args.add(WorkerMain.class.getName()); + args.add(CommandLineSerializer.render(request)); + + logger.finest(String.format("Full JVM (%s) args: %s", vm.name, args)); + return processBuilder; + } + + @VisibleForTesting static List<String> getJvmArgs( + VirtualMachine vm, + BenchmarkClass benchmarkClass) { + + VmConfig vmConfig = vm.config; + String platformName = vmConfig.platformName(); + + List<String> args = Lists.newArrayList(); + String jdkPath = vmConfig.vmExecutable().getAbsolutePath(); + args.add(jdkPath); + logger.fine(String.format("%s(%s) Path: %s", platformName, vm.name, jdkPath)); + + ImmutableList<String> jvmOptions = vmConfig.options(); + args.addAll(jvmOptions); + logger.fine(String.format("%s(%s) args: %s", platformName, vm.name, jvmOptions)); + + ImmutableSet<String> benchmarkJvmOptions = benchmarkClass.vmOptions(); + args.addAll(benchmarkJvmOptions); + logger.fine(String.format("Benchmark(%s) %s args: %s", benchmarkClass.name(), platformName, + benchmarkJvmOptions)); + + String classPath = vmConfig.workerClassPath(); + Collections.addAll(args, "-cp", classPath); + logger.finer(String.format("Class path: %s", classPath)); + return args; + } +} diff --git a/caliper/src/main/java/com/google/caliper/util/DisplayUsageException.java b/caliper/src/main/java/com/google/caliper/util/DisplayUsageException.java new file mode 100644 index 0000000..c412c55 --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/util/DisplayUsageException.java @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2011 Google Inc. + * + * 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.google.caliper.util; + +import java.io.PrintWriter; + +/** + * Exception used to abort command-line processing because the user has asked for help (using either + * --help or -h). + */ +@SuppressWarnings("serial") // who would serialize a command-line parsing error? +public final class DisplayUsageException extends InvalidCommandException { + public DisplayUsageException() { + super("(User asked for --help. This message should not appear anywhere.)"); + } + + @Override public void display(PrintWriter writer) { + displayUsage(writer); + } + + @Override public int exitCode() { + return 0; + } +} diff --git a/caliper/src/main/java/com/google/caliper/util/InterleavedReader.java b/caliper/src/main/java/com/google/caliper/util/InterleavedReader.java deleted file mode 100644 index 40293bf..0000000 --- a/caliper/src/main/java/com/google/caliper/util/InterleavedReader.java +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright (C) 2010 Google Inc. - * - * 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.google.caliper.util; - -import com.google.gson.JsonParser; - -import java.io.BufferedReader; -import java.io.Closeable; -import java.io.IOException; -import java.io.Reader; - -/** - * Reads a stream containing inline JSON objects. Each JSON object is prefixed - * by a marker string and suffixed by a newline character. - */ -public final class InterleavedReader implements Closeable { - - /** - * The length of the scratch buffer to search for markers in. Also acts as an - * upper bound on the length of returned strings. Not used as an I/O buffer. - */ - private static final int BUFFER_LENGTH = 80; - - private final String marker; - private final BufferedReader reader; - private final JsonParser jsonParser = new JsonParser(); - - public static final String DEFAULT_MARKER = "//ZxJ/"; - - public InterleavedReader(Reader reader) { - this(DEFAULT_MARKER, reader); - } - - public InterleavedReader(String marker, Reader reader) { - if (marker.length() > BUFFER_LENGTH) { - throw new IllegalArgumentException("marker.length() > BUFFER_LENGTH"); - } - this.marker = marker; - this.reader = reader instanceof BufferedReader - ? (BufferedReader) reader - : new BufferedReader(reader); - } - - /** - * Returns the next value in the stream: either a String, a JsonElement, or - * null to indicate the end of the stream. Callers should use instanceof to - * inspect the return type. - */ - public Object read() throws IOException { - char[] buffer = new char[BUFFER_LENGTH]; - reader.mark(BUFFER_LENGTH); - int count = 0; - int textEnd; - - while (true) { - int r = reader.read(buffer, count, buffer.length - count); - - if (r == -1) { - // the input is exhausted; return the remaining characters - textEnd = count; - break; - } - - count += r; - int possibleMarker = findPossibleMarker(buffer, count); - - if (possibleMarker != 0) { - // return the characters that precede the marker - textEnd = possibleMarker; - break; - } - - if (count < marker.length()) { - // the buffer contains only the prefix of a marker so we must read more - continue; - } - - // we've read a marker so return the value that follows - reader.reset(); - String json = reader.readLine().substring(marker.length()); - return jsonParser.parse(json); - } - - if (count == 0) { - return null; - } - - // return characters - reader.reset(); - count = reader.read(buffer, 0, textEnd); - return new String(buffer, 0, count); - } - - @Override public void close() throws IOException { - reader.close(); - } - - /** - * Returns the index of marker in {@code chars}, stopping at {@code limit}. - * Should the chars end with a prefix of marker, the offset of that prefix - * is returned. - */ - int findPossibleMarker(char[] chars, int limit) { - search: - for (int i = 0; true; i++) { - for (int m = 0; m < marker.length() && i + m < limit; m++) { - if (chars[i + m] != marker.charAt(m)) { - continue search; - } - } - return i; - } - } -} diff --git a/caliper/src/main/java/com/google/caliper/util/InvalidCommandException.java b/caliper/src/main/java/com/google/caliper/util/InvalidCommandException.java new file mode 100644 index 0000000..990ca7b --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/util/InvalidCommandException.java @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2011 Google Inc. + * + * 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.google.caliper.util; + +import com.google.common.collect.ImmutableList; + +import java.io.PrintWriter; +import java.util.List; + +/** + * Exception that signifies that the <i>user</i> has given an invalid argument string. + */ +@SuppressWarnings("serial") // who would serialize a command-line parsing error? +public class InvalidCommandException extends RuntimeException { + private ImmutableList<String> usage; + + public InvalidCommandException(String message, Object... args) { + super(String.format(message, args)); + } + + public void setUsage(List<String> usage) { + this.usage = ImmutableList.copyOf(usage); + } + + public void display(PrintWriter writer) { + writer.println(getMessage()); + if (usage != null) { + writer.println(); + displayUsage(writer); + } + } + + protected final void displayUsage(PrintWriter writer) { + for (String line : usage) { + writer.println(line); + } + } + + public int exitCode() { + return 1; + } +} diff --git a/caliper/src/main/java/com/google/caliper/util/OutputModule.java b/caliper/src/main/java/com/google/caliper/util/OutputModule.java new file mode 100644 index 0000000..8220436 --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/util/OutputModule.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2013 Google Inc. + * + * 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.google.caliper.util; + +import static com.google.common.base.Preconditions.checkNotNull; + +import dagger.Module; +import dagger.Provides; + +import java.io.PrintWriter; + +/** + * A module that binds {@link PrintWriter} instances for {@link Stdout} and {@link Stderr}. + */ +@Module +public final class OutputModule { + public static OutputModule system() { + return new OutputModule(new PrintWriter(System.out, true), new PrintWriter(System.err, true)); + } + + private final PrintWriter stdout; + private final PrintWriter stderr; + + public OutputModule(PrintWriter stdout, PrintWriter stderr) { + this.stdout = checkNotNull(stdout); + this.stderr = checkNotNull(stderr); + } + + @Provides @Stdout PrintWriter provideStdoutWriter() { + return stdout; + } + + @Provides @Stderr PrintWriter provideStderr() { + return stderr; + } +} diff --git a/caliper/src/main/java/com/google/caliper/util/Parser.java b/caliper/src/main/java/com/google/caliper/util/Parser.java new file mode 100644 index 0000000..0ab0d9e --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/util/Parser.java @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2011 Google Inc. + * + * 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.google.caliper.util; + +import java.text.ParseException; + +// TODO(kevinb): release common.text.Parser in Guava then nuke this +public interface Parser<T> { + T parse(CharSequence text) throws ParseException; +} diff --git a/caliper/src/main/java/com/google/caliper/util/Parsers.java b/caliper/src/main/java/com/google/caliper/util/Parsers.java new file mode 100644 index 0000000..3e56f1a --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/util/Parsers.java @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2011 Google Inc. + * + * 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.google.caliper.util; + +import com.google.common.collect.ImmutableList; +import com.google.common.primitives.Primitives; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.text.ParseException; +import java.util.List; + +public class Parsers { + public static final Parser<String> IDENTITY = new Parser<String>() { + @Override public String parse(CharSequence in) { + return in.toString(); + } + }; + + private static final List<String> CONVERSION_METHOD_NAMES = + ImmutableList.of("fromString", "decode", "valueOf"); + + /** + * Parser that tries, in this order: + * <ul> + * <li>ResultType.fromString(String) + * <li>ResultType.decode(String) + * <li>ResultType.valueOf(String) + * <li>new ResultType(String) + * </ul> + */ + public static <T> Parser<T> conventionalParser(Class<T> resultType) + throws NoSuchMethodException { + if (resultType == String.class) { + @SuppressWarnings("unchecked") // T == String + Parser<T> identity = (Parser<T>) IDENTITY; + return identity; + } + + final Class<T> wrappedResultType = Primitives.wrap(resultType); + + for (String methodName : CONVERSION_METHOD_NAMES) { + try { + final Method method = wrappedResultType.getDeclaredMethod(methodName, String.class); + + if (Util.isStatic(method) && wrappedResultType.isAssignableFrom(method.getReturnType())) { + method.setAccessible(true); // to permit inner enums, etc. + return new InvokingParser<T>() { + @Override protected T invoke(String input) throws Exception { + return wrappedResultType.cast(method.invoke(null, input)); + } + }; + } + } catch (Exception tryAgain) { + } + } + + final Constructor<T> constr = wrappedResultType.getDeclaredConstructor(String.class); + constr.setAccessible(true); + return new InvokingParser<T>() { + @Override protected T invoke(String input) throws Exception { + return wrappedResultType.cast(constr.newInstance(input)); + } + }; + } + + abstract static class InvokingParser<T> implements Parser<T> { + @Override public T parse(CharSequence input) throws ParseException { + try { + return invoke(input.toString()); + } catch (InvocationTargetException e) { + Throwable cause = e.getCause(); + String desc = firstNonNull(cause.getMessage(), cause.getClass().getSimpleName()); + throw newParseException(desc, cause); + } catch (Exception e) { + throw newParseException("Unknown parsing problem", e); + } + } + + protected abstract T invoke(String input) throws Exception; + } + + public static ParseException newParseException(String message, Throwable cause) { + ParseException pe = newParseException(message); + pe.initCause(cause); + return pe; + } + + public static ParseException newParseException(String message) { + return new ParseException(message, 0); + } + + private static <T> T firstNonNull(T first, T second) { + return (first != null) ? first : second; + } +} diff --git a/caliper/src/main/java/com/google/caliper/util/Reflection.java b/caliper/src/main/java/com/google/caliper/util/Reflection.java new file mode 100644 index 0000000..4b17d8d --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/util/Reflection.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2013 Google Inc. + * + * 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.google.caliper.util; + +import com.google.common.collect.ImmutableSet; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Method; + +/** + * A utility class for common reflection operations in Caliper. + */ +public final class Reflection { + private Reflection() {} + + public static ImmutableSet<Method> getAnnotatedMethods(Class<?> clazz, + Class<? extends Annotation> annotationClass) { + Method[] methods = clazz.getDeclaredMethods(); + ImmutableSet.Builder<Method> builder = ImmutableSet.builder(); + for (Method method : methods) { + if (method.isAnnotationPresent(annotationClass)) { + method.setAccessible(true); + builder.add(method); + } + } + return builder.build(); + } +} diff --git a/caliper/src/main/java/com/google/caliper/util/ShortDuration.java b/caliper/src/main/java/com/google/caliper/util/ShortDuration.java new file mode 100644 index 0000000..34d9a2b --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/util/ShortDuration.java @@ -0,0 +1,352 @@ +/* + * Copyright (C) 2011 Google Inc. + * + * 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.google.caliper.util; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +import com.google.common.base.Ascii; +import com.google.common.collect.ImmutableListMultimap; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Maps; +import com.google.common.primitives.Longs; + +import java.math.BigDecimal; +import java.math.MathContext; +import java.math.RoundingMode; +import java.util.Collections; +import java.util.Map; +import java.util.concurrent.TimeUnit; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import javax.annotation.Nullable; + +/** + * Represents a nonnegative duration from 0 to 100 days, with picosecond precision. + * Contrast with Joda-Time's duration class, which has only millisecond precision but can + * represent durations of millions of years. + */ +public abstract class ShortDuration implements Comparable<ShortDuration> { + // Factories + + public static ShortDuration of(long duration, TimeUnit unit) { + if (duration == 0) { + return ZERO; + } + checkArgument(duration >= 0, "negative duration: %s", duration); + checkArgument(duration <= MAXES.get(unit), + "ShortDuration cannot exceed 100 days: %s %s", duration, unit); + long nanos = TimeUnit.NANOSECONDS.convert(duration, unit); + return new PositiveShortDuration(nanos * 1000); + } + + public static ShortDuration of(BigDecimal duration, TimeUnit unit) { + // convert to picoseconds first, to minimize rounding + BigDecimal picos = duration.multiply(ONE_IN_PICOS.get(unit)); + return ofPicos(toLong(picos, RoundingMode.HALF_UP)); + } + + public static ShortDuration valueOf(String s) { + if ("0".equals(s)) { + return ZERO; + } + Matcher matcher = PATTERN.matcher(s); + checkArgument(matcher.matches(), "Invalid ShortDuration: %s", s); + + BigDecimal value = new BigDecimal(matcher.group(1)); + String abbrev = matcher.group(2); + TimeUnit unit = ABBREV_TO_UNIT.get(abbrev); + checkArgument(unit != null, "Unrecognized time unit: %s", abbrev); + + return of(value, unit); + } + + public static ShortDuration zero() { + return ZERO; + } + + // fortunately no abbreviation starts with 'e', so this should work + private static final Pattern PATTERN = Pattern.compile("^([0-9.eE+-]+) ?(\\S+)$"); + + private static ShortDuration ofPicos(long picos) { + if (picos == 0) { + return ZERO; + } + checkArgument(picos > 0); + return new PositiveShortDuration(picos); + } + + // TODO(kevinb): we sure seem to convert back and forth with BigDecimal a lot. + // Why not just *make* this a BigDecimal? + final long picos; + + ShortDuration(long picos) { + this.picos = picos; + } + + public long toPicos() { + return picos; + } + + public long to(TimeUnit unit) { + return to(unit, RoundingMode.HALF_UP); + } + + public abstract long to(TimeUnit unit, RoundingMode roundingMode); + + /* + * In Guava, this will probably implement an interface called Quantity, and the following methods + * will come from there, so they won't have to be defined here. + */ + + /** + * Returns an instance of this type that represents the sum of this value and {@code + * addend}. + */ + public abstract ShortDuration plus(ShortDuration addend); + + /** + * Returns an instance of this type that represents the difference of this value and + * {@code subtrahend}. + */ + public abstract ShortDuration minus(ShortDuration subtrahend); + + /** + * Returns an instance of this type that represents the product of this value and the + * integral value {@code multiplicand}. + */ + public abstract ShortDuration times(long multiplicand); + + /** + * Returns an instance of this type that represents the product of this value and {@code + * multiplicand}, rounded according to {@code roundingMode} if necessary. + * + * <p>If this class represents an amount that is "continuous" rather than discrete, the + * implementation of this method may simply ignore the rounding mode. + */ + public abstract ShortDuration times(BigDecimal multiplicand, RoundingMode roundingMode); + + /** + * Returns an instance of this type that represents this value divided by the integral + * value {@code divisor}, rounded according to {@code roundingMode} if necessary. + * + * <p>If this class represents an amount that is "continuous" rather than discrete, the + * implementation of this method may simply ignore the rounding mode. + */ + public abstract ShortDuration dividedBy(long divisor, RoundingMode roundingMode); + + /** + * Returns an instance of this type that represents this value divided by {@code + * divisor}, rounded according to {@code roundingMode} if necessary. + * + * <p>If this class represents an amount that is "continuous" rather than discrete, the + * implementation of this method may simply ignore the rounding mode. + */ + public abstract ShortDuration dividedBy(BigDecimal divisor, RoundingMode roundingMode); + + // Zero + + private static ShortDuration ZERO = new ShortDuration(0) { + @Override public long to(TimeUnit unit, RoundingMode roundingMode) { + return 0; + } + @Override public ShortDuration plus(ShortDuration addend) { + return addend; + } + @Override public ShortDuration minus(ShortDuration subtrahend) { + checkArgument(this == subtrahend); + return this; + } + @Override public ShortDuration times(long multiplicand) { + return this; + } + @Override public ShortDuration times(BigDecimal multiplicand, RoundingMode roundingMode) { + return this; + } + @Override public ShortDuration dividedBy(long divisor, RoundingMode roundingMode) { + return dividedBy(new BigDecimal(divisor), roundingMode); + } + @Override public ShortDuration dividedBy(BigDecimal divisor, RoundingMode roundingMode) { + checkArgument(divisor.compareTo(BigDecimal.ZERO) != 0); + return this; + } + @Override public int compareTo(ShortDuration that) { + if (this == that) { + return 0; + } + checkNotNull(that); + return -1; + } + @Override public boolean equals(@Nullable Object that) { + return this == that; + } + @Override public int hashCode() { + return 0; + } + @Override public String toString() { + return "0s"; + } + }; + + // Non-zero + + private static class PositiveShortDuration extends ShortDuration { + private PositiveShortDuration(long picos) { + super(picos); + checkArgument(picos > 0); + } + + @Override public long to(TimeUnit unit, RoundingMode roundingMode) { + BigDecimal divisor = ONE_IN_PICOS.get(unit); + return toLong(new BigDecimal(picos).divide(divisor), roundingMode); + } + + @Override public ShortDuration plus(ShortDuration addend) { + return new PositiveShortDuration(picos + addend.picos); + } + + @Override public ShortDuration minus(ShortDuration subtrahend) { + return ofPicos(picos - subtrahend.picos); + } + + @Override public ShortDuration times(long multiplicand) { + if (multiplicand == 0) { + return ZERO; + } + checkArgument(multiplicand >= 0, "negative multiplicand: %s", multiplicand); + checkArgument(multiplicand <= Long.MAX_VALUE / picos, + "product of %s and %s would overflow", this, multiplicand); + return new PositiveShortDuration(picos * multiplicand); + } + + @Override public ShortDuration times(BigDecimal multiplicand, RoundingMode roundingMode) { + BigDecimal product = BigDecimal.valueOf(picos).multiply(multiplicand); + return ofPicos(toLong(product, roundingMode)); + } + + @Override public ShortDuration dividedBy(long divisor, RoundingMode roundingMode) { + return dividedBy(new BigDecimal(divisor), roundingMode); + } + + @Override public ShortDuration dividedBy(BigDecimal divisor, RoundingMode roundingMode) { + BigDecimal product = BigDecimal.valueOf(picos).divide(divisor, roundingMode); + return ofPicos(product.longValueExact()); + } + + @Override public int compareTo(ShortDuration that) { + return Longs.compare(this.picos, that.picos); + } + + @Override public boolean equals(Object object) { + if (object instanceof PositiveShortDuration) { + PositiveShortDuration that = (PositiveShortDuration) object; + return this.picos == that.picos; + } + return false; + } + + @Override public int hashCode() { + return Longs.hashCode(picos); + } + + @Override public String toString() { + TimeUnit bestUnit = TimeUnit.NANOSECONDS; + for (TimeUnit unit : TimeUnit.values()) { + if (picosIn(unit) > picos) { + break; + } + bestUnit = unit; + } + BigDecimal divisor = ONE_IN_PICOS.get(bestUnit); + + return new BigDecimal(picos).divide(divisor, ROUNDER) + preferredAbbrev(bestUnit); + } + + private static final MathContext ROUNDER = new MathContext(4); + } + + // Private parts + + private static String preferredAbbrev(TimeUnit bestUnit) { + return ABBREVIATIONS.get(bestUnit).get(0); + } + + private static final ImmutableListMultimap<TimeUnit, String> ABBREVIATIONS = + createAbbreviations(); + + private static ImmutableListMultimap<TimeUnit, String> createAbbreviations() { + ImmutableListMultimap.Builder<TimeUnit, String> builder = ImmutableListMultimap.builder(); + builder.putAll(TimeUnit.NANOSECONDS, "ns", "nanos"); + builder.putAll(TimeUnit.MICROSECONDS, "\u03bcs" /*μs*/, "us", "micros"); + builder.putAll(TimeUnit.MILLISECONDS, "ms", "millis"); + builder.putAll(TimeUnit.SECONDS, "s", "sec"); + + // Do the rest in a JDK5-safe way + TimeUnit[] allUnits = TimeUnit.values(); + if (allUnits.length >= 7) { + builder.putAll(allUnits[4], "m", "min"); + builder.putAll(allUnits[5], "h", "hr"); + builder.putAll(allUnits[6], "d"); + } + + for (TimeUnit unit : TimeUnit.values()) { + builder.put(unit, Ascii.toLowerCase(unit.name())); + } + return builder.build(); + } + + private static final Map<String, TimeUnit> ABBREV_TO_UNIT = createAbbrevToUnitMap(); + + private static Map<String, TimeUnit> createAbbrevToUnitMap() { + ImmutableMap.Builder<String, TimeUnit> builder = ImmutableMap.builder(); + for (Map.Entry<TimeUnit, String> entry : ABBREVIATIONS.entries()) { + builder.put(entry.getValue(), entry.getKey()); + } + return builder.build(); + } + + private static final Map<TimeUnit, BigDecimal> ONE_IN_PICOS = createUnitToPicosMap(); + + private static Map<TimeUnit, BigDecimal> createUnitToPicosMap() { + Map<TimeUnit, BigDecimal> map = Maps.newEnumMap(TimeUnit.class); + for (TimeUnit unit : TimeUnit.values()) { + map.put(unit, new BigDecimal(picosIn(unit))); + } + return Collections.unmodifiableMap(map); + } + + private static final Map<TimeUnit, Long> MAXES = createMaxesMap(); + + private static Map<TimeUnit, Long> createMaxesMap() { + Map<TimeUnit, Long> map = Maps.newEnumMap(TimeUnit.class); + for (TimeUnit unit : TimeUnit.values()) { + // Max is 100 days + map.put(unit, unit.convert(100L * 24 * 60 * 60, TimeUnit.SECONDS)); + } + return Collections.unmodifiableMap(map); + } + + private static long toLong(BigDecimal bd, RoundingMode roundingMode) { + // setScale does not really mutate the BigDecimal + return bd.setScale(0, roundingMode).longValueExact(); + } + + private static long picosIn(TimeUnit unit) { + return unit.toNanos(1000); + } +} diff --git a/caliper/src/main/java/com/google/caliper/util/Stderr.java b/caliper/src/main/java/com/google/caliper/util/Stderr.java new file mode 100644 index 0000000..7aa9e1f --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/util/Stderr.java @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2013 Google Inc. + * + * 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.google.caliper.util; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import javax.inject.Qualifier; + +/** A binding annotation for standard err. */ +@Retention(RUNTIME) +@Target({FIELD, PARAMETER, METHOD}) +@Qualifier +public @interface Stderr {} diff --git a/caliper/src/main/java/com/google/caliper/util/Stdout.java b/caliper/src/main/java/com/google/caliper/util/Stdout.java new file mode 100644 index 0000000..26acf6d --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/util/Stdout.java @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2013 Google Inc. + * + * 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.google.caliper.util; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import javax.inject.Qualifier; + +/** A binding annotation for standard out. */ +@Retention(RUNTIME) +@Target({FIELD, PARAMETER, METHOD}) +@Qualifier +public @interface Stdout {} diff --git a/caliper/src/main/java/com/google/caliper/util/Util.java b/caliper/src/main/java/com/google/caliper/util/Util.java new file mode 100644 index 0000000..a3dc3f7 --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/util/Util.java @@ -0,0 +1,154 @@ +/* + * Copyright (C) 2011 Google Inc. + * + * 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.google.caliper.util; + +import com.google.common.collect.ImmutableBiMap; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Maps; +import com.google.common.io.ByteSource; +import com.google.common.io.Closer; +import com.google.common.io.Resources; + +import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.Member; +import java.lang.reflect.Modifier; +import java.util.Map; +import java.util.Properties; +import java.util.Set; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +public final class Util { + private Util() {} + + // Users have no idea that nested classes are identified with '$', not '.', so if class lookup + // fails try replacing the last . with $. + public static Class<?> lenientClassForName(String className) throws ClassNotFoundException { + try { + return loadClass(className); + } catch (ClassNotFoundException ignored) { + // try replacing the last dot with a $, in case that helps + // example: tutorial.Tutorial.Benchmark1 becomes tutorial.Tutorial$Benchmark1 + // amusingly, the $ character means three different things in this one line alone + String newName = className.replaceFirst("\\.([^.]+)$", "\\$$1"); + return loadClass(newName); + } + } + + /** + * Search for a class by name. + * + * @param className the name of the class. + * @return the class. + * @throws ClassNotFoundException if the class could not be found. + */ + public static Class<?> loadClass(String className) throws ClassNotFoundException { + // Use the thread context class loader. This is necessary because in some configurations, e.g. + // when run from a single JAR containing caliper and all its dependencies the caliper JAR + // ends up on the boot class path of the Worker and so needs to the use thread context class + // loader to load classes provided by the user. + return Class.forName(className, true, Thread.currentThread().getContextClassLoader()); + } + + public static ImmutableMap<String, String> loadProperties(ByteSource is) throws IOException { + Properties props = new Properties(); + Closer closer = Closer.create(); + InputStream in = closer.register(is.openStream()); + try { + props.load(in); + } finally { + closer.close(); + } + return Maps.fromProperties(props); + } + + public static ByteSource resourceSupplier(final Class<?> c, final String name) { + return Resources.asByteSource(c.getResource(name)); + } + + private static <T> ImmutableMap<String, T> prefixedSubmap( + Map<String, T> props, String prefix) { + ImmutableMap.Builder<String, T> submapBuilder = ImmutableMap.builder(); + for (Map.Entry<String, T> entry : props.entrySet()) { + String name = entry.getKey(); + if (name.startsWith(prefix)) { + submapBuilder.put(name.substring(prefix.length()), entry.getValue()); + } + } + return submapBuilder.build(); + } + + /** + * Returns a map containing only those entries whose key starts with {@code <groupName>.}. + * + * <p>The keys in the returned map have had their {@code <groupName>.} prefix removed. + * + * <p>e.g. If given a map that contained {@code group.key1 -> value1, key2 -> value2} and a + * {@code groupName} of {@code group} it would produce a map containing {@code key1 -> value1}. + */ + public static ImmutableMap<String, String> subgroupMap( + Map<String, String> map, String groupName) { + return prefixedSubmap(map, groupName + "."); + } + + public static boolean isPublic(Member member) { + return Modifier.isPublic(member.getModifiers()); + } + + public static boolean isStatic(Member member) { + return Modifier.isStatic(member.getModifiers()); + } + + private static final long FORCE_GC_TIMEOUT_SECS = 2; + + public static void forceGc() { + System.gc(); + System.runFinalization(); + final CountDownLatch latch = new CountDownLatch(1); + new Object() { + @Override protected void finalize() { + latch.countDown(); + } + }; + System.gc(); + System.runFinalization(); + try { + latch.await(FORCE_GC_TIMEOUT_SECS, TimeUnit.SECONDS); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } + + public static <T> ImmutableBiMap<T, String> assignNames(Set<T> items) { + ImmutableList<T> itemList = ImmutableList.copyOf(items); + ImmutableBiMap.Builder<T, String> itemNamesBuilder = ImmutableBiMap.builder(); + for (int i = 0; i < itemList.size(); i++) { + itemNamesBuilder.put(itemList.get(i), generateUniqueName(i)); + } + return itemNamesBuilder.build(); + } + + private static String generateUniqueName(int index) { + if (index < 26) { + return String.valueOf((char) ('A' + index)); + } else { + return generateUniqueName(index / 26 - 1) + generateUniqueName(index % 26); + } + } +} diff --git a/caliper/src/main/java/com/google/caliper/worker/AggregateAllocationsRecorder.java b/caliper/src/main/java/com/google/caliper/worker/AggregateAllocationsRecorder.java new file mode 100644 index 0000000..96fe5e1 --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/worker/AggregateAllocationsRecorder.java @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2013 Google Inc. + * + * 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.google.caliper.worker; + +import static com.google.common.base.Preconditions.checkState; + +import com.google.monitoring.runtime.instrumentation.Sampler; + +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; + +import javax.inject.Inject; + +/** + * An {@link AllocationRecorder} that records the number and cumulative size of allocation. + */ +final class AggregateAllocationsRecorder extends AllocationRecorder { + private final AtomicInteger allocationCount = new AtomicInteger(); + private final AtomicLong allocationSize = new AtomicLong(); + private volatile boolean recording = false; + + private final Sampler sampler = new Sampler() { + @Override public void sampleAllocation(int arrayCount, String desc, Object newObj, + long size) { + if (recording) { + allocationCount.getAndIncrement(); + allocationSize.getAndAdd(size); + } + } + }; + + @Inject AggregateAllocationsRecorder() { + com.google.monitoring.runtime.instrumentation.AllocationRecorder.addSampler(sampler); + } + + @Override protected void doStartRecording() { + checkState(!recording, "startRecording called, but we were already recording."); + allocationCount.set(0); + allocationSize.set(0); + recording = true; + } + + @Override public AllocationStats stopRecording(int reps) { + checkState(recording, "stopRecording called, but we were not recording."); + recording = false; + return new AllocationStats(allocationCount.get(), allocationSize.get(), reps); + } +}
\ No newline at end of file diff --git a/caliper/src/main/java/com/google/caliper/worker/AllAllocationsRecorder.java b/caliper/src/main/java/com/google/caliper/worker/AllAllocationsRecorder.java new file mode 100644 index 0000000..ae9baef --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/worker/AllAllocationsRecorder.java @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2013 Google Inc. + * + * 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.google.caliper.worker; + +import static com.google.common.base.Preconditions.checkState; +import static java.util.Arrays.asList; + +import com.google.caliper.runner.Running; +import com.google.common.collect.ConcurrentHashMultiset; +import com.google.monitoring.runtime.instrumentation.Sampler; + +import javax.inject.Inject; + +/** + * An {@link AllocationRecorder} that records every allocation and its location. + * + * <p>This recorder is enabled via the {@code trackAllocations} worker option. + */ +final class AllAllocationsRecorder extends AllocationRecorder { + private final Class<?> benchmarkClass; + private final String benchmarkMethodName; + private volatile boolean recording = false; + private final ConcurrentHashMultiset<Allocation> allocations = ConcurrentHashMultiset.create(); + + private final Sampler sampler = new Sampler() { + @Override public void sampleAllocation(int arrayCount, String desc, Object newObj, + long size) { + if (recording) { + if (arrayCount != -1) { + desc = desc + "[" + arrayCount + "]"; + } + // The first item is this line, the second is in AllocationRecorder and the + // one before that is the allocating line, so we start at index 2. + // We want to grab all lines until we get into the benchmark method. + StackTraceElement[] stackTrace = new Exception().getStackTrace(); + int startIndex = 2; + int endIndex = 2; + for (int i = startIndex; i < stackTrace.length; i++) { + StackTraceElement element = stackTrace[i]; + if (element.getClassName().startsWith( + AllAllocationsRecorder.class.getPackage().getName())) { + // Don't track locations up into the worker code, or originating within the worker + // code. + break; + } + endIndex = i; + if (element.getClassName().equals(benchmarkClass.getName()) + && element.getMethodName().equals(benchmarkMethodName)) { + // stop logging at the method under test + break; + } + } + allocations.add( + new Allocation(desc, size, asList(stackTrace).subList(startIndex, endIndex + 1))); + } + } + }; + + @Inject AllAllocationsRecorder(@Running.BenchmarkClass Class<?> benchmarkClass, + @Running.BenchmarkMethod String benchmarkMethodName) { + this.benchmarkClass = benchmarkClass; + this.benchmarkMethodName = benchmarkMethodName; + com.google.monitoring.runtime.instrumentation.AllocationRecorder.addSampler(sampler); + } + + @Override protected void doStartRecording() { + checkState(!recording, "startRecording called, but we were already recording."); + allocations.clear(); + recording = true; + } + + @Override public AllocationStats stopRecording(int reps) { + checkState(recording, "stopRecording called, but we were not recording."); + recording = false; + return new AllocationStats(allocations, reps); + } +}
\ No newline at end of file diff --git a/caliper/src/main/java/com/google/caliper/worker/Allocation.java b/caliper/src/main/java/com/google/caliper/worker/Allocation.java new file mode 100644 index 0000000..d855dc0 --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/worker/Allocation.java @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2013 Google Inc. + * + * 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.google.caliper.worker; + +import com.google.common.base.Joiner; +import com.google.common.base.Objects; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Interner; +import com.google.common.collect.Interners; + +import java.util.Collection; +import java.util.List; + +/** + * Data about a particular allocation performed by a benchmark. This tracks a human readable + * description of the allocation (e.g. 'int[23]', 'java.lang.Integer', or 'java.util.ArrayList'), + * the total size of the allocation in bytes and the location, which is a stringified stack trace of + * the allocation. + */ +final class Allocation { + // While particular lists of STEs can have a lot of variety within a benchmark there don't tend + // to be many individually unique STEs. This can save a lot of memory. + // Within a benchmark the code paths should be fairly uniform so it should be safe to just store + // these forever. + private static final Interner<StackTraceElement> steInterner = Interners.newWeakInterner(); + private static final Interner<String> descriptionInterner = Interners.newWeakInterner(); + + /** Returns the sum of the {@link #size sizes} of the allocations. */ + static long getTotalSize(Collection<Allocation> allocations) { + long totalSize = 0; + for (Allocation allocation : allocations) { + totalSize += allocation.size; + } + return totalSize; + } + + private final String description; + private final long size; + private final ImmutableList<StackTraceElement> location; + + Allocation(String description, long size, List<StackTraceElement> location) { + this.description = descriptionInterner.intern(description); + this.size = size; + ImmutableList.Builder<StackTraceElement> locationBuilder = ImmutableList.builder(); + for (StackTraceElement ste : location) { + locationBuilder.add(steInterner.intern(ste)); + } + this.location = locationBuilder.build(); + } + + @Override public boolean equals(Object obj) { + if (obj instanceof Allocation) { + Allocation other = (Allocation) obj; + return other.description.equals(description) + && other.size == size + && other.location.equals(location); + + } + return false; + } + + public String getDescription() { + return description; + } + + public long getSize() { + return size; + } + + @Override public int hashCode() { + return Objects.hashCode(description, size, location); + } + + @Override public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append(description).append(" (").append(size).append(" bytes)\n\tat "); + Joiner.on("\n\tat ").appendTo(builder, location); + return builder.toString(); + } +}
\ No newline at end of file diff --git a/caliper/src/main/java/com/google/caliper/worker/AllocationRecorder.java b/caliper/src/main/java/com/google/caliper/worker/AllocationRecorder.java new file mode 100644 index 0000000..3996e09 --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/worker/AllocationRecorder.java @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2013 Google Inc. + * + * 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.google.caliper.worker; + +/** + * An object that records all allocations that occur between {@link #startRecording()} + * and {@link #stopRecording(int)}. + * + * <p>This object can accurately track allocations made from multiple threads but is only + * expected to have {@link #startRecording()} and {@link #stopRecording(int)} called by a single + * thread. + */ +abstract class AllocationRecorder { + private boolean firstTime = true; + + /** + * Clears the prior state and starts a new recording. + * + * @throws IllegalStateException if the recording infrastructure is misconfigured. + */ + final void startRecording() { + if (firstTime) { + Object obj; + doStartRecording(); + obj = new Object(); + AllocationStats stats = stopRecording(1); + if (stats.getAllocationCount() != 1 || stats.getAllocationSize() < 1) { + throw new IllegalStateException( + String.format("The allocation recording infrastructure appears to be broken. " + + "Expected to find exactly one allocation of a java/lang/Object instead found %s", + stats)); + } + firstTime = false; + } + doStartRecording(); + } + + /** Clears the prior state and starts a new recording. */ + protected abstract void doStartRecording(); + + /** + * Stops recording allocations and saves all the allocation data recorded since the previous call + * to {@link #startRecording()} to an {@link AllocationStats} object. + * + * @param reps The number of reps that the previous set of allocation represents. + */ + abstract AllocationStats stopRecording(int reps); +} diff --git a/caliper/src/main/java/com/google/caliper/worker/AllocationStats.java b/caliper/src/main/java/com/google/caliper/worker/AllocationStats.java new file mode 100644 index 0000000..af3634c --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/worker/AllocationStats.java @@ -0,0 +1,215 @@ +/* + * Copyright (C) 2013 Google Inc. + * + * 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.google.caliper.worker; + +import static com.google.common.base.Preconditions.checkArgument; + +import com.google.caliper.model.Measurement; +import com.google.caliper.model.Value; +import com.google.common.base.MoreObjects; +import com.google.common.base.Objects; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMultiset; +import com.google.common.collect.Multiset; +import com.google.common.collect.Multiset.Entry; +import com.google.common.collect.Multisets; + +import java.text.DecimalFormat; +import java.util.Collection; + +/** + * A set of statistics about the allocations performed by a benchmark method. + */ +final class AllocationStats { + private final int allocationCount; + private final long allocationSize; + private final int reps; + private final ImmutableMultiset<Allocation> allocations; + + /** + * Constructs a new {@link AllocationStats} with the given number of allocations + * ({@code allocationCount}), cumulative size of the allocations ({@code allocationSize}) and the + * number of {@code reps} passed to the benchmark method. + */ + AllocationStats(int allocationCount, long allocationSize, int reps) { + this(allocationCount, allocationSize, reps, ImmutableMultiset.<Allocation>of()); + } + + /** + * Constructs a new {@link AllocationStats} with the given allocations and the number of + * {@code reps} passed to the benchmark method. + */ + AllocationStats(Collection<Allocation> allocations, int reps) { + this(allocations.size(), Allocation.getTotalSize(allocations), reps, + ImmutableMultiset.copyOf(allocations)); + } + + private AllocationStats(int allocationCount, long allocationSize, int reps, + Multiset<Allocation> allocations) { + checkArgument(allocationCount >= 0, "allocationCount (%s) was negative", allocationCount); + this.allocationCount = allocationCount; + checkArgument(allocationSize >= 0, "allocationSize (%s) was negative", allocationSize); + this.allocationSize = allocationSize; + checkArgument(reps >= 0, "reps (%s) was negative", reps); + this.reps = reps; + this.allocations = Multisets.copyHighestCountFirst(allocations); + } + + int getAllocationCount() { + return allocationCount; + } + + long getAllocationSize() { + return allocationSize; + } + + /** + * Computes and returns the difference between this measurement and the given + * {@code baseline} measurement. The {@code baseline} measurement must have a lower weight + * (fewer reps) than this measurement. + */ + AllocationStats minus(AllocationStats baseline) { + for (Entry<Allocation> entry : baseline.allocations.entrySet()) { + int superCount = allocations.count(entry.getElement()); + if (superCount < entry.getCount()) { + throw new IllegalStateException( + String.format("Your benchmark appears to have non-deterministic allocation behavior. " + + "Observed %d instance(s) of %s in the baseline but only %d in the actual " + + "measurement", + entry.getCount(), + entry.getElement(), + superCount)); + } + } + try { + return new AllocationStats(allocationCount - baseline.allocationCount, + allocationSize - baseline.allocationSize, + reps - baseline.reps, + Multisets.difference(allocations, baseline.allocations)); + } catch (IllegalArgumentException e) { + throw new IllegalStateException(String.format( + "Your benchmark appears to have non-deterministic allocation behavior. The difference " + + "between the baseline %s and the measurement %s is invalid. Consider enabling " + + "instrument.allocation.options.trackAllocations to get a more specific error message.", + baseline, this), e); + } + } + + /** + * Computes and returns the difference between this measurement and the given + * {@code baseline} measurement. Unlike {@link #minus(AllocationStats)} this does not have to + * be a super set of the baseline. + */ + public Delta delta(AllocationStats baseline) { + return new Delta( + allocationCount - baseline.allocationCount, + allocationSize - baseline.allocationSize, + reps - baseline.reps, + Multisets.difference(allocations, baseline.allocations), + Multisets.difference(baseline.allocations, allocations)); + } + + /** + * Returns a list of {@link Measurement measurements} based on this collection of stats. + */ + ImmutableList<Measurement> toMeasurements() { + for (Entry<Allocation> entry : allocations.entrySet()) { + double allocsPerRep = ((double) entry.getCount()) / reps; + System.out.printf("Allocated %f allocs per rep of %s%n", allocsPerRep, entry.getElement()); + } + return ImmutableList.of( + new Measurement.Builder() + .value(Value.create(allocationCount, "")) + .description("objects") + .weight(reps) + .build(), + new Measurement.Builder() + .value(Value.create(allocationSize, "B")) + .weight(reps) + .description("bytes") + .build()); + } + + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } else if (obj instanceof AllocationStats) { + AllocationStats that = (AllocationStats) obj; + return allocationCount == that.allocationCount + && allocationSize == that.allocationSize + && reps == that.reps + && Objects.equal(allocations, that.allocations); + } else { + return false; + } + } + + @Override + public int hashCode() { + return Objects.hashCode(allocationCount, allocationSize, reps, allocations); + } + + @Override public String toString() { + return MoreObjects.toStringHelper(this) + .add("allocationCount", allocationCount) + .add("allocationSize", allocationSize) + .add("reps", reps) + .add("allocations", allocations) + .toString(); + } + + /** + * The delta between two different sets of statistics. + */ + static final class Delta { + private final int count; + private final long size; + private final int reps; + private final Multiset<Allocation> additions; + private final Multiset<Allocation> removals; + + Delta( + int count, + long size, + int reps, + Multiset<Allocation> additions, + Multiset<Allocation> removals) { + this.count = count; + this.size = size; + this.reps = reps; + this.additions = additions; + this.removals = removals; + } + + /** + * Returns the long formatted with a leading +/- sign + */ + private static String formatWithLeadingSign(long n) { + return n > 0 ? "+" + n : "" + n; + } + + @Override public String toString() { + return MoreObjects.toStringHelper(this) + .add("count", formatWithLeadingSign(count)) + .add("size", formatWithLeadingSign(size)) + .add("reps", formatWithLeadingSign(reps)) + .add("additions", additions) + .add("removals", removals) + .toString(); + } + } +} diff --git a/caliper/src/main/java/com/google/caliper/worker/ArbitraryMeasurementWorker.java b/caliper/src/main/java/com/google/caliper/worker/ArbitraryMeasurementWorker.java new file mode 100644 index 0000000..75466f8 --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/worker/ArbitraryMeasurementWorker.java @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2011 Google Inc. + * + * 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.google.caliper.worker; + +import com.google.caliper.model.ArbitraryMeasurement; +import com.google.caliper.model.Measurement; +import com.google.caliper.model.Value; +import com.google.caliper.runner.Running.Benchmark; +import com.google.caliper.runner.Running.BenchmarkMethod; +import com.google.caliper.util.Util; +import com.google.common.collect.ImmutableSet; + +import java.lang.reflect.Method; +import java.util.Map; + +import javax.inject.Inject; + +/** + * Worker for arbitrary measurements. + */ +public final class ArbitraryMeasurementWorker extends Worker { + private final Options options; + private final String unit; + private final String description; + + @Inject ArbitraryMeasurementWorker( + @Benchmark Object benchmark, + @BenchmarkMethod Method method, + @WorkerOptions Map<String, String> workerOptions) { + super(benchmark, method); + this.options = new Options(workerOptions); + ArbitraryMeasurement annotation = method.getAnnotation(ArbitraryMeasurement.class); + this.unit = annotation.units(); + this.description = annotation.description(); + } + + @Override public void preMeasure(boolean inWarmup) throws Exception { + if (options.gcBeforeEach && !inWarmup) { + Util.forceGc(); + } + } + + @Override public Iterable<Measurement> measure() throws Exception { + double measured = (Double) benchmarkMethod.invoke(benchmark); + return ImmutableSet.of(new Measurement.Builder() + .value(Value.create(measured, unit)) + .weight(1) + .description(description) + .build()); + } + + private static class Options { + final boolean gcBeforeEach; + + Options(Map<String, String> options) { + this.gcBeforeEach = Boolean.parseBoolean(options.get("gcBeforeEach")); + } + } +} diff --git a/caliper/src/main/java/com/google/caliper/worker/MacrobenchmarkAllocationWorker.java b/caliper/src/main/java/com/google/caliper/worker/MacrobenchmarkAllocationWorker.java new file mode 100644 index 0000000..45af4f7 --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/worker/MacrobenchmarkAllocationWorker.java @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2011 Google Inc. + * + * 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.google.caliper.worker; + +import com.google.caliper.model.Measurement; +import com.google.caliper.runner.Running.Benchmark; +import com.google.caliper.runner.Running.BenchmarkMethod; +import com.google.common.collect.ImmutableList; + +import java.lang.reflect.Method; + +import javax.inject.Inject; + +/** + * The {@link Worker} for the {@code AllocationInstrument}. This class invokes the benchmark method + * a few times, with varying numbers of reps, and computes the number of object allocations and the + * total size of those allocations. + */ +public final class MacrobenchmarkAllocationWorker extends Worker { + private final AllocationRecorder recorder; + + @Inject MacrobenchmarkAllocationWorker(@Benchmark Object benchmark, + @BenchmarkMethod Method method, AllocationRecorder recorder) { + super(benchmark, method); + this.recorder = recorder; + } + + @Override public void bootstrap() throws Exception { + // do one initial measurement and throw away its results + measureAllocations(benchmark, benchmarkMethod); + } + + @Override public ImmutableList<Measurement> measure() throws Exception { + return measureAllocations(benchmark, benchmarkMethod).toMeasurements(); + } + + private AllocationStats measureAllocations(Object benchmark, Method method) throws Exception { + recorder.startRecording(); + method.invoke(benchmark); + return recorder.stopRecording(1); + } +} diff --git a/caliper/src/main/java/com/google/caliper/worker/MacrobenchmarkWorker.java b/caliper/src/main/java/com/google/caliper/worker/MacrobenchmarkWorker.java new file mode 100644 index 0000000..5960084 --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/worker/MacrobenchmarkWorker.java @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2013 Google Inc. + * + * 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.google.caliper.worker; + +import static com.google.caliper.util.Reflection.getAnnotatedMethods; +import static java.util.concurrent.TimeUnit.NANOSECONDS; + +import com.google.caliper.api.AfterRep; +import com.google.caliper.api.BeforeRep; +import com.google.caliper.model.Measurement; +import com.google.caliper.model.Value; +import com.google.caliper.runner.Running.Benchmark; +import com.google.caliper.runner.Running.BenchmarkMethod; +import com.google.caliper.util.Util; +import com.google.common.base.Stopwatch; +import com.google.common.base.Ticker; +import com.google.common.collect.ImmutableSet; + +import java.lang.reflect.Method; +import java.util.Map; + +import javax.inject.Inject; + +/** + * The {@link Worker} implementation for macrobenchmarks. + */ +public class MacrobenchmarkWorker extends Worker { + private final Stopwatch stopwatch; + private final ImmutableSet<Method> beforeRepMethods; + private final ImmutableSet<Method> afterRepMethods; + private final boolean gcBeforeEach; + + @Inject MacrobenchmarkWorker(@Benchmark Object benchmark, @BenchmarkMethod Method method, + Ticker ticker, @WorkerOptions Map<String, String> workerOptions) { + super(benchmark, method); + this.stopwatch = Stopwatch.createUnstarted(ticker); + this.beforeRepMethods = + getAnnotatedMethods(benchmark.getClass(), BeforeRep.class); + this.afterRepMethods = + getAnnotatedMethods(benchmark.getClass(), AfterRep.class); + this.gcBeforeEach = Boolean.parseBoolean(workerOptions.get("gcBeforeEach")); + } + + @Override public void preMeasure(boolean inWarmup) throws Exception { + for (Method beforeRepMethod : beforeRepMethods) { + beforeRepMethod.invoke(benchmark); + } + if (gcBeforeEach && !inWarmup) { + Util.forceGc(); + } + } + + @Override public Iterable<Measurement> measure() throws Exception { + stopwatch.start(); + benchmarkMethod.invoke(benchmark); + long nanos = stopwatch.stop().elapsed(NANOSECONDS); + stopwatch.reset(); + return ImmutableSet.of(new Measurement.Builder() + .description("runtime") + .weight(1) + .value(Value.create(nanos, "ns")) + .build()); + } + + @Override public void postMeasure() throws Exception { + for (Method afterRepMethod : afterRepMethods) { + afterRepMethod.invoke(benchmark); + } + } +} diff --git a/caliper/src/main/java/com/google/caliper/worker/MicrobenchmarkAllocationWorker.java b/caliper/src/main/java/com/google/caliper/worker/MicrobenchmarkAllocationWorker.java new file mode 100644 index 0000000..a4797b0 --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/worker/MicrobenchmarkAllocationWorker.java @@ -0,0 +1,144 @@ +/* + * Copyright (C) 2011 Google Inc. + * + * 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.google.caliper.worker; + +import com.google.caliper.model.Measurement; +import com.google.caliper.runner.Running.Benchmark; +import com.google.caliper.runner.Running.BenchmarkMethod; + +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +import javax.inject.Inject; + +/** + * The {@link Worker} for the {@code AllocationInstrument}. This class invokes the benchmark method + * a few times, with varying numbers of reps, and computes the number of object allocations and the + * total size of those allocations. + */ +public final class MicrobenchmarkAllocationWorker extends Worker { + // TODO(gak): make this or something like this an option + private static final int WARMUP_REPS = 10; + private static final int MAX_REPS = 100; + + /** + * The number of consecutive measurement runs that must have matching allocations during the warm + * up in order for the method to be determined to be deterministic. + */ + private static final int DETERMINISTIC_BENCHMARK_THRESHOLD = 2; + + /** + * The maximum number of warm up measurements to take before determining that the test is + * non-deterministic. + */ + private static final int DETERMINISTIC_MEASUREMENT_COUNT = DETERMINISTIC_BENCHMARK_THRESHOLD + 3; + + private static final String LINE_SEPARATOR = System.getProperty("line.separator"); + + private final Random random; + private final AllocationRecorder recorder; + + @Inject MicrobenchmarkAllocationWorker(@Benchmark Object benchmark, + @BenchmarkMethod Method method, AllocationRecorder recorder, Random random) { + super(benchmark, method); + this.random = random; + this.recorder = recorder; + } + + @Override public void bootstrap() throws Exception { + // do some initial measurements and throw away the results. this warms up the bootstrap method + // itself and also the method invocation path for calling that method. + + // warm up the loop in the benchmark method. + measureAllocations(benchmark, benchmarkMethod, WARMUP_REPS); + + // verify that the benchmark is deterministic in terms of the measured allocations. + verifyBenchmarkIsDeterministic(); + } + + /** + * Verify the determinism of the benchmark method. + * + * <p>The method invocation path, i.e. the code that the JVM executes to invoke the method, can + * vary depending on how many times it is run with a corresponding effect on the allocations + * measured. The invocations performed by this method should be sufficient to cause the JVM to + * settle on a single path for invoking the benchmark method and so cause identical allocations + * for each subsequent invocation. If tests start to fail with lots of non-deterministic + * allocation errors then it's possible that additional invocations are required in which case + * the value of {@link #DETERMINISTIC_BENCHMARK_THRESHOLD} should be increased. + */ + private void verifyBenchmarkIsDeterministic() throws Exception { + // keep track of all the statistics generated while warming up the method invocation path. + List<AllocationStats> history = new ArrayList<AllocationStats>(); + + // warm up the method invocation path by calling the benchmark multiple times with 0 reps. + AllocationStats baseline = null; + int matchingSequenceLength = 1; + for (int i = 0; i < DETERMINISTIC_MEASUREMENT_COUNT; ++i) { + AllocationStats stats = measureAllocations(benchmark, benchmarkMethod, 0); + history.add(stats); + if (stats.equals(baseline)) { + // if consecutive measurements with the same allocation characteristics reaches the + // threshold then treat the benchmark as being deterministic. + if (++matchingSequenceLength == DETERMINISTIC_BENCHMARK_THRESHOLD) { + return; + } + } else { + matchingSequenceLength = 1; + baseline = stats; + } + } + + // the baseline allocations did not settle down and so are probably non-deterministic. + StringBuilder builder = new StringBuilder(100); + AllocationStats previous = null; + for (AllocationStats allocationStats : history) { + if (previous == null) { + builder.append(LINE_SEPARATOR).append(" ").append(allocationStats); + } else { + AllocationStats.Delta delta = allocationStats.delta(previous); + builder.append(LINE_SEPARATOR).append(" ").append(delta); + } + previous = allocationStats; + } + throw new IllegalStateException(String.format( + "Your benchmark appears to have non-deterministic allocation behavior. " + + "During the warm up process there was no consecutive sequence of %d runs with" + + " identical allocations. The allocation history is:%s", + DETERMINISTIC_BENCHMARK_THRESHOLD, builder)); + } + + @Override public Iterable<Measurement> measure() throws Exception { + AllocationStats baseline = measureAllocations(benchmark, benchmarkMethod, 0); + // [1, MAX_REPS] + int measurementReps = random.nextInt(MAX_REPS) + 1; + AllocationStats measurement = measureAllocations(benchmark, benchmarkMethod, measurementReps); + return measurement.minus(baseline).toMeasurements(); + } + + private AllocationStats measureAllocations( + Object benchmark, Method method, int reps) throws Exception { + // do the Integer boxing and the creation of the Object[] outside of the record block, so that + // our internal allocations aren't counted in the benchmark's allocations. + Object[] args = {reps}; + recorder.startRecording(); + method.invoke(benchmark, args); + return recorder.stopRecording(reps); + } +} diff --git a/caliper/src/main/java/com/google/caliper/worker/RuntimeWorker.java b/caliper/src/main/java/com/google/caliper/worker/RuntimeWorker.java new file mode 100644 index 0000000..7d96229 --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/worker/RuntimeWorker.java @@ -0,0 +1,152 @@ +/* + * Copyright (C) 2011 Google Inc. + * + * 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.google.caliper.worker; + +import static java.util.concurrent.TimeUnit.NANOSECONDS; + +import com.google.caliper.model.Measurement; +import com.google.caliper.model.Value; +import com.google.caliper.runner.InvalidBenchmarkException; +import com.google.caliper.runner.Running.Benchmark; +import com.google.caliper.runner.Running.BenchmarkMethod; +import com.google.caliper.util.ShortDuration; +import com.google.caliper.util.Util; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Ticker; +import com.google.common.collect.ImmutableSet; + +import java.lang.reflect.Method; +import java.util.Map; +import java.util.Random; + +import javax.inject.Inject; + +/** + * A {@link Worker} base class for micro and pico benchmarks. + */ +public abstract class RuntimeWorker extends Worker { + @VisibleForTesting static final int INITIAL_REPS = 100; + + protected final Random random; + protected final Ticker ticker; + protected final Options options; + private long totalReps; + private long totalNanos; + private long nextReps; + + RuntimeWorker(Object benchmark, + Method method, Random random, Ticker ticker, + Map<String, String> workerOptions) { + super(benchmark, method); + this.random = random; + // TODO(gak): investigate whether or not we can use Stopwatch + this.ticker = ticker; + this.options = new Options(workerOptions); + } + + @Override public void bootstrap() throws Exception { + totalReps = INITIAL_REPS; + totalNanos = invokeTimeMethod(INITIAL_REPS); + } + + @Override public void preMeasure(boolean inWarmup) throws Exception { + nextReps = calculateTargetReps(totalReps, totalNanos, options.timingIntervalNanos, + random.nextGaussian()); + if (options.gcBeforeEach && !inWarmup) { + Util.forceGc(); + } + } + + @Override public Iterable<Measurement> measure() throws Exception { + long nanos = invokeTimeMethod(nextReps); + Measurement measurement = new Measurement.Builder() + .description("runtime") + .value(Value.create(nanos, "ns")) + .weight(nextReps) + .build(); + + totalReps += nextReps; + totalNanos += nanos; + return ImmutableSet.of(measurement); + } + + abstract long invokeTimeMethod(long reps) throws Exception; + + /** + * Returns a random number of reps based on a normal distribution around the estimated number of + * reps for the timing interval. The distribution used has a standard deviation of one fifth of + * the estimated number of reps. + */ + @VisibleForTesting static long calculateTargetReps(long reps, long nanos, long targetNanos, + double gaussian) { + double targetReps = (((double) reps) / nanos) * targetNanos; + return Math.max(1L, Math.round((gaussian * (targetReps / 5)) + targetReps)); + } + + /** + * A {@link Worker} for micro benchmarks. + */ + public static final class Micro extends RuntimeWorker { + @Inject Micro(@Benchmark Object benchmark, + @BenchmarkMethod Method method, Random random, Ticker ticker, + @WorkerOptions Map<String, String> workerOptions) { + super(benchmark, method, random, ticker, workerOptions); + } + + @Override long invokeTimeMethod(long reps) throws Exception { + int intReps = (int) reps; + if (reps != intReps) { + throw new InvalidBenchmarkException("%s.%s takes an int for reps, " + + "but requires a greater number to fill the given timing interval (%s). " + + "If this is expected (the benchmarked code is very fast), use a long parameter." + + "Otherwise, check your benchmark for errors.", + benchmark.getClass(), benchmarkMethod.getName(), + ShortDuration.of(options.timingIntervalNanos, NANOSECONDS)); + } + long before = ticker.read(); + benchmarkMethod.invoke(benchmark, intReps); + return ticker.read() - before; + } + } + + /** + * A {@link Worker} for pico benchmarks. + */ + public static final class Pico extends RuntimeWorker { + @Inject Pico(@Benchmark Object benchmark, + @BenchmarkMethod Method method, Random random, Ticker ticker, + @WorkerOptions Map<String, String> workerOptions) { + super(benchmark, method, random, ticker, workerOptions); + } + + @Override long invokeTimeMethod(long reps) throws Exception { + long before = ticker.read(); + benchmarkMethod.invoke(benchmark, reps); + return ticker.read() - before; + } + } + + private static final class Options { + long timingIntervalNanos; + boolean gcBeforeEach; + + Options(Map<String, String> optionMap) { + this.timingIntervalNanos = Long.parseLong(optionMap.get("timingIntervalNanos")); + this.gcBeforeEach = Boolean.parseBoolean(optionMap.get("gcBeforeEach")); + } + } +} diff --git a/caliper/src/main/java/com/google/caliper/worker/Worker.java b/caliper/src/main/java/com/google/caliper/worker/Worker.java new file mode 100644 index 0000000..d0f5c08 --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/worker/Worker.java @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2011 Google Inc. + * + * 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.google.caliper.worker; + +import com.google.caliper.model.Measurement; +import com.google.caliper.runner.Running.AfterExperimentMethods; +import com.google.caliper.runner.Running.BeforeExperimentMethods; +import com.google.common.collect.ImmutableSet; + +import java.lang.reflect.Method; + +import javax.inject.Inject; + +/** + * A {@link Worker} collects measurements on behalf of a particular Instrument. + */ +public abstract class Worker { + @Inject + @BeforeExperimentMethods + ImmutableSet<Method> beforeExperimentMethods; + + @Inject + @AfterExperimentMethods + ImmutableSet<Method> afterExperimentMethods; + + protected final Method benchmarkMethod; + protected final Object benchmark; + + protected Worker(Object benchmark, Method method) { + this.benchmark = benchmark; + this.benchmarkMethod = method; + } + + /** Initializes the benchmark object. */ + final void setUpBenchmark() throws Exception { + for (Method method : beforeExperimentMethods) { + method.invoke(benchmark); + } + } + + /** Called once before all measurements but after benchmark setup. */ + public void bootstrap() throws Exception {} + + /** + * Called immediately before {@link #measure()}. + * + * @param inWarmup whether we are in warmup, or taking real measurements. Used by + * some implementations to skip forcing GC to make warmup faster. + */ + public void preMeasure(boolean inWarmup) throws Exception {} + + /** Called immediately after {@link #measure()}. */ + public void postMeasure() throws Exception {} + + /** Template method for workers that produce multiple measurements. */ + public abstract Iterable<Measurement> measure() throws Exception; + + /** Tears down the benchmark object. */ + final void tearDownBenchmark() throws Exception { + for (Method method : afterExperimentMethods) { + method.invoke(benchmark); + } + } +} diff --git a/caliper/src/main/java/com/google/caliper/worker/WorkerComponent.java b/caliper/src/main/java/com/google/caliper/worker/WorkerComponent.java new file mode 100644 index 0000000..40b11da --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/worker/WorkerComponent.java @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2015 Google Inc. + * + * 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.google.caliper.worker; + +import com.google.caliper.bridge.BridgeModule; +import com.google.caliper.runner.BenchmarkClassModule; +import com.google.caliper.runner.ExperimentModule; +import dagger.Component; +import javax.inject.Singleton; + +/** + * Creates {@link Worker} for an {@link com.google.caliper.runner.Experiment}. + */ +@Singleton +@Component(modules = { + BenchmarkClassModule.class, + BridgeModule.class, + ExperimentModule.class, + WorkerModule.class +}) +interface WorkerComponent { + Worker getWorker(); +} diff --git a/caliper/src/main/java/com/google/caliper/worker/WorkerEventLog.java b/caliper/src/main/java/com/google/caliper/worker/WorkerEventLog.java new file mode 100644 index 0000000..7b903d4 --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/worker/WorkerEventLog.java @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2011 Google Inc. + * + * 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.google.caliper.worker; + +import com.google.caliper.bridge.FailureLogMessage; +import com.google.caliper.bridge.OpenedSocket; +import com.google.caliper.bridge.ShouldContinueMessage; +import com.google.caliper.bridge.StartMeasurementLogMessage; +import com.google.caliper.bridge.StartupAnnounceMessage; +import com.google.caliper.bridge.StopMeasurementLogMessage; +import com.google.caliper.bridge.VmPropertiesLogMessage; +import com.google.caliper.model.Measurement; + +import java.io.Closeable; +import java.io.IOException; +import java.util.UUID; + +/** The worker's interface for communicating with the runner. */ +final class WorkerEventLog implements Closeable { + private final OpenedSocket.Writer writer; + private final OpenedSocket.Reader reader; + + WorkerEventLog(OpenedSocket socket) { + this.writer = socket.writer(); + this.reader = socket.reader(); + } + + void notifyWorkerStarted(UUID trialId) throws IOException { + writer.write(new StartupAnnounceMessage(trialId)); + writer.write(new VmPropertiesLogMessage()); + writer.flush(); + } + + void notifyBootstrapPhaseStarting() throws IOException { + writer.write("Bootstrap phase starting."); + writer.flush(); + } + + void notifyMeasurementPhaseStarting() throws IOException { + writer.write("Measurement phase starting (includes warmup and actual measurement)."); + writer.flush(); + } + + void notifyMeasurementStarting() throws IOException { + writer.write("About to measure."); + writer.write(new StartMeasurementLogMessage()); + writer.flush(); + } + + /** + * Report the measurements and wait for it to be ack'd by the runner. Returns a message received + * from the runner, which lets us know whether to continue measuring and whether we're in the + * warmup or measurement phase. + */ + ShouldContinueMessage notifyMeasurementEnding(Iterable<Measurement> measurements) throws + IOException { + writer.write(new StopMeasurementLogMessage(measurements)); + writer.flush(); + return (ShouldContinueMessage) reader.read(); + } + + void notifyFailure(Exception e) throws IOException { + writer.write(new FailureLogMessage(e)); + writer.flush(); + } + + @Override public void close() throws IOException { + try { + reader.close(); + } finally { + writer.close(); + } + } +} diff --git a/caliper/src/main/java/com/google/caliper/worker/WorkerMain.java b/caliper/src/main/java/com/google/caliper/worker/WorkerMain.java new file mode 100644 index 0000000..34ec674 --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/worker/WorkerMain.java @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2011 Google Inc. + * + * 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.google.caliper.worker; + +import com.google.caliper.bridge.CommandLineSerializer; +import com.google.caliper.bridge.OpenedSocket; +import com.google.caliper.bridge.ShouldContinueMessage; +import com.google.caliper.bridge.WorkerSpec; +import com.google.caliper.runner.ExperimentModule; +import com.google.common.net.InetAddresses; + +import java.net.InetSocketAddress; +import java.nio.channels.SocketChannel; + +/** + * This class is invoked as a subprocess by the Caliper runner parent process; it re-stages + * the benchmark and hands it off to the instrument's worker. + */ +public final class WorkerMain { + private WorkerMain() {} + + public static void main(String[] args) throws Exception { + // TODO(lukes): instead of parsing the spec from the command line pass the port number on the + // command line and then receive the spec from the socket. This way we can start JVMs prior + // to starting experiments and thus get better experiment latency. + WorkerSpec request = CommandLineSerializer.parse(args[0]); + // nonblocking connect so we can interleave the system call with injector creation. + SocketChannel channel = SocketChannel.open(); + channel.configureBlocking(false); + channel.connect(new InetSocketAddress(InetAddresses.forString("127.0.0.1"), request.port)); + + WorkerComponent workerComponent = DaggerWorkerComponent.builder() + .experimentModule(ExperimentModule.forWorkerSpec(request)) + .workerModule(new WorkerModule(request)) + .build(); + Worker worker = workerComponent.getWorker(); + WorkerEventLog log = new WorkerEventLog(OpenedSocket.fromSocket(channel)); + + log.notifyWorkerStarted(request.trialId); + try { + worker.setUpBenchmark(); + log.notifyBootstrapPhaseStarting(); + worker.bootstrap(); + log.notifyMeasurementPhaseStarting(); + boolean keepMeasuring = true; + boolean isInWarmup = true; + while (keepMeasuring) { + worker.preMeasure(isInWarmup); + log.notifyMeasurementStarting(); + try { + ShouldContinueMessage message = log.notifyMeasurementEnding(worker.measure()); + keepMeasuring = message.shouldContinue(); + isInWarmup = !message.isWarmupComplete(); + } finally { + worker.postMeasure(); + } + } + } catch (Exception e) { + log.notifyFailure(e); + } finally { + System.out.flush(); // ? + worker.tearDownBenchmark(); + log.close(); + } + } +} diff --git a/caliper/src/main/java/com/google/caliper/worker/WorkerModule.java b/caliper/src/main/java/com/google/caliper/worker/WorkerModule.java new file mode 100644 index 0000000..1f7e201 --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/worker/WorkerModule.java @@ -0,0 +1,144 @@ +/* + * Copyright (C) 2012 Google Inc. + * + * 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.google.caliper.worker; + +import com.google.caliper.Param; +import com.google.caliper.bridge.WorkerSpec; +import com.google.caliper.runner.Running; +import com.google.caliper.util.InvalidCommandException; +import com.google.caliper.util.Util; +import com.google.common.base.Ticker; +import com.google.common.collect.ImmutableMap; +import dagger.MapKey; +import dagger.Module; +import dagger.Provides; +import dagger.Provides.Type; + +import java.util.Map; +import java.util.Random; + +import javax.inject.Provider; + +/** + * Binds classes necessary for the worker. Also manages the injection of {@link Param parameters} + * from the {@link WorkerSpec} into the benchmark. + * + * <p>TODO(gak): Ensure that each worker only has bindings for the objects it needs and not the + * objects required by different workers. (i.e. don't bind a Ticker if the worker is an allocation + * worker). + */ +@Module +final class WorkerModule { + private final Class<? extends Worker> workerClass; + private final ImmutableMap<String, String> workerOptions; + + private final Class<?> benchmarkClassObject; + + WorkerModule(WorkerSpec workerSpec) throws ClassNotFoundException { + this.workerClass = workerSpec.workerClass.asSubclass(Worker.class); + this.workerOptions = workerSpec.workerOptions; + + benchmarkClassObject = Util.loadClass(workerSpec.benchmarkSpec.className()); + } + + @Provides + @Running.BenchmarkClass + Class<?> provideBenchmarkClassObject() { + return benchmarkClassObject; + } + + @Provides + Worker provideWorker(Map<Class<? extends Worker>, Provider<Worker>> availableWorkers) { + Provider<Worker> workerProvider = availableWorkers.get(workerClass); + if (workerProvider == null) { + throw new InvalidCommandException("%s is not a supported worker (%s).", + workerClass, availableWorkers); + } + return workerProvider.get(); + } + + /** + * Specifies the {@link Class} object to use as a key in the map of available + * {@link Worker workers} passed to {@link #provideWorker(Map)}. + */ + @MapKey(unwrapValue = true) + public @interface WorkerClassKey { + Class<? extends Worker> value(); + } + + @Provides(type = Type.MAP) + @WorkerClassKey(ArbitraryMeasurementWorker.class) + static Worker provideArbitraryMeasurementWorker(ArbitraryMeasurementWorker impl) { + return impl; + } + + @Provides(type = Type.MAP) + @WorkerClassKey(MicrobenchmarkAllocationWorker.class) + static Worker provideMicrobenchmarkAllocationWorker(MicrobenchmarkAllocationWorker impl) { + return impl; + } + + @Provides(type = Type.MAP) + @WorkerClassKey(MacrobenchmarkWorker.class) + static Worker provideMacrobenchmarkWorker(MacrobenchmarkWorker impl) { + return impl; + } + + @Provides(type = Type.MAP) + @WorkerClassKey(MacrobenchmarkAllocationWorker.class) + static Worker provideMacrobenchmarkAllocationWorker(MacrobenchmarkAllocationWorker impl) { + return impl; + } + + @Provides(type = Type.MAP) + @WorkerClassKey(RuntimeWorker.Micro.class) + static Worker provideRuntimeWorkerMicro(RuntimeWorker.Micro impl) { + return impl; + } + + @Provides(type = Type.MAP) + @WorkerClassKey(RuntimeWorker.Pico.class) + static Worker provideRuntimeWorkerPico(RuntimeWorker.Pico impl) { + return impl; + } + + @Provides + static Ticker provideTicker() { + return Ticker.systemTicker(); + } + + @Provides + AllocationRecorder provideAllocationRecorder( + Provider<AllAllocationsRecorder> allAllocationsRecorderProvider, + Provider<AggregateAllocationsRecorder> aggregateAllocationsRecorderProvider) { + + return Boolean.valueOf(workerOptions.get("trackAllocations")) + ? allAllocationsRecorderProvider.get() + : aggregateAllocationsRecorderProvider.get(); + } + + @Provides + static Random provideRandom() { + return new Random(); + } + + @Provides + @WorkerOptions + Map<String, String> provideWorkerOptions() { + return workerOptions; + } +} diff --git a/caliper/src/main/java/com/google/caliper/worker/WorkerOptions.java b/caliper/src/main/java/com/google/caliper/worker/WorkerOptions.java new file mode 100644 index 0000000..bc93b18 --- /dev/null +++ b/caliper/src/main/java/com/google/caliper/worker/WorkerOptions.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2013 Google Inc. + * + * 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.google.caliper.worker; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import javax.inject.Qualifier; + +@Retention(RUNTIME) +@Target({FIELD, PARAMETER, METHOD}) +@Qualifier +@interface WorkerOptions {} diff --git a/caliper/src/main/resources/com/google/caliper/config/default-config.properties b/caliper/src/main/resources/com/google/caliper/config/default-config.properties new file mode 100644 index 0000000..ee5b3ee --- /dev/null +++ b/caliper/src/main/resources/com/google/caliper/config/default-config.properties @@ -0,0 +1,14 @@ +# Caliper config file +# Run with --print-config to see all of the options being applied + +# INSTRUMENT CONFIG +# instrument.micro.options.warmup=10s +# instrument.micro.options.timingInterval=500ms +# instrument.micro.options.reportedIntervals=7 +# instrument.micro.options.maxRuntime=10s + +# VM CONFIG +vm.args=-Xmx3g -Xms3g + +# See the Caliper webapp to get a key so you can associate results with your account +results.upload.options.key= diff --git a/caliper/src/main/resources/com/google/caliper/config/global-config.properties b/caliper/src/main/resources/com/google/caliper/config/global-config.properties new file mode 100644 index 0000000..dd741f0 --- /dev/null +++ b/caliper/src/main/resources/com/google/caliper/config/global-config.properties @@ -0,0 +1,90 @@ +# Caliper global config file +# Users' ~/.caliper/config settings may override these + +###################### +# VM CONFIGURATION +###################### + +# This directory can be automatically prepended to non-absolute VM paths +vm.baseDirectory=/usr/local/buildtools/java + +# Standard vm parameter options. +vm.args= + +# Common configurations + +vm.jdk-32-client.home=jdk-32 +vm.jdk-32-client.args=-d32 -client + +vm.jdk-32-server.home=jdk-32 +vm.jdk-32-server.args=-d32 -server + +vm.jdk-64-compressed.home=jdk-64 +vm.jdk-64-compressed.args=-d64 -XX:+UseCompressedOops + +vm.jdk-64-uncompressed.home=jdk-64 +vm.jdk-64-uncompressed.args=-d64 -XX:-UseCompressedOops + + +###################### +# INSTRUMENT CONFIG +###################### + +# To define new instrument configurations, provide an "instrument.<name>.class" property +# pointing to a concrete class that extends com.google.caliper.runner.Instrument, and add +# whichever other options it supports using "instrument.<name>.<optionName>=<value>". + +# Instrument "runtime" +instrument.runtime.class=com.google.caliper.runner.RuntimeInstrument + +# Do not report any measurements from before this minimum time has elapsed +instrument.runtime.options.warmup=10s +# Interrupt warmup when it has been running for this much wall-clock time, +# even if the measured warmup time (above) hasn't been reached. This prevents fast benchmarks +# with high per-measurement overhead (e.g. long @BeforeRep and @AfterRep methods) +# from taking too long to warm up. +instrument.runtime.options.maxWarmupWallTime=10m + +# Caliper chooses rep counts such that the total timing interval comes out near this value. +# Higher values take longer, but are more precise (less vulnerable to fixed costs) +instrument.runtime.options.timingInterval=500ms + +# Caliper ultimately records only the final N measurements, where N is this value. +instrument.runtime.options.measurements=9 + +# Run GC before every measurement? +instrument.runtime.options.gcBeforeEach=true + +# Whether or not to make suggestions about whether a benchmark should be a pico/micro/macro +# benchmark. Note that this will not effect errors that result from benchmarks that are unable to +# take proper measurements due to granularity issues. +instrument.runtime.options.suggestGranularity=true + +# Instrument "arbitrary" +instrument.arbitrary.class=com.google.caliper.runner.ArbitraryMeasurementInstrument + +# Run GC before every measurement? +instrument.arbitrary.options.gcBeforeEach=false + +# Instrument "allocation" +instrument.allocation.class=com.google.caliper.runner.AllocationInstrument + +# Track and log a summary of every individual allocation. This enables better error messages for +# buggy benchmarks and prints detailed reports of allocation behavior in verbose mode. N.B. This +# can increase the memory usage of the allocation worker significantly, so it is not recommended +# for benchmarks that do a lot of allocation. +instrument.allocation.options.trackAllocations=false + + +# Sets the maximum number of trials that can run in parallel. +runner.maxParallelism=2 + +###################### +# RESULTS PROCESSORS +###################### + +results.file.class=com.google.caliper.runner.OutputFileDumper + +results.upload.class=com.google.caliper.runner.HttpUploader + +results.upload.options.url=https://microbenchmarks.appspot.com/ diff --git a/caliper/src/test/java/com/google/caliper/CaliperTest.java b/caliper/src/test/java/com/google/caliper/CaliperTest.java deleted file mode 100644 index 18857ba..0000000 --- a/caliper/src/test/java/com/google/caliper/CaliperTest.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright (C) 2010 Google Inc. - * - * 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.google.caliper; - -import com.google.common.base.Supplier; -import java.util.Map; -import java.util.Set; -import junit.framework.TestCase; - -public final class CaliperTest extends TestCase { - - /** - * Test we detect and fail when benchmarks don't scale properly. - * @throws Exception - */ - public void testBenchmarkScalesNonLinearly() throws Exception { - TimeMeasurer timeMeasurer = new TimeMeasurer(1000, 1000); - try { - timeMeasurer.run(new NonLinearTimedRunnable()); - fail(); - } catch (UserException.DoesNotScaleLinearlyException e) { - } - } - - private static class NonLinearTimedRunnable extends ConfiguredBenchmark - implements Supplier<ConfiguredBenchmark> { - private NonLinearTimedRunnable() { - super(new NoOpBenchmark()); - } - - @Override public ConfiguredBenchmark get() { - return this; - } - - @Override public Object run(int reps) throws Exception { - return null; // broken! doesn't loop reps times. - } - - @Override public void close() throws Exception {} - } - - private static class NoOpBenchmark implements Benchmark { - @Override public Set<String> parameterNames() { - return null; - } - - @Override public Set<String> parameterValues(String parameterName) { - return null; - } - - @Override public ConfiguredBenchmark createBenchmark(Map<String, String> parameterValues) { - return null; - } - - @Override public Map<String, Integer> getTimeUnitNames() { - return null; - } - - @Override public Map<String, Integer> getInstanceUnitNames() { - return null; - } - - @Override public Map<String, Integer> getMemoryUnitNames() { - return null; - } - - @Override public double nanosToUnits(double nanos) { - return 0; - } - - @Override public double instancesToUnits(long instances) { - return 0; - } - - @Override public double bytesToUnits(long bytes) { - return 0; - } - } -} diff --git a/caliper/src/test/java/com/google/caliper/JsonTest.java b/caliper/src/test/java/com/google/caliper/JsonTest.java deleted file mode 100644 index cee02b0..0000000 --- a/caliper/src/test/java/com/google/caliper/JsonTest.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (C) 2011 Google Inc. - * - * 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.google.caliper; - -import com.google.common.collect.ImmutableMap; -import java.util.Date; -import java.util.Locale; -import java.util.Map; -import junit.framework.TestCase; - -public final class JsonTest extends TestCase { - - public void testJsonSerialization() { - Result original = newSampleResult(); - String json = Json.getGsonInstance().toJson(original, Result.class); - Result reserialized = Json.getGsonInstance().fromJson(json, Result.class); - assertEquals(original, reserialized); - } - - /** - * Caliper's JSON files used to include dates specific to the host machine's - * locale. http://code.google.com/p/caliper/issues/detail?id=113 - */ - public void testJsonSerializationWithFancyLocale() { - Result original = newSampleResult(); - - // serialize in one locale... - Locale defaultLocale = Locale.getDefault(); - Locale.setDefault(Locale.ITALY); - String json; - try { - json = Json.getGsonInstance().toJson(original, Result.class); - } finally { - Locale.setDefault(defaultLocale); - } - - // deserialize in another - Result reserialized = Json.getGsonInstance().fromJson(json, Result.class); - assertEquals(original, reserialized); - } - - private Result newSampleResult() { - Map<String,Integer> units = ImmutableMap.of("ns", 1); - MeasurementSet timeMeasurements = new MeasurementSet(new Measurement(units, 2.0, 2.0)); - Date executedDate = new Date(0); - Scenario scenario = new Scenario(ImmutableMap.of("benchmark", "Foo")); - ScenarioResult scenarioResult = new ScenarioResult( - timeMeasurements, "log", null, null, null, null); - Run run = new Run(ImmutableMap.of(scenario, scenarioResult), "foo.FooBenchmark", executedDate); - Environment environment = new Environment(ImmutableMap.of("os.name", "Linux")); - return new Result(run, environment); - } -} diff --git a/caliper/src/test/java/com/google/caliper/MeasurementSetTest.java b/caliper/src/test/java/com/google/caliper/MeasurementSetTest.java deleted file mode 100644 index f6f7cbb..0000000 --- a/caliper/src/test/java/com/google/caliper/MeasurementSetTest.java +++ /dev/null @@ -1,216 +0,0 @@ -/* - * Copyright (C) 2010 Google Inc. - * - * 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.google.caliper; - -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.Ordering; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import junit.framework.TestCase; - -public class MeasurementSetTest extends TestCase { - - Ordering<Measurement> MEASUREMENT_BY_NANOS = new Ordering<Measurement>() { - @Override public int compare(Measurement a, Measurement b) { - return Double.compare(a.getRaw(), b.getRaw()); - } - }; - - public void testIncompatibleMeasurements() { - Measurement[] measurements = new Measurement[2]; - measurements[0] = new Measurement(ImmutableMap.of("doublens", 1), 1.1, 2.2); - measurements[1] = new Measurement(ImmutableMap.of("triplens", 1), 3.8, 7.6); - try { - new MeasurementSet(measurements); - fail("illegal argument exception not thrown"); - } catch (IllegalArgumentException e) { - // success - } - } - - public void testIncompatibleAddedMeasurements() { - Measurement[] measurements = new Measurement[1]; - measurements[0] = new Measurement(ImmutableMap.of("doublens", 1), 1.1, 2.2); - MeasurementSet measurementSet = new MeasurementSet(measurements); - try { - measurementSet.plusMeasurement(new Measurement(ImmutableMap.of("triplens", 1), 3.8, 7.6)); - fail("illegal argument exception not thrown"); - } catch (IllegalArgumentException e) { - // success - } - } - - public void testSize() { - Measurement[] measurements = new Measurement[3]; - measurements[0] = new Measurement(ImmutableMap.of("doublens", 1), 1.1, 2.2); - measurements[1] = new Measurement(ImmutableMap.of("doublens", 1), 3.8, 7.6); - measurements[2] = new Measurement(ImmutableMap.of("doublens", 1), 2.3, 4.6); - MeasurementSet measurementSet = new MeasurementSet(measurements); - assertEquals(3, measurementSet.size()); - - Measurement[] measurements2 = new Measurement[4]; - measurements2[0] = new Measurement(ImmutableMap.of("doublens", 1), 1.1, 2.2); - measurements2[1] = new Measurement(ImmutableMap.of("doublens", 1), 3.8, 7.6); - measurements2[2] = new Measurement(ImmutableMap.of("doublens", 1), 2.3, 4.6); - measurements2[3] = new Measurement(ImmutableMap.of("doublens", 1), 7.2, 14.4); - MeasurementSet measurementSet2 = - new MeasurementSet(measurements2); - assertEquals(4, measurementSet2.size()); - } - - public void testPlusMeasurement() { - Measurement[] measurements = new Measurement[3]; - measurements[0] = new Measurement(ImmutableMap.of("doublens", 1), 1.1, 2.2); - measurements[1] = new Measurement(ImmutableMap.of("doublens", 1), 3.8, 7.6); - measurements[2] = new Measurement(ImmutableMap.of("doublens", 1), 2.3, 4.6); - MeasurementSet measurementSet = new MeasurementSet(measurements); - - Measurement[] measurements2 = new Measurement[4]; - measurements2[0] = new Measurement(ImmutableMap.of("doublens", 1), 1.1, 2.2); - measurements2[1] = new Measurement(ImmutableMap.of("doublens", 1), 3.8, 7.6); - measurements2[2] = new Measurement(ImmutableMap.of("doublens", 1), 2.3, 4.6); - measurements2[3] = new Measurement(ImmutableMap.of("doublens", 1), 7.2, 14.4); - MeasurementSet measurementSet2 = - new MeasurementSet(measurements2); - - MeasurementSet measurementSet3 = measurementSet.plusMeasurement(measurements2[3]); - - assertDoubleListsEquals(measurementSet2.getMeasurementsRaw(), - measurementSet3.getMeasurementsRaw(), 0.0000001); - assertDoubleListsEquals(measurementSet2.getMeasurementUnits(), - measurementSet3.getMeasurementUnits(), 0.0000001); - assertEquals(measurementSet2.getUnitNames(), measurementSet3.getUnitNames()); - - List<Measurement> measurementList1 = - MEASUREMENT_BY_NANOS.sortedCopy(measurementSet2.getMeasurements()); - List<Measurement> measurementList2 = - MEASUREMENT_BY_NANOS.sortedCopy(measurementSet3.getMeasurements()); - assertEquals(measurementList1.size(), measurementList2.size()); - for (int i = 0; i < measurementList1.size(); i++) { - assertEquals(measurementList1.get(i).getRaw(), - measurementList2.get(i).getRaw()); - assertEquals(measurementList1.get(i).getProcessed(), - measurementList2.get(i).getProcessed()); - } - } - - public void testMedian() { - Measurement[] measurements = new Measurement[3]; - measurements[0] = new Measurement(ImmutableMap.of("doublens", 1), 1.1, 2.2); - measurements[1] = new Measurement(ImmutableMap.of("doublens", 1), 3.8, 7.6); - measurements[2] = new Measurement(ImmutableMap.of("doublens", 1), 2.3, 4.6); - MeasurementSet measurementSet = new MeasurementSet(measurements); - assertEquals(2.3, measurementSet.medianRaw(), 0.00000001); - assertEquals(4.6, measurementSet.medianUnits(), 0.00000001); - - Measurement[] measurements2 = new Measurement[4]; - measurements2[0] = new Measurement(ImmutableMap.of("doublens", 1), 1.1, 2.2); - measurements2[1] = new Measurement(ImmutableMap.of("doublens", 1), 3.8, 7.6); - measurements2[2] = new Measurement(ImmutableMap.of("doublens", 1), 2.3, 4.6); - measurements2[3] = new Measurement(ImmutableMap.of("doublens", 1), 7.2, 14.4); - MeasurementSet measurementSet2 = - new MeasurementSet(measurements2); - assertEquals((2.3 + 3.8) / 2, measurementSet2.medianRaw(), 0.00000001); - assertEquals((4.6 + 7.6) / 2, measurementSet2.medianUnits(), 0.00000001); - } - - public void testMean() { - Measurement[] measurements = new Measurement[3]; - measurements[0] = new Measurement(ImmutableMap.of("doublens", 1), 1.1, 2.2); - measurements[1] = new Measurement(ImmutableMap.of("doublens", 1), 3.8, 7.6); - measurements[2] = new Measurement(ImmutableMap.of("doublens", 1), 2.3, 4.6); - MeasurementSet measurementSet = new MeasurementSet(measurements); - assertEquals((1.1 + 3.8 + 2.3) / 3, measurementSet.meanRaw(), 0.00000001); - assertEquals((2.2 + 7.6 + 4.6) / 3, measurementSet.meanUnits(), 0.00000001); - - Measurement[] measurements2 = new Measurement[4]; - measurements2[0] = new Measurement(ImmutableMap.of("doublens", 1), 1.1, 2.2); - measurements2[1] = new Measurement(ImmutableMap.of("doublens", 1), 3.8, 7.6); - measurements2[2] = new Measurement(ImmutableMap.of("doublens", 1), 2.3, 4.6); - measurements2[3] = new Measurement(ImmutableMap.of("doublens", 1), 7.2, 14.4); - MeasurementSet measurementSet2 = - new MeasurementSet(measurements2); - assertEquals((1.1 + 2.3 + 3.8 + 7.2) / 4, measurementSet2.meanRaw(), 0.00000001); - assertEquals((2.2 + 4.6 + 7.6 + 14.4) / 4, measurementSet2.meanUnits(), 0.00000001); - } - - public void testStandardDeviation() { - Measurement[] measurements = new Measurement[3]; - measurements[0] = new Measurement(ImmutableMap.of("doublens", 1), 1.1, 2.2); - measurements[1] = new Measurement(ImmutableMap.of("doublens", 1), 3.8, 7.6); - measurements[2] = new Measurement(ImmutableMap.of("doublens", 1), 2.3, 4.6); - MeasurementSet measurementSet = new MeasurementSet(measurements); - assertEquals(1.35277, measurementSet.standardDeviationRaw(), 0.00001); - assertEquals(2.70555, measurementSet.standardDeviationUnits(), 0.00001); - } - - public void testMax() { - Measurement[] measurements = new Measurement[3]; - measurements[0] = new Measurement(ImmutableMap.of("doublens", 1), 1.1, 2.2); - measurements[1] = new Measurement(ImmutableMap.of("doublens", 1), 3.8, 7.6); - measurements[2] = new Measurement(ImmutableMap.of("doublens", 1), 2.3, 4.6); - MeasurementSet measurementSet = new MeasurementSet(measurements); - assertEquals(3.8, measurementSet.maxRaw(), 0.00000001); - assertEquals(7.6, measurementSet.maxUnits(), 0.00000001); - } - - public void testMin() { - Measurement[] measurements = new Measurement[3]; - measurements[0] = new Measurement(ImmutableMap.of("doublens", 1), 1.1, 2.2); - measurements[1] = new Measurement(ImmutableMap.of("doublens", 1), 3.8, 7.6); - measurements[2] = new Measurement(ImmutableMap.of("doublens", 1), 2.3, 4.6); - MeasurementSet measurementSet = new MeasurementSet(measurements); - assertEquals(1.1, measurementSet.minRaw(), 0.00000001); - assertEquals(2.2, measurementSet.minUnits(), 0.00000001); - } - - public void testJsonRoundtrip() { - Measurement[] measurements = new Measurement[3]; - measurements[0] = new Measurement(ImmutableMap.of("doublens", 1), 1.1, 2.2); - measurements[1] = new Measurement(ImmutableMap.of("doublens", 1), 3.8, 7.6); - measurements[2] = new Measurement(ImmutableMap.of("doublens", 1), 2.3, 4.6); - MeasurementSet measurementSet = new MeasurementSet(measurements); - MeasurementSet roundTripped = - Json.measurementSetFromJson(Json.measurementSetToJson(measurementSet)); - assertDoubleListsEquals(measurementSet.getMeasurementsRaw(), - roundTripped.getMeasurementsRaw(), 0.00000001); - assertDoubleListsEquals(measurementSet.getMeasurementUnits(), - roundTripped.getMeasurementUnits(), 0.00000001); - assertEquals(measurementSet.getUnitNames(), roundTripped.getUnitNames()); - } - - @SuppressWarnings({"AssertEqualsBetweenInconvertibleTypes"}) - public void testFromLegacyString() { - MeasurementSet measurementSet = Json.measurementSetFromJson("122.0 133.0 144.0"); - assertDoubleListsEquals(Arrays.asList(122.0, 133.0, 144.0), - measurementSet.getMeasurementsRaw(), 0.00000001); - assertDoubleListsEquals(Arrays.asList(122.0, 133.0, 144.0), - measurementSet.getMeasurementUnits(), 0.00000001); - assertEquals(ImmutableMap.of("ns", 1, "us", 1000, "ms", 1000000, "s", 1000000000), - measurementSet.getUnitNames()); - } - - private void assertDoubleListsEquals(List<Double> expected, List<Double> actual, double epsilon) { - assertEquals(expected.size(), actual.size()); - Collections.sort(expected); - Collections.sort(actual); - for (int i = 0; i < expected.size(); i++) { - assertEquals(expected.get(i), actual.get(i), epsilon); - } - } -} diff --git a/caliper/src/test/java/com/google/caliper/ParameterTest.java b/caliper/src/test/java/com/google/caliper/ParameterTest.java deleted file mode 100644 index 9063261..0000000 --- a/caliper/src/test/java/com/google/caliper/ParameterTest.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (C) 2010 Google Inc. - * - * 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.google.caliper; - -import com.google.common.collect.ImmutableList; -import com.google.common.collect.Sets; - -import junit.framework.TestCase; - -import java.util.Arrays; -import java.util.Collection; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; - -public class ParameterTest extends TestCase { - - public static class A extends SimpleBenchmark { - @Param({"value1", "value2"}) String param; - } - - public void testFromAnnotation() throws Exception { - Map<String,Parameter<?>> map = Parameter.forClass(A.class); - Parameter<?> p = map.get("param"); - assertEquals("param", p.getName()); - assertEquals(String.class, p.getType()); - - checkParameterValues(A.class, "value1", "value2"); - } - - public enum Foo { VALUE1, VALUE2 } - - public static class H extends SimpleBenchmark { - @Param Foo param; - } - - public void testAllEnums() throws Exception { - checkParameterValues(H.class, Foo.VALUE1, Foo.VALUE2); - } - - public static class I extends SimpleBenchmark { - @Param boolean param; - } - - public void testBoolean() throws Exception { - checkParameterValues(I.class, true, false); - } - - private static void checkParameterValues(Class<? extends SimpleBenchmark> bClass, - Object... expected) throws Exception { - Map<String,Parameter<?>> map = Parameter.forClass(bClass); - assertEquals(1, map.size()); - Parameter<?> p = map.get("param"); - List<Object> values = ImmutableList.copyOf(p.values()); - assertEquals(Arrays.asList(expected), values); - } -} diff --git a/caliper/src/test/java/com/google/caliper/WarmupOverflowTest.java b/caliper/src/test/java/com/google/caliper/WarmupOverflowTest.java deleted file mode 100644 index 85e59af..0000000 --- a/caliper/src/test/java/com/google/caliper/WarmupOverflowTest.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright (C) 2010 Google Inc. - * - * 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.google.caliper; - -import com.google.caliper.UserException.DoesNotScaleLinearlyException; -import com.google.common.util.concurrent.SimpleTimeLimiter; -import com.google.common.util.concurrent.TimeLimiter; -import java.util.concurrent.Callable; -import java.util.concurrent.Executors; -import java.util.concurrent.TimeUnit; -import junit.framework.TestCase; - -/** - * Test exposing an issue where any warmup that completes enough executions to reach - * Integer.MAX_VALUE reps (either because the benchmark code is optimized away or because the - * warmupMillis are long enough compared to the benchmark execution time). - */ -public class WarmupOverflowTest extends TestCase { - private TimeLimiter timeLimiter; - - @Override public void setUp() { - timeLimiter = new SimpleTimeLimiter(Executors.newSingleThreadExecutor()); - } - - public void testOptimizedAwayBenchmarkDoesNotTakeTooLongToRun() throws Exception { - try { - timeLimiter.callWithTimeout(new Callable<Void>() { - @Override public Void call() throws Exception { - InProcessRunner runner = new InProcessRunner(); - runner.run(OptimizedAwayBenchmark.class.getName(), "--warmupMillis", "3000", - "--measurementType", "TIME"); - return null; - } - }, 90, TimeUnit.SECONDS, false); - } catch (DoesNotScaleLinearlyException expected) { - } - } - - public void testLongWarmupMillisDoesNotTakeTooLongToRun() throws Exception { - timeLimiter.callWithTimeout(new Callable<Void>() { - @Override public Void call() throws Exception { - InProcessRunner runner = new InProcessRunner(); - runner.run(RelativelyFastBenchmark.class.getName(), "--warmupMillis", "8000", - "--runMillis", "51", "--measurementType", "TIME"); - return null; - } - }, 90, TimeUnit.SECONDS, false); - } - - public static class OptimizedAwayBenchmark extends SimpleBenchmark { - public void timeIsNullOrEmpty(int reps) { - for (int i = 0; i < reps; i++) { - // do nothing! - } - } - } - - public static class RelativelyFastBenchmark extends SimpleBenchmark { - public long timeSqrt(int reps) { - long result = 0; - for(int i = 0; i < reps; i++) { - result += Math.sqrt(81); - } - return result; - } - } -} diff --git a/caliper/src/test/java/com/google/caliper/bridge/GcLogMessageGenerator.java b/caliper/src/test/java/com/google/caliper/bridge/GcLogMessageGenerator.java new file mode 100644 index 0000000..292bc94 --- /dev/null +++ b/caliper/src/test/java/com/google/caliper/bridge/GcLogMessageGenerator.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2013 Google Inc. + * + * 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.google.caliper.bridge; + +import com.sun.management.HotSpotDiagnosticMXBean; + +import java.io.IOException; +import java.lang.management.ManagementFactory; + +import javax.management.MBeanServer; + +/** + * A simple class that invokes {@link System#gc()} over and over to generate some GC log messages. + */ +public final class GcLogMessageGenerator { + public static void main(String[] args) throws IOException { + checkGcLogging(); + for (int i = 0; i < 100; i++) { + System.gc(); + } + } + + private static final String HOTSPOT_BEAN_NAME = "com.sun.management:type=HotSpotDiagnostic"; + + private static void checkGcLogging() throws IOException { + MBeanServer server = ManagementFactory.getPlatformMBeanServer(); + HotSpotDiagnosticMXBean bean = ManagementFactory.newPlatformMXBeanProxy( + server, HOTSPOT_BEAN_NAME, HotSpotDiagnosticMXBean.class); + if (!bean.getVMOption("PrintGC").getValue().equals(Boolean.TRUE.toString())) { + System.err.println("This is only useful if you run with -XX:+PrintGC"); + System.exit(1); + } + } +} diff --git a/caliper/src/test/java/com/google/caliper/bridge/LogMessageParserTest.java b/caliper/src/test/java/com/google/caliper/bridge/LogMessageParserTest.java new file mode 100644 index 0000000..46370d5 --- /dev/null +++ b/caliper/src/test/java/com/google/caliper/bridge/LogMessageParserTest.java @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2013 Google Inc. + * + * 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.google.caliper.bridge; + +import static com.google.caliper.bridge.GcLogMessage.Type.FULL; +import static com.google.caliper.bridge.GcLogMessage.Type.INCREMENTAL; +import static com.google.common.base.Charsets.UTF_8; +import static java.util.concurrent.TimeUnit.MICROSECONDS; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import com.google.caliper.util.ShortDuration; +import com.google.common.io.Resources; + +import dagger.Component; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +import java.util.List; + +import javax.inject.Inject; + +/** + * Tests {@link LogMessageParser}. + */ +@RunWith(JUnit4.class) + +public class LogMessageParserTest { + @Inject LogMessageParser parser; + + @Component(modules = BridgeModule.class) + interface LogMessageParserComponent { + void inject(LogMessageParserTest test); + } + + @Before public void setUp() { + DaggerLogMessageParserTest_LogMessageParserComponent.create().inject(this); + } + + @Test public void gcPatten_jdk6() throws Exception { + List<String> lines = Resources.readLines( + Resources.getResource(LogMessageParserTest.class, "jdk6-gc.txt"), UTF_8); + for (String line : lines) { + assertTrue(parser.parse(line) instanceof GcLogMessage); + } + } + + @Test public void gcPatten_jdk7() throws Exception { + List<String> lines = Resources.readLines( + Resources.getResource(LogMessageParserTest.class, "jdk7-gc.txt"), UTF_8); + for (String line : lines) { + assertTrue(parser.parse(line) instanceof GcLogMessage); + } + } + + @Test public void gcMessageData() { + assertEquals(new GcLogMessage(INCREMENTAL, ShortDuration.of(1232, MICROSECONDS)), + parser.parse("[GC 987K->384K(62848K), 0.0012320 secs]")); + assertEquals(new GcLogMessage(FULL, ShortDuration.of(5455, MICROSECONDS)), + parser.parse("[Full GC 384K->288K(62848K), 0.0054550 secs]")); + assertEquals(new GcLogMessage(INCREMENTAL, ShortDuration.of(1424, MICROSECONDS)), + parser.parse( + "2013-02-11T20:15:26.706-0600: 0.098: [GC 1316K->576K(62848K), 0.0014240 secs]")); + assertEquals(new GcLogMessage(FULL, ShortDuration.of(4486, MICROSECONDS)), + parser.parse( + "2013-02-11T20:15:26.708-0600: 0.099: [Full GC 576K->486K(62848K), 0.0044860 secs]")); + } + + @Test public void jitPattern_jdk6() throws Exception { + List<String> lines = Resources.readLines( + Resources.getResource(LogMessageParserTest.class, "jdk6-compilation.txt"), UTF_8); + for (String line : lines) { + assertTrue(parser.parse(line) instanceof HotspotLogMessage); + } + } + + @Test public void jitPattern_jdk7() throws Exception { + List<String> lines = Resources.readLines( + Resources.getResource(LogMessageParserTest.class, "jdk7-compilation.txt"), UTF_8); + for (String line : lines) { + assertTrue(parser.parse(line) instanceof HotspotLogMessage); + } + } + + @Test public void vmOptionPattern_jdk6() throws Exception { + List<String> lines = Resources.readLines( + Resources.getResource(LogMessageParserTest.class, "jdk6-flags.txt"), UTF_8); + for (String line : lines) { + assertTrue(parser.parse(line) instanceof VmOptionLogMessage); + } + } + + @Test public void vmOptionPattern_jdk7() throws Exception { + List<String> lines = Resources.readLines( + Resources.getResource(LogMessageParserTest.class, "jdk7-flags.txt"), UTF_8); + for (String line : lines) { + assertTrue(parser.parse(line) instanceof VmOptionLogMessage); + } + } +} diff --git a/caliper/src/test/java/com/google/caliper/config/CaliperConfigLoaderTest.java b/caliper/src/test/java/com/google/caliper/config/CaliperConfigLoaderTest.java new file mode 100644 index 0000000..317d1e5 --- /dev/null +++ b/caliper/src/test/java/com/google/caliper/config/CaliperConfigLoaderTest.java @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2012 Google Inc. + * + * 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.google.caliper.config; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.when; + +import com.google.caliper.options.CaliperOptions; +import com.google.common.collect.ImmutableMap; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.Properties; + +/** + * Tests {@link CaliperConfigLoader}. + */ +@RunWith(MockitoJUnitRunner.class) + +public class CaliperConfigLoaderTest { + @Mock CaliperOptions optionsMock; + + private File tempConfigFile; + + @Before public void createTempUserProperties() throws IOException { + tempConfigFile = File.createTempFile("caliper-config-test", "properties"); + tempConfigFile.deleteOnExit(); + Properties userProperties = new Properties(); + userProperties.put("some.property", "franklin"); + FileOutputStream fs = new FileOutputStream(tempConfigFile); + userProperties.store(fs, null); + fs.close(); + } + + @After public void deleteTempUserProperties() { + tempConfigFile.delete(); + } + + @Test public void loadOrCreate_configFileExistsNoOverride() throws Exception { + when(optionsMock.caliperConfigFile()).thenReturn(tempConfigFile); + when(optionsMock.configProperties()).thenReturn(ImmutableMap.<String, String>of()); + CaliperConfigLoader loader = new CaliperConfigLoader(optionsMock); + CaliperConfig config = loader.loadOrCreate(); + assertEquals("franklin", config.properties.get("some.property")); + } + + @Test public void loadOrCreate_configFileExistsWithOverride() throws Exception { + when(optionsMock.caliperConfigFile()).thenReturn(tempConfigFile); + when(optionsMock.configProperties()).thenReturn(ImmutableMap.of( + "some.property", "tacos")); + CaliperConfigLoader loader = new CaliperConfigLoader(optionsMock); + CaliperConfig config = loader.loadOrCreate(); + assertEquals("tacos", config.properties.get("some.property")); + } +} diff --git a/caliper/src/test/java/com/google/caliper/config/CaliperConfigTest.java b/caliper/src/test/java/com/google/caliper/config/CaliperConfigTest.java new file mode 100644 index 0000000..05bbe32 --- /dev/null +++ b/caliper/src/test/java/com/google/caliper/config/CaliperConfigTest.java @@ -0,0 +1,178 @@ +/* + * Copyright (C) 2012 Google Inc. + * + * 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.google.caliper.config; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +import com.google.caliper.api.ResultProcessor; +import com.google.caliper.model.Trial; +import com.google.caliper.platform.Platform; +import com.google.caliper.platform.jvm.JvmPlatform; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +import java.io.File; +import java.lang.management.ManagementFactory; + +/** + * Tests {@link CaliperConfig}. + * + * @author gak@google.com (Gregory Kick) + */ +@RunWith(JUnit4.class) +public class CaliperConfigTest { + @Rule public TemporaryFolder folder = new TemporaryFolder(); + + private Platform platform = new JvmPlatform(); + + @Test public void getDefaultVmConfig() throws Exception { + CaliperConfig configuration = new CaliperConfig( + ImmutableMap.of("vm.args", "-very -special=args")); + VmConfig defaultVmConfig = configuration.getDefaultVmConfig(platform); + assertEquals(new File(System.getProperty("java.home")), defaultVmConfig.vmHome()); + ImmutableList<String> expectedArgs = new ImmutableList.Builder<String>() + .addAll(ManagementFactory.getRuntimeMXBean().getInputArguments()) + .add("-very") + .add("-special=args") + .build(); + assertEquals(expectedArgs, defaultVmConfig.options()); + } + + @Test public void getVmConfig_baseDirectoryAndName() throws Exception { + File tempBaseDir = folder.newFolder(); + File jdkHome = new File(tempBaseDir, "test"); + jdkHome.mkdir(); + CaliperConfig configuration = new CaliperConfig(ImmutableMap.of( + "vm.baseDirectory", tempBaseDir.getAbsolutePath())); + assertEquals(new VmConfig.Builder(platform, jdkHome).build(), + configuration.getVmConfig(platform, "test")); + } + + @Test public void getVmConfig_baseDirectoryAndHome() throws Exception { + File tempBaseDir = folder.newFolder(); + File jdkHome = new File(tempBaseDir, "test-home"); + jdkHome.mkdir(); + CaliperConfig configuration = new CaliperConfig(ImmutableMap.of( + "vm.baseDirectory", tempBaseDir.getAbsolutePath(), + "vm.test.home", "test-home")); + assertEquals(new VmConfig.Builder(platform, jdkHome).build(), + configuration.getVmConfig(platform, "test")); + } + + @Test public void getVmConfig() throws Exception { + File jdkHome = folder.newFolder(); + CaliperConfig configuration = new CaliperConfig(ImmutableMap.of( + "vm.args", "-a -b -c", + "vm.test.home", jdkHome.getAbsolutePath(), + "vm.test.args", " -d -e ")); + assertEquals( + new VmConfig.Builder(platform, jdkHome) + .addOption("-a") + .addOption("-b") + .addOption("-c") + .addOption("-d") + .addOption("-e") + .build(), + configuration.getVmConfig(platform, "test")); + } + + @Test public void getVmConfig_escapedSpacesInArgs() throws Exception { + File jdkHome = folder.newFolder(); + CaliperConfig configuration = new CaliperConfig(ImmutableMap.of( + "vm.args", "-a=string\\ with\\ spa\\ces -b -c", + "vm.test.home", jdkHome.getAbsolutePath())); + assertEquals( + new VmConfig.Builder(platform, jdkHome) + .addOption("-a=string with spaces") + .addOption("-b") + .addOption("-c") + .build(), + configuration.getVmConfig(platform, "test")); + } + + @Test public void getInstrumentConfig() throws Exception { + CaliperConfig configuration = new CaliperConfig(ImmutableMap.of( + "instrument.test.class", "test.ClassName", + "instrument.test.options.a", "1", + "instrument.test.options.b", "excited b b excited")); + assertEquals( + new InstrumentConfig.Builder() + .className("test.ClassName") + .addOption("a", "1") + .addOption("b", "excited b b excited") + .build(), + configuration.getInstrumentConfig("test")); + } + + @Test public void getInstrumentConfig_notConfigured() throws Exception { + CaliperConfig configuration = new CaliperConfig(ImmutableMap.of( + "instrument.test.options.a", "1", + "instrument.test.options.b", "excited b b excited")); + try { + configuration.getInstrumentConfig("test"); + fail(); + } catch (IllegalArgumentException expected) {} + } + + @Test public void getConfiguredInstruments() throws Exception { + CaliperConfig configuration = new CaliperConfig(ImmutableMap.of( + "instrument.test.class", "test.ClassName", + "instrument.test2.class", "test.ClassName", + "instrument.test3.options.a", "1", + "instrument.test4.class", "test.ClassName", + "instrument.test4.options.b", "excited b b excited")); + assertEquals(ImmutableSet.of("test", "test2", "test4"), + configuration.getConfiguredInstruments()); + } + + @Test public void getConfiguredResultProcessors() throws Exception { + assertEquals(ImmutableSet.of(), + new CaliperConfig(ImmutableMap.<String, String>of()).getConfiguredResultProcessors()); + CaliperConfig configuration = new CaliperConfig(ImmutableMap.of( + "results.test.class", TestResultProcessor.class.getName())); + assertEquals(ImmutableSet.of(TestResultProcessor.class), + configuration.getConfiguredResultProcessors()); + } + + @Test public void getResultProcessorConfig() throws Exception { + CaliperConfig configuration = new CaliperConfig(ImmutableMap.of( + "results.test.class", TestResultProcessor.class.getName(), + "results.test.options.g", "ak", + "results.test.options.c", "aliper")); + assertEquals( + new ResultProcessorConfig.Builder() + .className(TestResultProcessor.class.getName()) + .addOption("g", "ak") + .addOption("c", "aliper") + .build(), + configuration.getResultProcessorConfig(TestResultProcessor.class)); + } + + private static final class TestResultProcessor implements ResultProcessor { + @Override public void close() {} + + @Override public void processTrial(Trial trial) {} + } +} diff --git a/caliper/src/test/java/com/google/caliper/config/LoggingConfigLoaderTest.java b/caliper/src/test/java/com/google/caliper/config/LoggingConfigLoaderTest.java new file mode 100644 index 0000000..e4320ef --- /dev/null +++ b/caliper/src/test/java/com/google/caliper/config/LoggingConfigLoaderTest.java @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2013 Google Inc. + * + * 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.google.caliper.config; + +import static com.google.common.base.Charsets.UTF_8; +import static java.util.logging.Level.INFO; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import com.google.caliper.model.Run; +import com.google.common.io.Files; + +import org.joda.time.Instant; +import org.joda.time.format.ISODateTimeFormat; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; + +import java.io.File; +import java.io.IOException; +import java.util.UUID; +import java.util.logging.FileHandler; +import java.util.logging.Handler; +import java.util.logging.LogManager; +import java.util.logging.LogRecord; +import java.util.logging.Logger; +import java.util.logging.SimpleFormatter; + +/** + * Tests {@link LoggingConfigLoader}. + */ + +@RunWith(MockitoJUnitRunner.class) +public class LoggingConfigLoaderTest { + @Rule public TemporaryFolder folder = new TemporaryFolder(); + + @Mock LogManager logManager; + @Mock Logger logger; + @Captor ArgumentCaptor<Handler> handlerCaptor; + + private LoggingConfigLoader loader; + private UUID runId = UUID.randomUUID(); + private Instant startTime = new Instant(); + private File caliperDirectory; + + @Before public void setUp() throws IOException { + this.caliperDirectory = folder.newFolder(); + this.loader = new LoggingConfigLoader(caliperDirectory, logManager, new Run.Builder(runId) + .label("fake run") + .startTime(startTime) + .build()); + } + + @Test public void testLoadDefaultLogConfiguration() + throws SecurityException, IOException { + when(logManager.getLogger("")).thenReturn(logger); + loader.maybeLoadDefaultLogConfiguration(logManager); + verify(logManager).reset(); + verify(logger).addHandler(handlerCaptor.capture()); + FileHandler fileHandler = (FileHandler) handlerCaptor.getValue(); + assertEquals(UTF_8.name(), fileHandler.getEncoding()); + assertTrue(fileHandler.getFormatter() instanceof SimpleFormatter); + fileHandler.publish(new LogRecord(INFO, "some message")); + File logFile = new File(new File(caliperDirectory, "log"), + ISODateTimeFormat.basicDateTimeNoMillis().print(startTime) + "." + runId + ".log"); + assertTrue(logFile.isFile()); + assertTrue(Files.toString(logFile, UTF_8).contains("some message")); + } +} diff --git a/caliper/src/test/java/com/google/caliper/config/VmConfigTest.java b/caliper/src/test/java/com/google/caliper/config/VmConfigTest.java new file mode 100644 index 0000000..7998866 --- /dev/null +++ b/caliper/src/test/java/com/google/caliper/config/VmConfigTest.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2012 Google Inc. + * + * 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.google.caliper.config; + +import static org.junit.Assert.assertTrue; + +import com.google.caliper.platform.jvm.JvmPlatform; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +import java.io.File; + +/** + * Tests {@link VmConfig}. + * + * @author gak@google.com (Gregory Kick) + */ +@RunWith(JUnit4.class) +public class VmConfigTest { + + + @Test + public void testExecutable() { + File javaExecutable = + new VmConfig.Builder(new JvmPlatform(), new File(System.getProperty("java.home"))) + .build() + .vmExecutable(); + assertTrue("Could not find: " + javaExecutable, javaExecutable.exists()); + assertTrue(javaExecutable + " is not a file", javaExecutable.isFile()); + } +} diff --git a/caliper/src/test/java/com/google/caliper/memory/ObjectGraphMeasurerTest.java b/caliper/src/test/java/com/google/caliper/memory/ObjectGraphMeasurerTest.java new file mode 100644 index 0000000..5888f9c --- /dev/null +++ b/caliper/src/test/java/com/google/caliper/memory/ObjectGraphMeasurerTest.java @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2011 Google Inc. + * + * 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.google.caliper.memory; + +import com.google.caliper.memory.ObjectGraphMeasurer.Footprint; +import com.google.common.collect.ImmutableMultiset; + +import junit.framework.TestCase; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** + * Tests for ObjectGraphMeasurer. + */ +@RunWith(JUnit4.class) +public class ObjectGraphMeasurerTest extends TestCase { + enum DummyEnum { + VALUE; + } + static final Object oneEnumField = new Object() { + @SuppressWarnings("unused") DummyEnum enumField = DummyEnum.VALUE; + }; + + // enums are treated as statics (and ignored) + @Test public void testEnum() { + ObjectGraphMeasurer.Footprint footprint = ObjectGraphMeasurer.measure(oneEnumField); + assertEquals(new Footprint(1, 1, 0, NO_PRIMITIVES), footprint); + } + + static final Object oneClassField = new Object() { + @SuppressWarnings("unused") Class<?> clazz = Object.class; + }; + + // Class instances are treated as statics (and ignored) + @Test public void testClass() { + ObjectGraphMeasurer.Footprint footprint = ObjectGraphMeasurer.measure(oneClassField); + assertEquals(new ObjectGraphMeasurer.Footprint(1, 1, 0, NO_PRIMITIVES), footprint); + } + + static final Object oneObjectField = new Object() { + @SuppressWarnings("unused") Object objectField = new Object(); + }; + + @Test public void testObject() { + ObjectGraphMeasurer.Footprint footprint = ObjectGraphMeasurer.measure(oneObjectField); + assertEquals(new ObjectGraphMeasurer.Footprint(2, 1, 0, NO_PRIMITIVES), footprint); + } + + static final Object withCycle = new Object() { + Object[] array = new Object[1]; + { + array[0] = this; + } + }; + + @Test public void testCycle() { + ObjectGraphMeasurer.Footprint footprint = ObjectGraphMeasurer.measure(withCycle); + assertEquals(new ObjectGraphMeasurer.Footprint(2, 2, 0, NO_PRIMITIVES), footprint); + } + + static final Object multiplePathsToObject = new Object() { + Object object = new Object(); + @SuppressWarnings("unused") Object ref1 = object; + @SuppressWarnings("unused") Object ref2 = object; + }; + + @Test public void testMultiplePathsToObject() { + ObjectGraphMeasurer.Footprint footprint = ObjectGraphMeasurer.measure(multiplePathsToObject); + assertEquals(new ObjectGraphMeasurer.Footprint(2, 3, 0, NO_PRIMITIVES), footprint); + } + + static final Object multiplePathsToClass = new Object() { + Object object = Object.class; + @SuppressWarnings("unused") Object ref1 = object; + @SuppressWarnings("unused") Object ref2 = object; + }; + + @Test public void testMultiplePathsToClass() { + ObjectGraphMeasurer.Footprint footprint = ObjectGraphMeasurer.measure(multiplePathsToClass); + assertEquals(new ObjectGraphMeasurer.Footprint(1, 3, 0, NO_PRIMITIVES), footprint); + } + + static class WithStaticField { + static WithStaticField INSTANCE = new WithStaticField(); + } + + @Test public void testStaticFields() { + ObjectGraphMeasurer.Footprint footprint = ObjectGraphMeasurer.measure(new WithStaticField()); + assertEquals(new ObjectGraphMeasurer.Footprint(1, 0, 0, NO_PRIMITIVES), footprint); + } + + @SuppressWarnings("unused") // unused test fields + static final Object oneNullOneNonNull = new Object() { + Object nonNull1 = new Object(); + Object nonNull2 = nonNull1; + Object null1 = null; + Object null2 = null; + Object null3 = null; + }; + + @Test public void testNullField() { + ObjectGraphMeasurer.Footprint footprint = ObjectGraphMeasurer.measure(oneNullOneNonNull); + assertEquals(new ObjectGraphMeasurer.Footprint(2, 2, 3, NO_PRIMITIVES), footprint); + } + + private static final ImmutableMultiset<Class<?>> NO_PRIMITIVES = ImmutableMultiset.of(); +} diff --git a/caliper/src/test/java/com/google/caliper/options/ParsedOptionsTest.java b/caliper/src/test/java/com/google/caliper/options/ParsedOptionsTest.java new file mode 100644 index 0000000..46d678a --- /dev/null +++ b/caliper/src/test/java/com/google/caliper/options/ParsedOptionsTest.java @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2011 Google Inc. + * + * 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.google.caliper.options; + +import static java.util.concurrent.TimeUnit.SECONDS; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import com.google.caliper.util.DisplayUsageException; +import com.google.caliper.util.InvalidCommandException; +import com.google.caliper.util.ShortDuration; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.ImmutableSetMultimap; +import com.google.common.collect.Iterables; +import com.google.common.io.Files; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +import java.io.File; +import java.io.IOException; + +@RunWith(JUnit4.class) + +public class ParsedOptionsTest { + private File tempDir; + + @Before public void setUp() throws IOException { + tempDir = Files.createTempDir(); + makeTestVmTree(tempDir); + } + + @After public void tearDown() throws IOException { + if (tempDir != null) { + Runtime.getRuntime().exec(new String[] {"rm", "-rf", tempDir.getCanonicalPath()}); + } + } + + private static void makeTestVmTree(File baseDir) throws IOException { + File bin = new File(baseDir, "testVm/bin"); + bin.mkdirs(); + File java = new File(bin, "java"); + Files.touch(java); + } + + @Test public void testNoOptions_RequireBenchmarkClassName() { + try { + ParsedOptions.from(new String[] {}, true); + fail(); + } catch (InvalidCommandException expected) { + assertEquals("No benchmark class specified", expected.getMessage()); + } + } + + @Test public void testTooManyArguments_RequireBenchmarkClassName() { + try { + ParsedOptions.from(new String[] {"a", "b"}, true); + fail(); + } catch (InvalidCommandException expected) { + assertEquals("Extra stuff, expected only class name: [a, b]", expected.getMessage()); + } + } + + @Test public void testTooManyArguments_DoNotRequireBenchmarkClassName() { + try { + ParsedOptions.from(new String[] {"a", "b"}, false); + fail(); + } catch (InvalidCommandException expected) { + assertEquals("Extra stuff, did not expect non-option arguments: [a, b]", + expected.getMessage()); + } + } + + @Test public void testHelp() throws InvalidCommandException { + try { + ParsedOptions.from(new String[] {"--help"}, true); + fail(); + } catch (DisplayUsageException expected) { + } + } + + @Test public void testDefaults_RequireBenchmarkClassName() throws InvalidCommandException { + CaliperOptions options = ParsedOptions.from(new String[] {CLASS_NAME}, true); + + assertEquals(CLASS_NAME, options.benchmarkClassName()); + checkDefaults(options); + } + + @Test public void testDefaults_DoNotRequireBenchmarkClassName() throws InvalidCommandException { + CaliperOptions options = ParsedOptions.from(new String[] {}, false); + + assertNull(options.benchmarkClassName()); + checkDefaults(options); + } + + private void checkDefaults(CaliperOptions options) { + assertTrue(options.benchmarkMethodNames().isEmpty()); + assertFalse(options.dryRun()); + ImmutableSet<String> expectedInstruments = new ImmutableSet.Builder<String>() + .add("allocation") + .add("runtime") + .build(); + assertEquals(expectedInstruments, options.instrumentNames()); + assertEquals(1, options.trialsPerScenario()); + assertTrue(options.userParameters().isEmpty()); + assertFalse(options.printConfiguration()); + assertTrue(options.vmArguments().isEmpty()); + assertEquals(0, options.vmNames().size()); + } + + @Test public void testKitchenSink() throws InvalidCommandException { + String[] args = { + "--benchmark=foo;bar;qux", + "--instrument=testInstrument", + "--directory=/path/to/some/dir", + "--trials=2", + "--time-limit=15s", + "-Dx=a;b;c", + "-Dy=b;d", + "-Csome.property=value", + "-Csome.other.property=other-value", + "--print-config", + "-JmemoryMax=-Xmx32m;-Xmx64m", + "--vm=testVm", + "--delimiter=;", + CLASS_NAME, + }; + CaliperOptions options = ParsedOptions.from(args, true); + + assertEquals(CLASS_NAME, options.benchmarkClassName()); + assertEquals(ImmutableSet.of("foo", "bar", "qux"), options.benchmarkMethodNames()); + assertFalse(options.dryRun()); + assertEquals(ImmutableSet.of("testInstrument"), options.instrumentNames()); + assertEquals(new File("/path/to/some/dir"), options.caliperDirectory()); + assertEquals(2, options.trialsPerScenario()); + assertEquals(ShortDuration.of(15, SECONDS), options.timeLimit()); + assertEquals(ImmutableSetMultimap.of("x", "a", "x", "b", "x", "c", "y", "b", "y", "d"), + options.userParameters()); + assertEquals(ImmutableMap.of("some.property", "value", "some.other.property", "other-value"), + options.configProperties()); + assertTrue(options.printConfiguration()); + assertEquals(ImmutableSetMultimap.of("memoryMax", "-Xmx32m", "memoryMax", "-Xmx64m"), + options.vmArguments()); + + String vmName = Iterables.getOnlyElement(options.vmNames()); + assertEquals("testVm", vmName); + } + + public static class FakeBenchmark {} + + private static final String CLASS_NAME = FakeBenchmark.class.getName(); +} diff --git a/caliper/src/test/java/com/google/caliper/runner/AllocationInstrumentTest.java b/caliper/src/test/java/com/google/caliper/runner/AllocationInstrumentTest.java new file mode 100644 index 0000000..0186aa0 --- /dev/null +++ b/caliper/src/test/java/com/google/caliper/runner/AllocationInstrumentTest.java @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2013 Google Inc. + * + * 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.google.caliper.runner; + +import static org.junit.Assert.assertEquals; + +import com.google.caliper.BeforeExperiment; +import com.google.caliper.Benchmark; +import com.google.caliper.config.VmConfig; +import com.google.caliper.model.Measurement; +import com.google.caliper.model.Trial; +import com.google.caliper.platform.Platform; +import com.google.caliper.platform.jvm.JvmPlatform; +import com.google.common.collect.ImmutableListMultimap; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +import java.io.File; +import java.util.List; + +/** + * Tests {@link AllocationInstrument}. + */ + +@RunWith(JUnit4.class) +public class AllocationInstrumentTest { + + @Rule public CaliperTestWatcher runner = new CaliperTestWatcher(); + + @Test public void getExtraCommandLineArgs() throws Exception { + AllocationInstrument instrument = new AllocationInstrument(); + File fakeJar = File.createTempFile("fake", "jar"); + fakeJar.deleteOnExit(); + instrument.setOptions(ImmutableMap.of("allocationAgentJar", fakeJar.getAbsolutePath())); + ImmutableSet<String> expected = new ImmutableSet.Builder<String>() + .addAll(JvmPlatform.INSTRUMENT_JVM_ARGS) + .add("-Xint") + .add("-javaagent:" + fakeJar.getAbsolutePath()) + .add("-Xbootclasspath/a:" + fakeJar.getAbsolutePath()) + .add("-Dsun.reflect.inflationThreshold=0") + .build(); + Platform platform = new JvmPlatform(); + VmConfig vmConfig = new VmConfig.Builder(platform, new File(System.getProperty("java.home"))) + .build(); + assertEquals(expected, instrument.getExtraCommandLineArgs(vmConfig)); + fakeJar.delete(); + } + + @Test + public void intrinsics() throws Exception { + runner.forBenchmark(ArrayListGrowthBenchmark.class) + .instrument("allocation") + .run(); + Trial trial = Iterables.getOnlyElement(runner.trials()); + ImmutableListMultimap<String, Measurement> measurementsByDescription = + Measurement.indexByDescription(trial.measurements()); + // 14 objects and 1960 bytes are the known values for growing an ArrayList from 1 element to 100 + // elements + for (Measurement objectMeasurement : measurementsByDescription.get("objects")) { + assertEquals(14.0, objectMeasurement.value().magnitude() / objectMeasurement.weight(), 0.001); + } + for (Measurement byteMeasurement : measurementsByDescription.get("bytes")) { + assertEquals(1960.0, byteMeasurement.value().magnitude() / byteMeasurement.weight(), 0.001); + } + } + + public static class TestBenchmark { + List<Object> list = Lists.newLinkedList(); + @Benchmark public int compressionSize(int reps) { + for (int i = 0; i < reps; i++) { + list.add(new Object()); + } + int hashCode = list.hashCode(); + list.clear(); + return hashCode; + } + } + + public static class ArrayListGrowthBenchmark { + @BeforeExperiment void warmUp() { + // ensure that hotspot has compiled this code + benchmarkGrowth(100000); + } + + @Benchmark void benchmarkGrowth(int reps) { + for (int i = 0; i < reps; i++) { + List<String> list = Lists.newArrayListWithCapacity(1); + for (int j = 0; j < 100; j++) { + list.add(""); + } + } + } + } +} diff --git a/caliper/src/test/java/com/google/caliper/runner/ArbitraryMeasurmentInstrumentTest.java b/caliper/src/test/java/com/google/caliper/runner/ArbitraryMeasurmentInstrumentTest.java new file mode 100644 index 0000000..1e7f98c --- /dev/null +++ b/caliper/src/test/java/com/google/caliper/runner/ArbitraryMeasurmentInstrumentTest.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2013 Google Inc. + * + * 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.google.caliper.runner; + +import static org.junit.Assert.assertEquals; + +import com.google.caliper.model.ArbitraryMeasurement; +import com.google.caliper.model.Measurement; +import com.google.caliper.model.Value; +import com.google.common.collect.Iterables; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** + * Integration tests for the {@link ArbitraryMeasurementInstrument} + */ +@RunWith(JUnit4.class) +public class ArbitraryMeasurmentInstrumentTest { + @Rule public CaliperTestWatcher runner = new CaliperTestWatcher(); + + @Test + + public void testSuccess() throws Exception { + runner.forBenchmark(TestBenchmark.class) + .instrument("arbitrary") + .run(); + Measurement measurement = Iterables.getOnlyElement( + Iterables.getOnlyElement(runner.trials()).measurements()); + Measurement expected = new Measurement.Builder() + .description("fake measurment") + .weight(1) + .value(Value.create(1.0, "hz")) + .build(); + assertEquals(expected, measurement); + } + + public static class TestBenchmark { + @ArbitraryMeasurement(units = "hz", description = "fake measurment") + public double compressionSize() { + return 1.0; + } + } +} diff --git a/caliper/src/test/java/com/google/caliper/runner/BadUserCodeTest.java b/caliper/src/test/java/com/google/caliper/runner/BadUserCodeTest.java new file mode 100644 index 0000000..3312088 --- /dev/null +++ b/caliper/src/test/java/com/google/caliper/runner/BadUserCodeTest.java @@ -0,0 +1,220 @@ +/* + * Copyright (C) 2013 Google Inc. + * + * 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.google.caliper.runner; + +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import com.google.caliper.BeforeExperiment; +import com.google.caliper.Benchmark; +import com.google.common.collect.Lists; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +import java.util.List; + +/** + * Integration tests for misbehaving benchmarks. + */ +@RunWith(JUnit4.class) +public class BadUserCodeTest { + @Rule public CaliperTestWatcher runner = new CaliperTestWatcher(); + + @Test + + public void testExceptionInInit() throws Exception { + try { + runner.forBenchmark(ExceptionInInitBenchmark.class).run(); + fail(); + } catch (UserCodeException expected) {} + } + + private static void throwSomeUserException() { + throw new RuntimeException(); + } + + static class ExceptionInInitBenchmark { + static { + throwSomeUserException(); + } + + @Benchmark void timeSomething(int reps) { + fail("" + reps); + } + } + + @Test + + public void testExceptionInConstructor() throws Exception { + try { + runner.forBenchmark(ExceptionInConstructorBenchmark.class).run(); + fail(); + } catch (UserCodeException expected) {} + } + + static class ExceptionInConstructorBenchmark { + ExceptionInConstructorBenchmark() { + throw new RuntimeException(); + } + + @Benchmark void timeSomething(int reps) { + fail("" + reps); + } + } + + @Test + + public void testExceptionInMethod() throws Exception { + try { + runner.forBenchmark(ExceptionInMethodBenchmark.class).run(); + fail(); + } catch (UserCodeException expected) {} + } + + static class ExceptionInMethodBenchmark { + @Benchmark void timeSomething(@SuppressWarnings("unused") int reps) { + throw new RuntimeException(); + } + } + + @Test + + public void testExceptionInMethod_notInDryRun() throws Exception { + try { + runner.forBenchmark(ExceptionLateInMethodBenchmark.class).run(); + fail(); + } catch (ProxyWorkerException expected) { + assertTrue(expected.getMessage().contains(ExceptionLateInMethodBenchmark.class.getName())); + } + } + + static class ExceptionLateInMethodBenchmark { + @Benchmark void timeSomething(int reps) { + if (reps > 1) { + throw new RuntimeException(); + } + } + } + + @Test + + public void testExceptionInSetUp() throws Exception { + try { + runner.forBenchmark(ExceptionInSetUpBenchmark.class).run(); + fail(); + } catch (UserCodeException expected) {} + } + + static class ExceptionInSetUpBenchmark { + @BeforeExperiment void setUp() { + throw new RuntimeException(); + } + + @Benchmark void timeSomething(int reps) { + fail("" + reps); + } + } + + @Test + + public void testNonDeterministicAllocation_noTrackAllocations() throws Exception { + try { + runner.forBenchmark(NonDeterministicAllocationBenchmark.class) + .instrument("allocation") + .options("-Cinstrument.allocation.options.trackAllocations=" + false) + .run(); + fail(); + } catch (ProxyWorkerException expected) { + String message = "Your benchmark appears to have non-deterministic allocation behavior"; + assertTrue("Expected " + expected.getMessage() + " to contain " + message, + expected.getMessage().contains(message)); + } + } + + @Test + + public void testNonDeterministicAllocation_trackAllocations() throws Exception { + try { + runner.forBenchmark(NonDeterministicAllocationBenchmark.class) + .instrument("allocation") + .options("-Cinstrument.allocation.options.trackAllocations=" + true) + .run(); + fail(); + } catch (ProxyWorkerException expected) { + String message = "Your benchmark appears to have non-deterministic allocation behavior"; + assertTrue("Expected " + expected.getMessage() + " to contain " + message, + expected.getMessage().contains(message)); + } + } + + /** The number of allocations is non deterministic because it depends on static state. */ + static class NonDeterministicAllocationBenchmark { + static int timeCount = 0; + // We dump items into this list so the jit cannot remove the allocations + static List<Object> list = Lists.newArrayList(); + @Benchmark int timeSomethingFBZ(@SuppressWarnings("unused") int reps) { + timeCount++; + if (timeCount % 2 == 0) { + list.add(new Object()); + return list.hashCode(); + } + return this.hashCode(); + } + } + + @Test + + public void testComplexNonDeterministicAllocation_noTrackAllocations() throws Exception { + // Without trackAllocations enabled this kind of non-determinism cannot be detected. + runner.forBenchmark(ComplexNonDeterministicAllocationBenchmark.class) + .instrument("allocation") + .options("-Cinstrument.allocation.options.trackAllocations=" + false) + .run(); + } + + @Test + + public void testComplexNonDeterministicAllocation_trackAllocations() throws Exception { + try { + runner.forBenchmark(ComplexNonDeterministicAllocationBenchmark.class) + .instrument("allocation") + .options("-Cinstrument.allocation.options.trackAllocations=" + true) + .run(); + } catch (ProxyWorkerException expected) { + String message = "Your benchmark appears to have non-deterministic allocation behavior"; + assertTrue("Expected " + expected.getMessage() + " to contain " + message, + expected.getMessage().contains(message)); + } + } + + /** Benchmark allocates the same number of things each time but in a different way. */ + static class ComplexNonDeterministicAllocationBenchmark { + static int timeCount = 0; + @Benchmark int timeSomethingFBZ(@SuppressWarnings("unused") int reps) { + // We dump items into this list so the jit cannot remove the allocations + List<Object> list = Lists.newArrayList(); + timeCount++; + if (timeCount % 2 == 0) { + list.add(new Object()); + } else { + list.add(new Object()); + } + return list.hashCode(); + } + } +} diff --git a/caliper/src/test/java/com/google/caliper/runner/BenchmarkClassCheckerTest.java b/caliper/src/test/java/com/google/caliper/runner/BenchmarkClassCheckerTest.java new file mode 100644 index 0000000..66305d2 --- /dev/null +++ b/caliper/src/test/java/com/google/caliper/runner/BenchmarkClassCheckerTest.java @@ -0,0 +1,63 @@ +package com.google.caliper.runner; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import com.google.caliper.Benchmark; +import com.google.caliper.api.Macrobenchmark; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +import java.util.Arrays; +import java.util.Collections; + +/** + * Tests {@link BenchmarkClassChecker}. + */ + +@RunWith(JUnit4.class) +public class BenchmarkClassCheckerTest { + + private BenchmarkClassChecker benchmarkClassChecker = + BenchmarkClassChecker.create(Collections.<String>emptyList()); + + @Test + public void testNoBenchmarkMethods() { + assertFalse(benchmarkClassChecker.isBenchmark(Object.class)); + } + + @Test + public void testBenchmarkAnnotatedMethod() { + assertTrue(benchmarkClassChecker.isBenchmark(BenchmarkAnnotatedMethod.class)); + } + + public static class BenchmarkAnnotatedMethod { + @Benchmark void benchmarkMethod() {} + } + + @Test + public void testMacroBenchmarkAnnotatedMethod() { + assertTrue(benchmarkClassChecker.isBenchmark(MacroBenchmarkAnnotatedMethod.class)); + } + + @Test + public void testMacroBenchmarkAnnotatedMethod_NoSuitableInstrument() { + benchmarkClassChecker = BenchmarkClassChecker.create(Arrays.asList("-i", "allocation")); + assertFalse(benchmarkClassChecker.isBenchmark(MacroBenchmarkAnnotatedMethod.class)); + } + + public static class MacroBenchmarkAnnotatedMethod { + @Macrobenchmark void macrobenchmarkMethod() {} + } + + @Test + public void testTimeMethod() { + assertTrue(benchmarkClassChecker.isBenchmark(TimeMethod.class)); + } + + public static class TimeMethod { + public void timeMethod() {} + } +} diff --git a/caliper/src/test/java/com/google/caliper/runner/BenchmarkClassTest.java b/caliper/src/test/java/com/google/caliper/runner/BenchmarkClassTest.java new file mode 100644 index 0000000..dcc6113 --- /dev/null +++ b/caliper/src/test/java/com/google/caliper/runner/BenchmarkClassTest.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2013 Google Inc. + * + * 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.google.caliper.runner; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +import com.google.caliper.AfterExperiment; +import com.google.caliper.BeforeExperiment; +import com.google.common.collect.ImmutableSet; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** + * Tests {@link BenchmarkClass}. + */ +@RunWith(JUnit4.class) +public class BenchmarkClassTest { + @Test public void beforeMeasurementMethods_AnnotatedBenchmark() throws Exception { + assertEquals( + ImmutableSet.of( + MyBenchmark.class.getDeclaredMethod("before1"), + MyBenchmark.class.getDeclaredMethod("before2")), + BenchmarkClass.forClass(MyBenchmark.class).beforeExperimentMethods()); + } + + @Test public void afterMeasurementMethods_AnnotatedBenchmark() throws Exception { + assertEquals( + ImmutableSet.of( + MyBenchmark.class.getDeclaredMethod("after1"), + MyBenchmark.class.getDeclaredMethod("after2")), + BenchmarkClass.forClass(MyBenchmark.class).afterExperimentMethods()); + } + + @Test public void forClass_inheritenceThrows() throws Exception { + try { + BenchmarkClass.forClass(MalformedBenhcmark.class); + fail(); + } catch (InvalidBenchmarkException expected) {} + } + + static class MyBenchmark { + @BeforeExperiment void before1() {} + @BeforeExperiment void before2() {} + @AfterExperiment void after1() {} + @AfterExperiment void after2() {} + } + + static class MalformedBenhcmark extends MyBenchmark {} +} diff --git a/caliper/src/test/java/com/google/caliper/runner/BenchmarkCreatorTest.java b/caliper/src/test/java/com/google/caliper/runner/BenchmarkCreatorTest.java new file mode 100644 index 0000000..c1b2bf6 --- /dev/null +++ b/caliper/src/test/java/com/google/caliper/runner/BenchmarkCreatorTest.java @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2015 Google Inc. + * + * 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.google.caliper.runner; + +import com.google.caliper.Param; +import com.google.common.collect.ImmutableSortedMap; +import junit.framework.TestCase; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** + * Test for {@link BenchmarkCreator} + */ +@RunWith(JUnit4.class) +public class BenchmarkCreatorTest extends TestCase { + + @Test + public void publicDefaultConstructorNoParamBenchmark() { + BenchmarkCreator creator = new BenchmarkCreator(PublicDefaultConstructorNoParamBenchmark.class, + ImmutableSortedMap.<String, String>of()); + + Object benchmarkInstance = creator.createBenchmarkInstance(); + assertTrue(benchmarkInstance instanceof PublicDefaultConstructorNoParamBenchmark); + } + + public static class PublicDefaultConstructorNoParamBenchmark { + } + + @Test + public void publicDefaultConstructorWithParamBenchmark() { + BenchmarkCreator creator = new BenchmarkCreator( + PublicDefaultConstructorWithParamBenchmark.class, + ImmutableSortedMap.of("byteField", "1", "intField", "2", "stringField", "string")); + + Object benchmarkInstance = creator.createBenchmarkInstance(); + assertTrue(benchmarkInstance instanceof PublicDefaultConstructorWithParamBenchmark); + PublicDefaultConstructorWithParamBenchmark benchmark = + (PublicDefaultConstructorWithParamBenchmark) benchmarkInstance; + assertEquals(1, benchmark.byteField); + assertEquals(2, benchmark.intField); + assertEquals("string", benchmark.stringField); + } + + public static class PublicDefaultConstructorWithParamBenchmark { + @Param + byte byteField; + + @Param + int intField; + + @Param + String stringField; + } + + @Test + public void publicNoSuitableConstructorBenchmark() { + try { + new BenchmarkCreator( + PublicNoSuitableConstructorBenchmark.class, + ImmutableSortedMap.<String, String>of()); + } catch (UserCodeException e) { + assertEquals("Benchmark class " + + PublicNoSuitableConstructorBenchmark.class.getName() + + " does not have a publicly visible default constructor", e.getMessage()); + } + } + + public static class PublicNoSuitableConstructorBenchmark { + @Param + byte byteField; + + @Param + int intField; + + @Param + String stringField; + + public PublicNoSuitableConstructorBenchmark( + byte byteField, int intField, String stringField) { + this.byteField = byteField; + this.intField = intField; + this.stringField = stringField; + } + } +} diff --git a/caliper/src/test/java/com/google/caliper/runner/CaliperTestWatcher.java b/caliper/src/test/java/com/google/caliper/runner/CaliperTestWatcher.java new file mode 100644 index 0000000..63f0044 --- /dev/null +++ b/caliper/src/test/java/com/google/caliper/runner/CaliperTestWatcher.java @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2013 Google Inc. + * + * 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.google.caliper.runner; + +import static com.google.common.base.Preconditions.checkState; + +import com.google.caliper.config.InvalidConfigurationException; +import com.google.caliper.model.Trial; +import com.google.caliper.util.InvalidCommandException; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Lists; +import com.google.common.io.Files; + +import org.junit.rules.TestWatcher; +import org.junit.runner.Description; + +import java.io.File; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.util.Arrays; +import java.util.List; + +/** + * A {@link TestWatcher} that can be used to configure a run of caliper. + * + * <p>Provides common test configuration utilities and redirects output to a buffer and only dumps + * it during a failure. + * + * <p>TODO(lukes,gak): This is a bad name since it isn't just watching the tests, it is helping you + * run the tests. + */ +public final class CaliperTestWatcher extends TestWatcher { + // N.B. StringWriter is internally synchronized and is safe to write to from multiple threads. + private StringWriter stdout; + private final StringWriter stderr = new StringWriter(); + private File workerOutput; + + private String instrument; + private Class<?> benchmarkClass; + private List<String> extraOptions = Lists.newArrayList(); + + CaliperTestWatcher forBenchmark(Class<?> benchmarkClass) { + this.benchmarkClass = benchmarkClass; + return this; + } + + CaliperTestWatcher instrument(String instrument) { + this.instrument = instrument; + return this; + } + + CaliperTestWatcher options(String... extraOptions) { + this.extraOptions = Arrays.asList(extraOptions); + return this; + } + + void run() throws InvalidCommandException, InvalidBenchmarkException, + InvalidConfigurationException { + checkState(benchmarkClass != null, "You must configure a benchmark!"); + workerOutput = Files.createTempDir(); + // configure a custom dir so the files aren't deleted when CaliperMain returns + List<String> options = Lists.newArrayList( + "-Cworker.output=" + workerOutput.getPath(), + "-Cresults.file.class=", + "-Cresults.upload.class=" + InMemoryResultsUploader.class.getName()); + if (instrument != null) { + options.add("-i"); + options.add(instrument); + } + options.addAll(extraOptions); + options.add(benchmarkClass.getName()); + this.stdout = new StringWriter(); + CaliperMain.exitlessMain( + options.toArray(new String[0]), + new PrintWriter(stdout, true), + new PrintWriter(stderr, true)); + } + + @Override protected void finished(Description description) { + if (workerOutput != null) { + for (File f : workerOutput.listFiles()) { + f.delete(); + } + workerOutput.delete(); + } + } + + @Override protected void failed(Throwable e, Description description) { + // don't log if run was never called. + if (stdout != null) { + System.err.println("Caliper failed with the following output (stdout):\n" + + stdout.toString() + "stderr:\n" + stderr.toString()); + } + } + + ImmutableList<Trial> trials() { + return InMemoryResultsUploader.trials(); + } + + public StringWriter getStderr() { + return stderr; + } + + public StringWriter getStdout() { + return stdout; + } + + File workerOutputDirectory() { + return workerOutput; + } +} diff --git a/caliper/src/test/java/com/google/caliper/runner/ExperimentingRunnerModuleTest.java b/caliper/src/test/java/com/google/caliper/runner/ExperimentingRunnerModuleTest.java new file mode 100644 index 0000000..a941f56 --- /dev/null +++ b/caliper/src/test/java/com/google/caliper/runner/ExperimentingRunnerModuleTest.java @@ -0,0 +1,149 @@ +/* + * Copyright (C) 2013 Google Inc. + * + * 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.google.caliper.runner; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import static org.mockito.Mockito.when; + +import com.google.caliper.Benchmark; +import com.google.caliper.options.CaliperOptions; +import com.google.caliper.platform.Platform; +import com.google.caliper.platform.SupportedPlatform; +import com.google.caliper.runner.Instrument.Instrumentation; +import com.google.caliper.worker.Worker; +import com.google.common.collect.ImmutableSet; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; + +import java.lang.reflect.Method; + +/** + * Tests {@link ExperimentingRunnerModule}. + */ +@RunWith(MockitoJUnitRunner.class) +public class ExperimentingRunnerModuleTest { + private ExperimentingRunnerModule module = new ExperimentingRunnerModule(); + private Instrument instrumentA = new FakeInstrument(); + private Instrument instrumentB = new FakeInstrument(); + + @Mock CaliperOptions options; + + private Method methodA; + private Method methodB; + private Method methodC; + + @Before public void setUp() throws Exception { + methodA = TestBenchmark.class.getDeclaredMethod("a"); + methodB = TestBenchmark.class.getDeclaredMethod("b"); + methodC = TestBenchmark.class.getDeclaredMethod("c"); + } + + @Test public void provideInstrumentations_noNames() throws Exception { + when(options.benchmarkMethodNames()).thenReturn(ImmutableSet.<String>of()); + assertEquals( + new ImmutableSet.Builder<Instrumentation>() + .add(instrumentA.createInstrumentation(methodA)) + .add(instrumentA.createInstrumentation(methodB)) + .add(instrumentA.createInstrumentation(methodC)) + .add(instrumentB.createInstrumentation(methodA)) + .add(instrumentB.createInstrumentation(methodB)) + .add(instrumentB.createInstrumentation(methodC)) + .build(), + module.provideInstrumentations(options, + BenchmarkClass.forClass(TestBenchmark.class), + ImmutableSet.of(instrumentA, instrumentB))); + } + + @SuppressWarnings("unchecked") + @Test public void provideInstrumentations_withNames() throws Exception { + when(options.benchmarkMethodNames()).thenReturn(ImmutableSet.of("b"), + ImmutableSet.of("a", "c")); + assertEquals( + new ImmutableSet.Builder<Instrumentation>() + .add(instrumentA.createInstrumentation(methodB)) + .add(instrumentB.createInstrumentation(methodB)) + .build(), + module.provideInstrumentations(options, + BenchmarkClass.forClass(TestBenchmark.class), + ImmutableSet.of(instrumentA, instrumentB))); + assertEquals( + new ImmutableSet.Builder<Instrumentation>() + .add(instrumentA.createInstrumentation(methodA)) + .add(instrumentA.createInstrumentation(methodC)) + .add(instrumentB.createInstrumentation(methodA)) + .add(instrumentB.createInstrumentation(methodC)) + .build(), + module.provideInstrumentations(options, + BenchmarkClass.forClass(TestBenchmark.class), + ImmutableSet.of(instrumentA, instrumentB))); + } + + @Test public void provideInstrumentations_withInvalidName() { + when(options.benchmarkMethodNames()).thenReturn( + ImmutableSet.of("a", "c", "bad")); + try { + module.provideInstrumentations(options, + BenchmarkClass.forClass(TestBenchmark.class), + ImmutableSet.of(instrumentA, instrumentB)); + fail("should have thrown for invalid benchmark method name"); + } catch (Exception expected) { + assertTrue(expected.getMessage().contains("[bad]")); + } + } + + static final class TestBenchmark { + @Benchmark void a() {} + @Benchmark void b() {} + @Benchmark void c() {} + } + + @SupportedPlatform(Platform.Type.JVM) + static final class FakeInstrument extends Instrument { + @Override public boolean isBenchmarkMethod(Method method) { + return true; + } + + @Override + public Instrumentation createInstrumentation(Method benchmarkMethod) + throws InvalidBenchmarkException { + return new Instrumentation(benchmarkMethod) { + @Override + public Class<? extends Worker> workerClass() { + throw new UnsupportedOperationException(); + } + + @Override + public void dryRun(Object benchmark) throws InvalidBenchmarkException {} + + @Override + MeasurementCollectingVisitor getMeasurementCollectingVisitor() { + throw new UnsupportedOperationException(); + } + }; + } + + @Override public TrialSchedulingPolicy schedulingPolicy() { + return TrialSchedulingPolicy.SERIAL; + } + } +} diff --git a/caliper/src/test/java/com/google/caliper/runner/FakeWorkers.java b/caliper/src/test/java/com/google/caliper/runner/FakeWorkers.java new file mode 100644 index 0000000..309a462 --- /dev/null +++ b/caliper/src/test/java/com/google/caliper/runner/FakeWorkers.java @@ -0,0 +1,195 @@ +/* + * Copyright (C) 2014 Google Inc. + * + * 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.google.caliper.runner; + +import com.google.caliper.bridge.LogMessage; +import com.google.caliper.bridge.LogMessageVisitor; +import com.google.caliper.bridge.OpenedSocket; +import com.google.caliper.config.CaliperConfig; +import com.google.caliper.config.InvalidConfigurationException; +import com.google.caliper.platform.Platform; +import com.google.caliper.platform.jvm.JvmPlatform; +import com.google.caliper.util.Util; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.collect.ImmutableMap; + +import java.io.IOException; +import java.io.Serializable; +import java.net.InetAddress; +import java.net.Socket; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.CountDownLatch; + +import javax.annotation.concurrent.GuardedBy; + +/** + * A collection of Simple java executables and a helper method for creating process builders for + * them. + */ +final class FakeWorkers { + + @GuardedBy("FakeWorkers.class") + private static VirtualMachine jvm; + + /** + * Try to find the currently executing jvm binary, N.B. This isn't guaranteed to be cross + * platform. + */ + private static synchronized VirtualMachine init() { + if (jvm == null) { + try { + Platform platform = new JvmPlatform(); + jvm = new VirtualMachine("default", + new CaliperConfig(ImmutableMap.<String, String>of()).getDefaultVmConfig(platform)); + } catch (InvalidConfigurationException e) { + throw new RuntimeException(); + } + } + return jvm; + } + + /** + * Returns a ProcessBuilder that attempts to invoke the given class as main in a JVM configured + * with a classpath equivalent to the currently executing JVM. + */ + static ProcessBuilder createProcessBuilder(Class<?> mainClass, String ...mainArgs) { + VirtualMachine jvm = init(); + List<String> args; + try { + args = WorkerProcess.getJvmArgs(jvm, BenchmarkClass.forClass(mainClass)); + } catch (InvalidBenchmarkException e) { + throw new RuntimeException(e); + } + args.add(mainClass.getName()); + Collections.addAll(args, mainArgs); + return new ProcessBuilder().command(args); + } + + public static VirtualMachine getVirtualMachine() { + return init(); + } + + /** + * A simple main method that will sleep for the number of milliseconds specified in the first + * argument. + */ + static final class Sleeper { + public static void main(String[] args) throws NumberFormatException, InterruptedException { + Thread.sleep(Long.parseLong(args[0])); + } + } + + /** + * A simple main method that exits immediately with the code provided by the first argument + */ + static final class Exit { + public static void main(String[] args) { + System.exit(Integer.parseInt(args[0])); + } + } + + /** + * A simple main method that exits immediately with the code provided by the first argument + */ + static final class CloseAndSleep { + public static void main(String[] args) throws IOException, InterruptedException { + System.err.close(); + System.in.close(); + System.out.close(); + new CountDownLatch(1).await(); // wait forever + } + } + + /** + * Prints alternating arguments to standard out and standard error. + */ + static final class PrintClient { + public static void main(String[] args) { + for (int i = 0; i < args.length; i++) { + if (i % 2 == 0) { + System.out.println(args[i]); + System.out.flush(); + } else { + System.err.println(args[i]); + System.err.flush(); + } + } + } + } + + /** + * Prints alternating arguments to standard out and standard error. + */ + @VisibleForTesting + static final class LoadBenchmarkClass { + + public static void main(String[] args) throws ClassNotFoundException { + String benchmarkClassName = args[0]; + Util.loadClass(benchmarkClassName); + } + } + + static final class DummyLogMessage extends LogMessage implements Serializable { + private final String content; + + DummyLogMessage(String content) { + this.content = content; + } + + @Override public void accept(LogMessageVisitor visitor) {} + + @Override public String toString() { + return content; + } + + @Override public boolean equals(Object obj) { + return obj instanceof DummyLogMessage && ((DummyLogMessage) obj).content.equals(content); + } + + @Override public int hashCode() { + return content.hashCode(); + } + } + + /** + * Connects to a socket on localhost on the port provided as the first argument and echos all + * data. + * + * <p>Once the connection has been closed it prints the remaining args to stdout + */ + static final class SocketEchoClient { + public static void main(String[] args) throws Exception { + int port = Integer.parseInt(args[0]); + OpenedSocket openedSocket = OpenedSocket.fromSocket( + new Socket(InetAddress.getLocalHost(), port)); + OpenedSocket.Reader reader = openedSocket.reader(); + OpenedSocket.Writer writer = openedSocket.writer(); + writer.write(new DummyLogMessage("start")); + writer.flush(); + Serializable obj; + while ((obj = reader.read()) != null) { + writer.write(obj); + writer.flush(); + } + writer.close(); + reader.close(); + for (int i = 1; i < args.length; i++) { + System.out.println(args[i]); + System.out.flush(); + } + } + } +} diff --git a/caliper/src/test/java/com/google/caliper/runner/InMemoryResultsUploader.java b/caliper/src/test/java/com/google/caliper/runner/InMemoryResultsUploader.java new file mode 100644 index 0000000..075a6e2 --- /dev/null +++ b/caliper/src/test/java/com/google/caliper/runner/InMemoryResultsUploader.java @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2013 Google Inc. + * + * 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.google.caliper.runner; + +import static com.google.common.base.Preconditions.checkState; + +import com.google.caliper.api.ResultProcessor; +import com.google.caliper.model.Trial; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Lists; + +import java.io.IOException; +import java.util.List; + +import javax.inject.Inject; + +/** + * A {@link ResultProcessor} that collects all trials in a static list for easy inspection by tests. + */ +public class InMemoryResultsUploader implements ResultProcessor { + static ImmutableList<Trial> trials() { + return ImmutableList.copyOf(trials); + } + + private static List<Trial> trials; + private boolean isClosed; + + @Inject public InMemoryResultsUploader() { + trials = Lists.newArrayList(); + } + + @Override public void close() throws IOException { + checkState(!isClosed); + isClosed = true; + } + + @Override public void processTrial(Trial trial) { + checkState(!isClosed); + trials.add(trial); + } +}
\ No newline at end of file diff --git a/caliper/src/test/java/com/google/caliper/runner/MalformedBenchmarksTest.java b/caliper/src/test/java/com/google/caliper/runner/MalformedBenchmarksTest.java new file mode 100644 index 0000000..80a1330 --- /dev/null +++ b/caliper/src/test/java/com/google/caliper/runner/MalformedBenchmarksTest.java @@ -0,0 +1,183 @@ +/* + * Copyright (C) 2009 Google Inc. + * + * 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.google.caliper.runner; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +import com.google.caliper.Benchmark; +import com.google.caliper.Param; +import com.google.caliper.config.InvalidConfigurationException; +import com.google.caliper.util.InvalidCommandException; + +import junit.framework.AssertionFailedError; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +import java.io.PrintWriter; +import java.io.StringWriter; + +/** + * Unit test covering common user mistakes in benchmark classes. + */ +@RunWith(JUnit4.class) + +public class MalformedBenchmarksTest { + // Put the expected messages together here, which may promote some kind of + // consistency in their wording. :) + + private static final String ABSTRACT = + "Class '%s' is abstract"; + private static final String NO_CONSTRUCTOR = + "Benchmark class %s does not have a publicly visible default constructor"; + private static final String NO_METHODS = + "There were no experiments to be performed for the class %s using the instruments " + + "[allocation, runtime]"; + private static final String STATIC_BENCHMARK = + "Benchmark methods must not be static: timeIt"; + private static final String WRONG_ARGUMENTS = + "Benchmark methods must have no arguments or accept a single int or long parameter: timeIt"; + private static final String STATIC_PARAM = + "Parameter field 'oops' must not be static"; + private static final String RESERVED_PARAM = + "Class '%s' uses reserved parameter name 'vm'"; + private static final String NO_CONVERSION = "Type 'Object' of parameter field 'oops' " + + "has no recognized String-converting method; see <TODO> for details"; + private static final String CONVERT_FAILED = // granted this one's a little weird (and brittle) + "Cannot convert value 'oops' to type 'int': For input string: \"oops\""; + + @Test public void abstractBenchmark() throws Exception { + expectException(ABSTRACT, AbstractBenchmark.class); + } + abstract static class AbstractBenchmark {} + + @Test public void noSuitableConstructor() throws Exception { + expectException(String.format(NO_CONSTRUCTOR, BadConstructorBenchmark.class.getName()), + BadConstructorBenchmark.class); + } + + @SuppressWarnings("unused") + static class BadConstructorBenchmark { + BadConstructorBenchmark(String damnParam) {} + @Benchmark void timeIt(int reps) {} + } + + @Test public void noBenchmarkMethods() throws Exception { + expectException(NO_METHODS, NoMethodsBenchmark.class); + } + + @SuppressWarnings("unused") + static class NoMethodsBenchmark { + void timeIt(int reps) {} // not annotated + } + + @Test public void staticBenchmarkMethod() throws Exception { + expectException(STATIC_BENCHMARK, StaticBenchmarkMethodBenchmark.class); + } + + @SuppressWarnings("unused") + static class StaticBenchmarkMethodBenchmark { + @Benchmark public static void timeIt(int reps) {} + } + + @Test public void wrongSignature() throws Exception { + expectException(WRONG_ARGUMENTS, BoxedParamBenchmark.class); + expectException(WRONG_ARGUMENTS, ExtraParamBenchmark.class); + } + + @SuppressWarnings("unused") + static class BoxedParamBenchmark { + @Benchmark void timeIt(Integer reps) {} + } + + @SuppressWarnings("unused") + static class ExtraParamBenchmark { + @Benchmark void timeIt(int reps, int what) {} + } + + @Test public void hasBenchmarkOverloads() throws Exception { + // N.B. baz is fine since although it has an overload, its overload is not a benchmark method. + expectException( + "Overloads are disallowed for benchmark methods, found overloads of [bar, foo] in " + + "benchmark OverloadsAnnotatedBenchmark", + OverloadsAnnotatedBenchmark.class); + } + + @SuppressWarnings("unused") + static class OverloadsAnnotatedBenchmark { + @Benchmark public void foo(long reps) {} + @Benchmark public void foo(int reps) {} + @Benchmark public void bar(long reps) {} + @Benchmark public void bar(int reps) {} + @Benchmark public void baz(int reps) {} + public void baz(long reps, boolean thing) {} + public void baz(long reps) {} + } + + @Test public void staticParam() throws Exception { + expectException(STATIC_PARAM, StaticParamBenchmark.class); + } + static class StaticParamBenchmark { + @Param static String oops; + } + + @Test public void reservedParameterName() throws Exception { + expectException(RESERVED_PARAM, ReservedParamBenchmark.class); + } + static class ReservedParamBenchmark { + @Param String vm; + } + + @Test public void unparsableParamType() throws Exception { + expectException(NO_CONVERSION, UnparsableParamTypeBenchmark.class); + } + static class UnparsableParamTypeBenchmark { + @Param Object oops; + } + + @Test public void unparsableParamDefault() throws Exception { + expectException(CONVERT_FAILED, UnparsableParamDefaultBenchmark.class); + } + static class UnparsableParamDefaultBenchmark { + @Param({"1", "2", "oops"}) int number; + } + + // end of tests + + private void expectException(String expectedMessageFmt, Class<?> benchmarkClass) + throws InvalidCommandException, InvalidConfigurationException { + try { + CaliperMain.exitlessMain( + new String[] {"--instrument=allocation,runtime", "--dry-run", benchmarkClass.getName()}, + new PrintWriter(new StringWriter()), new PrintWriter(new StringWriter())); + fail("no exception thrown"); + } catch (InvalidBenchmarkException e) { + try { + String expectedMessageText = + String.format(expectedMessageFmt, benchmarkClass.getSimpleName()); + assertEquals(expectedMessageText, e.getMessage()); + + // don't swallow our real stack trace + } catch (AssertionFailedError afe) { + afe.initCause(e); + throw afe; + } + } + } +} diff --git a/caliper/src/test/java/com/google/caliper/runner/ResultProcessorCreatorTest.java b/caliper/src/test/java/com/google/caliper/runner/ResultProcessorCreatorTest.java new file mode 100644 index 0000000..d88ee39 --- /dev/null +++ b/caliper/src/test/java/com/google/caliper/runner/ResultProcessorCreatorTest.java @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2015 Google Inc. + * + * 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.google.caliper.runner; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import com.google.caliper.api.ResultProcessor; +import com.google.caliper.model.Trial; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; +import java.io.IOException; + +/** + * Unit test to ensure that {@link ResultProcessorCreator} works properly. + */ +@RunWith(JUnit4.class) +public class ResultProcessorCreatorTest { + + public static final String NOT_SUPPORTED = + "ResultProcessor %s not supported as it does not have a public default constructor"; + + @Test + public void testNotPublicConstructor() { + try { + ResultProcessorCreator.createResultProcessor(NoPublicConstructorResultProcessor.class); + fail("Did not fail on non-public constructor"); + } catch (UserCodeException e) { + assertEquals(String.format(NOT_SUPPORTED, NoPublicConstructorResultProcessor.class), + e.getMessage()); + } + } + + public static class NoPublicConstructorResultProcessor implements ResultProcessor { + + NoPublicConstructorResultProcessor() { + } + + @Override + public void processTrial(Trial trial) { + } + + @Override + public void close() throws IOException { + } + } + + @Test + public void testPublicButNotDefaultConstructor() { + try { + ResultProcessorCreator.createResultProcessor( + PublicButNotDefaultDefaultConstructorResultProcessor.class); + fail("Did not fail on public but not default constructor"); + } catch (UserCodeException e) { + assertEquals( + String.format(NOT_SUPPORTED, PublicButNotDefaultDefaultConstructorResultProcessor.class), + e.getMessage()); + } + } + + public static class PublicButNotDefaultDefaultConstructorResultProcessor + implements ResultProcessor { + + public PublicButNotDefaultDefaultConstructorResultProcessor( + @SuppressWarnings("UnusedParameters") int i) { + } + + @Override + public void processTrial(Trial trial) { + } + + @Override + public void close() throws IOException { + } + } + + @Test + public void testPublicConstructor() { + ResultProcessor processor = + ResultProcessorCreator.createResultProcessor(PublicDefaultConstructorResultProcessor.class); + assertTrue(processor instanceof PublicDefaultConstructorResultProcessor); + } + + public static class PublicDefaultConstructorResultProcessor implements ResultProcessor { + + @Override + public void processTrial(Trial trial) { + } + + @Override + public void close() throws IOException { + } + } +} diff --git a/caliper/src/test/java/com/google/caliper/runner/RuntimeInstrumentTest.java b/caliper/src/test/java/com/google/caliper/runner/RuntimeInstrumentTest.java new file mode 100644 index 0000000..e960c03 --- /dev/null +++ b/caliper/src/test/java/com/google/caliper/runner/RuntimeInstrumentTest.java @@ -0,0 +1,255 @@ +/* + * Copyright (C) 2013 Google Inc. + * + * 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.google.caliper.runner; + +import static java.util.concurrent.TimeUnit.NANOSECONDS; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import com.google.caliper.Benchmark; +import com.google.caliper.api.BeforeRep; +import com.google.caliper.api.Macrobenchmark; +import com.google.caliper.runner.Instrument.Instrumentation; +import com.google.caliper.util.ShortDuration; +import com.google.caliper.worker.MacrobenchmarkWorker; +import com.google.caliper.worker.RuntimeWorker; +import com.google.common.base.Function; +import com.google.common.base.Predicate; +import com.google.common.collect.FluentIterable; +import com.google.common.collect.ImmutableSet; +import com.google.common.util.concurrent.Uninterruptibles; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.concurrent.TimeUnit; + +/** + * Tests {@link RuntimeInstrument}. + */ +@RunWith(JUnit4.class) +public class RuntimeInstrumentTest { + @Rule public CaliperTestWatcher runner = new CaliperTestWatcher(); + + private RuntimeInstrument instrument; + + @Before public void createInstrument() { + this.instrument = new RuntimeInstrument(ShortDuration.of(100, NANOSECONDS)); + } + + @Test public void isBenchmarkMethod() { + assertEquals( + ImmutableSet.of("macrobenchmark", "microbenchmark", "picobenchmark", "integerParam"), + FluentIterable.from(Arrays.asList(RuntimeBenchmark.class.getDeclaredMethods())) + .filter(new Predicate<Method>() { + @Override public boolean apply(Method input) { + return instrument.isBenchmarkMethod(input); + } + }) + .transform(new Function<Method, String>() { + @Override public String apply(Method input) { + return input.getName(); + } + }) + .toSet()); + } + + @Test public void createInstrumentation_macrobenchmark() throws Exception { + Method benchmarkMethod = RuntimeBenchmark.class.getDeclaredMethod("macrobenchmark"); + Instrumentation instrumentation = instrument.createInstrumentation(benchmarkMethod); + assertEquals(benchmarkMethod, instrumentation.benchmarkMethod()); + assertEquals(instrument, instrumentation.instrument()); + assertEquals(MacrobenchmarkWorker.class, instrumentation.workerClass()); + } + + @Test public void createInstrumentation_microbenchmark() throws Exception { + Method benchmarkMethod = RuntimeBenchmark.class.getDeclaredMethod("microbenchmark", int.class); + Instrumentation instrumentation = instrument.createInstrumentation(benchmarkMethod); + assertEquals(benchmarkMethod, instrumentation.benchmarkMethod()); + assertEquals(instrument, instrumentation.instrument()); + assertEquals(RuntimeWorker.Micro.class, instrumentation.workerClass()); + } + + @Test public void createInstrumentation_picobenchmark() throws Exception { + Method benchmarkMethod = RuntimeBenchmark.class.getDeclaredMethod("picobenchmark", long.class); + Instrumentation instrumentation = instrument.createInstrumentation(benchmarkMethod); + assertEquals(benchmarkMethod, instrumentation.benchmarkMethod()); + assertEquals(instrument, instrumentation.instrument()); + assertEquals(RuntimeWorker.Pico.class, instrumentation.workerClass()); + } + + @Test public void createInstrumentation_badParam() throws Exception { + Method benchmarkMethod = + RuntimeBenchmark.class.getDeclaredMethod("integerParam", Integer.class); + try { + instrument.createInstrumentation(benchmarkMethod); + fail(); + } catch (InvalidBenchmarkException expected) {} + } + + @Test public void createInstrumentation_notAMacrobenchmark() throws Exception { + Method benchmarkMethod = RuntimeBenchmark.class.getDeclaredMethod("notAMacrobenchmark"); + try { + instrument.createInstrumentation(benchmarkMethod); + fail(); + } catch (IllegalArgumentException expected) {} + } + + @Test public void createInstrumentationnotAMicrobenchmark() throws Exception { + Method benchmarkMethod = + RuntimeBenchmark.class.getDeclaredMethod("notAMicrobenchmark", int.class); + try { + instrument.createInstrumentation(benchmarkMethod); + fail(); + } catch (IllegalArgumentException expected) {} + } + + @Test public void createInstrumentation_notAPicobenchmark() throws Exception { + Method benchmarkMethod = + RuntimeBenchmark.class.getDeclaredMethod("notAPicobenchmark", long.class); + try { + instrument.createInstrumentation(benchmarkMethod); + fail(); + } catch (IllegalArgumentException expected) {} + } + + @SuppressWarnings("unused") + private static final class RuntimeBenchmark { + @Benchmark void macrobenchmark() {} + @Benchmark void microbenchmark(int reps) {} + @Benchmark void picobenchmark(long reps) {} + + @Benchmark void integerParam(Integer oops) {} + + void notAMacrobenchmark() {} + void notAMicrobenchmark(int reps) {} + void notAPicobenchmark(long reps) {} + } + + private double relativeDifference(double a, double b) { + return Math.abs(a - b) / ((a + b) / 2.0); + } + + static final class TestBenchmark { + @Benchmark long pico(long reps) { + long dummy = 0; + for (long i = 0; i < reps; i++) { + dummy += spin(); + } + return dummy; + } + + @Benchmark long micro(int reps) { + long dummy = 0; + for (int i = 0; i < reps; i++) { + dummy += spin(); + } + return dummy; + } + + @Macrobenchmark long macro() { + return spin(); + } + } + + // busy spin for 10ms and return the elapsed time. N.B. we busy spin instead of sleeping so + // that we aren't put at the mercy (and variance) of the thread scheduler. + private static long spin() { + long remainingNanos = TimeUnit.MILLISECONDS.toNanos(10); + long start = System.nanoTime(); + long elapsed; + while ((elapsed = System.nanoTime() - start) < remainingNanos) {} + return elapsed; + } + + @Test + + public void gcBeforeEachOptionIsHonored() throws Exception { + runBenchmarkWithKnownHeap(true); + // The GC error will only be avoided if gcBeforeEach is true, and + // honored by the MacrobenchmarkWorker. + assertFalse("No GC warning should be printed to stderr", + runner.getStdout().toString().contains("WARNING: GC occurred during timing.")); + } + + @Test + + public void gcBeforeEachOptionIsReallyNecessary() throws Exception { + // Verifies that we indeed get a GC warning if gcBeforeEach = false. + runBenchmarkWithKnownHeap(false); + assertTrue("A GC warning should be printed to stderr if gcBeforeEach isn't honored", + runner.getStdout().toString().contains("WARNING: GC occurred during timing.")); + } + + private void runBenchmarkWithKnownHeap(boolean gcBeforeEach) throws Exception { + runner.forBenchmark(BenchmarkThatAllocatesALot.class) + .instrument("runtime") + .options( + "-Cvm.args=-Xmx512m", + "-Cinstrument.runtime.options.measurements=10", + "-Cinstrument.runtime.options.gcBeforeEach=" + gcBeforeEach, + "--time-limit=30s") + .run(); + } + + static final class BenchmarkThatAllocatesALot { + @Benchmark + int benchmarkMethod() { + // Any larger and the GC doesn't manage to make enough space, resulting in + // OOMErrors in both test cases above. + long[] array = new long[32 * 1024 * 1024]; + return array.length; + } + } + + @Test + + public void maxWarmupWallTimeOptionIsHonored() throws Exception { + runner.forBenchmark(MacroBenchmarkWithLongBeforeRep.class) + .instrument("runtime") + .options( + "-Cinstrument.runtime.options.maxWarmupWallTime=100ms", + "--time-limit=10s") + .run(); + + assertTrue( + "The maxWarmupWallTime should trigger an interruption of warmup and a warning " + + "should be printed to stderr", + runner.getStdout().toString().contains( + "WARNING: Warmup was interrupted " + + "because it took longer than 100ms of wall-clock time.")); + } + + static final class MacroBenchmarkWithLongBeforeRep { + @BeforeRep + public void beforeRepMuchLongerThanBenchmark() { + Uninterruptibles.sleepUninterruptibly(100, TimeUnit.MILLISECONDS); + } + + @Benchmark + long prettyFastMacroBenchmark() { + return spin(); + } + } +} diff --git a/caliper/src/test/java/com/google/caliper/runner/ServerSocketServiceTest.java b/caliper/src/test/java/com/google/caliper/runner/ServerSocketServiceTest.java new file mode 100644 index 0000000..8add40b --- /dev/null +++ b/caliper/src/test/java/com/google/caliper/runner/ServerSocketServiceTest.java @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2013 Google Inc. + * + * 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.google.caliper.runner; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import com.google.caliper.bridge.OpenedSocket; +import com.google.caliper.bridge.StartupAnnounceMessage; +import com.google.common.util.concurrent.ListenableFuture; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.Socket; +import java.util.UUID; +import java.util.concurrent.ExecutionException; + +/** + * Tests for {@link ServerSocketService}. + */ +@RunWith(JUnit4.class) + +public class ServerSocketServiceTest { + + private final ServerSocketService service = new ServerSocketService(); + private int port; + + @Before public void startService() { + service.startAsync().awaitRunning(); + port = service.getPort(); + } + + @After public void stopService() { + service.stopAsync().awaitTerminated(); + } + + @Test public void getConnectionId_requestComesInFirst() throws Exception { + UUID id = UUID.randomUUID(); + ListenableFuture<OpenedSocket> pendingServerConnection = service.getConnection(id); + assertFalse(pendingServerConnection.isDone()); + OpenedSocket clientSocket = openConnectionAndIdentify(id); + // Assert that the ends are hooked up to each other + assertEndsConnected(clientSocket, pendingServerConnection.get()); + } + + @Test public void getConnectionIdTwice_acceptComesFirst() throws Exception { + UUID id = UUID.randomUUID(); + OpenedSocket clientSocket = openConnectionAndIdentify(id); + + ListenableFuture<OpenedSocket> pendingServerConnection = service.getConnection(id); + // wait for the service to fully initialize the connection + OpenedSocket serverSocket = pendingServerConnection.get(); + assertEndsConnected(clientSocket, serverSocket); + try { + // the second request is an error + service.getConnection(id).get(); + fail(); + } catch (IllegalStateException expected) {} + } + + @Test public void getConnectionStoppedService() throws Exception { + UUID id = UUID.randomUUID(); + ListenableFuture<OpenedSocket> pendingServerConnection = service.getConnection(id); + assertFalse(pendingServerConnection.isDone()); + service.stopAsync().awaitTerminated(); + assertTrue(pendingServerConnection.isDone()); + + try { + pendingServerConnection.get(); + fail(); + } catch (ExecutionException e) { + assertEquals("The socket has been closed", e.getCause().getMessage()); + } + + try { + service.getConnection(UUID.randomUUID()); + fail(); + } catch (IllegalStateException expected) {} + } + + private OpenedSocket openClientConnection() throws IOException { + return OpenedSocket.fromSocket(new Socket(InetAddress.getLoopbackAddress(), port)); + } + + /** + * Opens a connection to the service and identifies itself using the id. + */ + private OpenedSocket openConnectionAndIdentify(UUID id) throws IOException { + OpenedSocket clientSocket = openClientConnection(); + OpenedSocket.Writer writer = clientSocket.writer(); + writer.write(new StartupAnnounceMessage(id)); + writer.flush(); + return clientSocket; + } + + private void assertEndsConnected(OpenedSocket clientSocket, OpenedSocket serverSocket) + throws IOException { + serverSocket.writer().write("hello client!"); + serverSocket.writer().flush(); // necessary to prevent deadlock + assertEquals("hello client!", clientSocket.reader().read()); + + clientSocket.writer().write("hello server!"); + clientSocket.writer().flush(); // ditto + assertEquals("hello server!", serverSocket.reader().read()); + } +} diff --git a/caliper/src/test/java/com/google/caliper/runner/StreamServiceTest.java b/caliper/src/test/java/com/google/caliper/runner/StreamServiceTest.java new file mode 100644 index 0000000..f20e72a --- /dev/null +++ b/caliper/src/test/java/com/google/caliper/runner/StreamServiceTest.java @@ -0,0 +1,252 @@ +/* + * Copyright (C) 2013 Google Inc. + * + * 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.google.caliper.runner; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkState; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotSame; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import com.google.caliper.bridge.LogMessage; +import com.google.caliper.bridge.OpenedSocket; +import com.google.caliper.runner.FakeWorkers.DummyLogMessage; +import com.google.caliper.runner.StreamService.StreamItem; +import com.google.caliper.runner.StreamService.StreamItem.Kind; +import com.google.caliper.util.Parser; +import com.google.common.collect.Sets; +import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.ListenableFutureTask; +import com.google.common.util.concurrent.MoreExecutors; +import com.google.common.util.concurrent.Service.Listener; +import com.google.common.util.concurrent.Service.State; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.net.ServerSocket; +import java.net.SocketException; +import java.text.ParseException; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.Callable; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +/** + * Tests for {@link StreamService}. + */ +@RunWith(JUnit4.class) + +public class StreamServiceTest { + + private ServerSocket serverSocket; + private final StringWriter writer = new StringWriter(); + private final PrintWriter stdout = new PrintWriter(writer, true); + private final Parser<LogMessage> parser = new Parser<LogMessage>() { + @Override public LogMessage parse(final CharSequence text) throws ParseException { + return new DummyLogMessage(text.toString()); + } + }; + + private StreamService service; + private final CountDownLatch terminalLatch = new CountDownLatch(1); + private static final int TRIAL_NUMBER = 3; + + @Before public void setUp() throws IOException { + serverSocket = new ServerSocket(0); + } + + @After public void closeSocket() throws IOException { + serverSocket.close(); + } + + @After public void stopService() { + if (service != null && service.state() != State.FAILED && service.state() != State.TERMINATED) { + service.stopAsync().awaitTerminated(); + } + } + + @Test public void testReadOutput() throws Exception { + makeService(FakeWorkers.PrintClient.class, "foo", "bar"); + service.startAsync().awaitRunning(); + StreamItem item1 = readItem(); + assertEquals(Kind.DATA, item1.kind()); + Set<String> lines = Sets.newHashSet(); + lines.add(item1.content().toString()); + StreamItem item2 = readItem(); + assertEquals(Kind.DATA, item2.kind()); + lines.add(item2.content().toString()); + assertEquals(Sets.newHashSet("foo", "bar"), lines); + assertEquals(State.RUNNING, service.state()); + StreamItem item3 = readItem(); + assertEquals(Kind.EOF, item3.kind()); + awaitStopped(100, TimeUnit.MILLISECONDS); + assertTerminated(); + } + + @Test public void failingProcess() throws Exception { + makeService(FakeWorkers.Exit.class, "1"); + service.startAsync().awaitRunning(); + assertEquals(Kind.EOF, readItem().kind()); + awaitStopped(100, TimeUnit.MILLISECONDS); + assertEquals(State.FAILED, service.state()); + } + + @Test public void processDoesntExit() throws Exception { + // close all fds and then sleep + makeService(FakeWorkers.CloseAndSleep.class); + service.startAsync().awaitRunning(); + assertEquals(Kind.EOF, readItem().kind()); + awaitStopped(200, TimeUnit.MILLISECONDS); // we + assertEquals(State.FAILED, service.state()); + } + + @Test public void testSocketInputOutput() throws Exception { + int localport = serverSocket.getLocalPort(); + // read from the socket and echo it back + makeService(FakeWorkers.SocketEchoClient.class, Integer.toString(localport)); + + service.startAsync().awaitRunning(); + assertEquals(new DummyLogMessage("start"), readItem().content()); + service.sendMessage(new DummyLogMessage("hello socket world")); + assertEquals(new DummyLogMessage("hello socket world"), readItem().content()); + service.closeWriter(); + assertEquals(State.RUNNING, service.state()); + StreamItem nextItem = readItem(); + assertEquals("Expected EOF " + nextItem, Kind.EOF, nextItem.kind()); + awaitStopped(100, TimeUnit.MILLISECONDS); + assertTerminated(); + } + + @Test public void testSocketClosesBeforeProcess() throws Exception { + int localport = serverSocket.getLocalPort(); + // read from the socket and echo it back + makeService(FakeWorkers.SocketEchoClient.class, Integer.toString(localport), "foo"); + service.startAsync().awaitRunning(); + assertEquals(new DummyLogMessage("start"), readItem().content()); + service.sendMessage(new DummyLogMessage("hello socket world")); + assertEquals(new DummyLogMessage("hello socket world"), readItem().content()); + service.closeWriter(); + + assertEquals("foo", readItem().content().toString()); + + assertEquals(State.RUNNING, service.state()); + assertEquals(Kind.EOF, readItem().kind()); + awaitStopped(100, TimeUnit.MILLISECONDS); + assertTerminated(); + } + + @Test public void failsToAcceptConnection() throws Exception { + serverSocket.close(); // This will force serverSocket.accept to throw a SocketException + makeService(FakeWorkers.Sleeper.class, Long.toString(TimeUnit.MINUTES.toMillis(10))); + try { + service.startAsync().awaitRunning(); + fail(); + } catch (IllegalStateException expected) {} + assertEquals(SocketException.class, service.failureCause().getClass()); + } + + /** Reads an item, asserting that there was no timeout. */ + private StreamItem readItem() throws InterruptedException { + StreamItem item = service.readItem(10, TimeUnit.SECONDS); + assertNotSame("Timed out while reading item from worker", Kind.TIMEOUT, item.kind()); + return item; + } + + /** + * Wait for the service to reach a terminal state without calling stop. + */ + private void awaitStopped(long time, TimeUnit unit) throws InterruptedException { + assertTrue(terminalLatch.await(time, unit)); + } + + private void assertTerminated() { + State state = service.state(); + if (state != State.TERMINATED) { + if (state == State.FAILED) { + throw new AssertionError(service.failureCause()); + } + fail("Expected service to be terminated but was: " + state); + } + } + + @SuppressWarnings("resource") + private void makeService(Class<?> main, String ...args) { + checkState(service == null, "You can only make one StreamService per test"); + UUID trialId = UUID.randomUUID(); + TrialOutputLogger trialOutput = new TrialOutputLogger(new TrialOutputFactory() { + @Override public FileAndWriter getTrialOutputFile(int trialNumber) + throws FileNotFoundException { + checkArgument(trialNumber == TRIAL_NUMBER); + return new FileAndWriter(new File("/tmp/not-a-file"), stdout); + } + + @Override public void persistFile(File f) { + throw new UnsupportedOperationException(); + } + + }, TRIAL_NUMBER, trialId, null /* experiment */); + try { + // normally the TrialRunLoop opens/closes the logger + trialOutput.open(); + } catch (IOException e) { + throw new RuntimeException(e); + } + service = new StreamService( + new WorkerProcess(FakeWorkers.createProcessBuilder(main, args), + trialId, + getSocketFuture(), + new RuntimeShutdownHookRegistrar()), + parser, + trialOutput); + service.addListener(new Listener() { + @Override public void starting() {} + @Override public void running() {} + @Override public void stopping(State from) {} + @Override public void terminated(State from) { + terminalLatch.countDown(); + } + @Override public void failed(State from, Throwable failure) { + terminalLatch.countDown(); + } + }, MoreExecutors.directExecutor()); + } + + private ListenableFuture<OpenedSocket> getSocketFuture() { + ListenableFutureTask<OpenedSocket> openSocketTask = ListenableFutureTask.create( + new Callable<OpenedSocket>() { + @Override + public OpenedSocket call() throws Exception { + return OpenedSocket.fromSocket(serverSocket.accept()); + } + }); + // N.B. this thread will block on serverSocket.accept until a connection is accepted or the + // socket is closed, so no matter what this thread will die with the test. + Thread opener = new Thread(openSocketTask, "SocketOpener"); + opener.setDaemon(true); + opener.start(); + return openSocketTask; + } +} diff --git a/caliper/src/test/java/com/google/caliper/runner/WorkerProcessTest.java b/caliper/src/test/java/com/google/caliper/runner/WorkerProcessTest.java new file mode 100644 index 0000000..397f7d8 --- /dev/null +++ b/caliper/src/test/java/com/google/caliper/runner/WorkerProcessTest.java @@ -0,0 +1,174 @@ +/* + * Copyright (C) 2012 Google Inc. + * + * 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.google.caliper.runner; + +import static java.util.concurrent.TimeUnit.MINUTES; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import com.google.caliper.Benchmark; +import com.google.caliper.config.VmConfig; +import com.google.caliper.model.BenchmarkSpec; +import com.google.caliper.platform.jvm.JvmPlatform; +import com.google.caliper.worker.WorkerMain; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Iterables; +import com.google.common.collect.Sets; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +import java.io.File; +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.List; +import java.util.Set; +import java.util.UUID; + +/** + * Tests {@link WorkerProcess}. + * + * <p>TODO(lukes,gak): write more tests for how our specs get turned into commandlines + */ + +@RunWith(JUnit4.class) +public class WorkerProcessTest { + private static final int PORT_NUMBER = 4004; + private static final UUID TRIAL_ID = UUID.randomUUID(); + + private static class MockRegistrar implements ShutdownHookRegistrar { + Set<Thread> hooks = Sets.newHashSet(); + @Override public void addShutdownHook(Thread hook) { + hooks.add(hook); + } + @Override public boolean removeShutdownHook(Thread hook) { + return hooks.remove(hook); + } + } + + private final MockRegistrar registrar = new MockRegistrar(); + private BenchmarkClass benchmarkClass; + + @Before public void setUp() throws InvalidBenchmarkException { + benchmarkClass = BenchmarkClass.forClass(TestBenchmark.class); + } + + @Test public void simpleArgsTest() throws Exception { + Method method = TestBenchmark.class.getDeclaredMethods()[0]; + AllocationInstrument allocationInstrument = new AllocationInstrument(); + allocationInstrument.setOptions(ImmutableMap.of("trackAllocations", "true")); + VmConfig vmConfig = new VmConfig( + new File("foo"), + Arrays.asList("--doTheHustle"), + new File("java"), + new JvmPlatform()); + Experiment experiment = new Experiment( + allocationInstrument.createInstrumentation(method), + ImmutableMap.<String, String>of(), + new VirtualMachine("foo-jvm", vmConfig)); + BenchmarkSpec spec = new BenchmarkSpec.Builder() + .className(TestBenchmark.class.getName()) + .methodName(method.getName()) + .build(); + ProcessBuilder builder = createProcess(experiment, spec); + List<String> commandLine = builder.command(); + assertEquals(new File("java").getAbsolutePath(), commandLine.get(0)); + assertEquals("--doTheHustle", commandLine.get(1)); // vm specific flags come next + assertEquals("-cp", commandLine.get(2)); // then the classpath + // should we assert on classpath contents? + ImmutableSet<String> extraCommandLineArgs = + allocationInstrument.getExtraCommandLineArgs(vmConfig); + assertEquals(extraCommandLineArgs.asList(), + commandLine.subList(4, 4 + extraCommandLineArgs.size())); + int index = 4 + extraCommandLineArgs.size(); + assertEquals("-XX:+PrintFlagsFinal", commandLine.get(index)); + assertEquals("-XX:+PrintCompilation", commandLine.get(++index)); + assertEquals("-XX:+PrintGC", commandLine.get(++index)); + assertEquals(WorkerMain.class.getName(), commandLine.get(++index)); + // followed by worker args... + } + + @Test public void shutdownHook_waitFor() throws Exception { + Process worker = createWorkerProcess(FakeWorkers.Exit.class, "0").startWorker(); + assertEquals("worker-shutdown-hook-" + TRIAL_ID, + Iterables.getOnlyElement(registrar.hooks).getName()); + worker.waitFor(); + assertTrue(registrar.hooks.isEmpty()); + } + + @Test public void shutdownHook_exitValueThrows() throws Exception { + Process worker = createWorkerProcess( + FakeWorkers.Sleeper.class, Long.toString(MINUTES.toMillis(1))).startWorker(); + try { + Thread hook = Iterables.getOnlyElement(registrar.hooks); + assertEquals("worker-shutdown-hook-" + TRIAL_ID, hook.getName()); + try { + worker.exitValue(); + fail(); + } catch (IllegalThreadStateException expected) {} + assertTrue(registrar.hooks.contains(hook)); + } finally { + worker.destroy(); // clean up + } + } + + @Test public void shutdownHook_exitValue() throws Exception { + Process worker = createWorkerProcess(FakeWorkers.Exit.class, "0").startWorker(); + while (true) { + try { + worker.exitValue(); + assertTrue(registrar.hooks.isEmpty()); + break; + } catch (IllegalThreadStateException e) { + Thread.sleep(10); // keep polling + } + } + } + + @Test public void shutdownHook_destroy() throws Exception { + Process worker = createWorkerProcess( + FakeWorkers.Sleeper.class, Long.toString(MINUTES.toMillis(1))).startWorker(); + worker.destroy(); + assertTrue(registrar.hooks.isEmpty()); + } + + static final class TestBenchmark { + @Benchmark long thing(long reps) { + long dummy = 0; + for (long i = 0; i < reps; i++) { + dummy += new Long(dummy).hashCode(); + } + return dummy; + } + } + + private ProcessBuilder createProcess(Experiment experiment, BenchmarkSpec benchmarkSpec) { + return WorkerProcess.buildProcess(TRIAL_ID, experiment, benchmarkSpec, PORT_NUMBER, + benchmarkClass); + } + + private WorkerProcess createWorkerProcess(Class<?> main, String ...args) { + return new WorkerProcess(FakeWorkers.createProcessBuilder(main, args), + TRIAL_ID, + null, + registrar); + } +} diff --git a/caliper/src/test/java/com/google/caliper/util/LinearTranslationTest.java b/caliper/src/test/java/com/google/caliper/util/LinearTranslationTest.java new file mode 100644 index 0000000..4ae875b --- /dev/null +++ b/caliper/src/test/java/com/google/caliper/util/LinearTranslationTest.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2010 Google Inc. + * + * 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.google.caliper.util; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@RunWith(JUnit4.class) +public class LinearTranslationTest { + private static final double CLOSE_ENOUGH = 1.0E-13; + + @Test public void linearTranslation() { + LinearTranslation ctof = new LinearTranslation(0, 32, 100, 212); + assertEquals(32, ctof.translate(0), CLOSE_ENOUGH); + assertEquals(212, ctof.translate(100), CLOSE_ENOUGH); + assertEquals(98.6, ctof.translate(37), CLOSE_ENOUGH); + assertEquals(-40, ctof.translate(-40), CLOSE_ENOUGH); + + LinearTranslation reversed = new LinearTranslation(5, 42, 69, 0); + assertEquals(-21, reversed.translate(101), CLOSE_ENOUGH); + } +} diff --git a/caliper/src/test/java/com/google/caliper/util/ShortDurationTest.java b/caliper/src/test/java/com/google/caliper/util/ShortDurationTest.java new file mode 100644 index 0000000..0c32fc8 --- /dev/null +++ b/caliper/src/test/java/com/google/caliper/util/ShortDurationTest.java @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2011 Google Inc. + * + * 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.google.caliper.util; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +import java.util.concurrent.TimeUnit; + +@RunWith(JUnit4.class) +public class ShortDurationTest { + @Test public void valueOf() { + assertEquals(ShortDuration.zero(), ShortDuration.valueOf("0")); + testIt(0, "0ns", "0s"); + testIt(0, "0 ns", "0s"); + testIt(0, "0nanos", "0s"); + testIt(0, "0nanoseconds", "0s"); + testIt(0, "0 ms", "0s"); + testIt(0, "0us", "0s"); + testIt(0, "1e-12 ms", "0s"); + testIt(1, "0.501 ns", "0.501ns"); + testIt(1000, "1 \u03bcs", "1\u03bcs"); + // testIt(500, "0.5us", "500ns"); + // testIt(499, "0.499000000000000000000000000000001us", "499ns"); + // testIt(500, "0.49995 us", "500ns"); + // testIt(60480000000000L, "0.7 days", "16.80h"); + // testIt(Long.MAX_VALUE, "106751.99116730064591 days", "106751.99d"); + } + + @Test public void tooLongForALong() { + try { + ShortDuration.valueOf("106751.99116730064592 days"); + fail(); + } catch (ArithmeticException expected) { + } + } + + private static void testIt(long i, String s, String p) { + ShortDuration d = ShortDuration.valueOf(s); + assertEquals(i, d.to(TimeUnit.NANOSECONDS)); + assertEquals(p, d.toString()); + } +} diff --git a/caliper/src/test/java/com/google/caliper/worker/RuntimeWorkerTest.java b/caliper/src/test/java/com/google/caliper/worker/RuntimeWorkerTest.java new file mode 100644 index 0000000..0d75d3b --- /dev/null +++ b/caliper/src/test/java/com/google/caliper/worker/RuntimeWorkerTest.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2013 Google Inc. + * + * 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.google.caliper.worker; + +import static com.google.caliper.worker.RuntimeWorker.INITIAL_REPS; +import static com.google.caliper.worker.RuntimeWorker.calculateTargetReps; +import static java.util.concurrent.TimeUnit.HOURS; +import static java.util.concurrent.TimeUnit.MILLISECONDS; +import static java.util.concurrent.TimeUnit.NANOSECONDS; +import static java.util.concurrent.TimeUnit.SECONDS; +import static org.junit.Assert.assertEquals; + +import com.google.caliper.util.ShortDuration; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +import java.math.BigDecimal; + +/** + * Tests {@link RuntimeWorker}. + */ +@RunWith(JUnit4.class) +public class RuntimeWorkerTest { + private static final ShortDuration TIMING_INTERVAL = ShortDuration.of(100, MILLISECONDS); + + @Test public void testCalculateTargetReps_tinyBenchmark() { + // this is one cycle on a 5GHz machine + ShortDuration oneCycle = ShortDuration.of(new BigDecimal("2.0e-10"), SECONDS); + long targetReps = calculateTargetReps(INITIAL_REPS, + oneCycle.times(INITIAL_REPS).to(NANOSECONDS), TIMING_INTERVAL.to(NANOSECONDS), 0.0); + long expectedReps = TIMING_INTERVAL.toPicos() / oneCycle.toPicos(); + assertEquals(expectedReps, targetReps); + } + + @Test public void testCalculateTargetReps_hugeBenchmark() { + long targetReps = + calculateTargetReps(INITIAL_REPS, HOURS.toNanos(1), TIMING_INTERVAL.to(NANOSECONDS), 0.0); + assertEquals(1, targetReps); + } + + @Test public void testCalculateTargetReps_applyRandomness() { + long targetReps = calculateTargetReps(INITIAL_REPS, MILLISECONDS.toNanos(100), + TIMING_INTERVAL.to(NANOSECONDS), 0.5); + assertEquals(110, targetReps); + } +} diff --git a/caliper/src/test/resources/com/google/caliper/bridge/jdk6-compilation.txt b/caliper/src/test/resources/com/google/caliper/bridge/jdk6-compilation.txt new file mode 100644 index 0000000..2227fb1 --- /dev/null +++ b/caliper/src/test/resources/com/google/caliper/bridge/jdk6-compilation.txt @@ -0,0 +1,288 @@ + 1 java.lang.String::hashCode (60 bytes) + 2 java.lang.String::lastIndexOf (156 bytes) + 3 java.lang.String::indexOf (151 bytes) + 4 sun.nio.cs.UTF_8$Decoder::decodeArrayLoop (553 bytes) + 5 java.io.UnixFileSystem::normalize (75 bytes) + 6 sun.nio.cs.UTF_8$Encoder::encodeArrayLoop (490 bytes) + 7 java.lang.String::indexOf (166 bytes) + 1% sun.net.www.ParseUtil::encodePath @ 29 (336 bytes) + 8 java.lang.String::equals (88 bytes) + 9 sun.net.www.ParseUtil::encodePath (336 bytes) + 10 java.lang.String::charAt (33 bytes) + 11 java.util.Arrays::binarySearch0 (72 bytes) + 12 com.google.common.base.CharMatcher$10::matches (17 bytes) + 13 java.util.Arrays::binarySearch (9 bytes) + 2% com.google.common.base.CharMatcher::setBits @ 3 (28 bytes) + 14 com.google.common.base.CharMatcher::setBits (28 bytes) + 15 java.lang.Object::<init> (1 bytes) + 16 java.util.zip.ZipFile::ensureOpen (37 bytes) + 17 java.util.zip.ZipFile::access$300 (5 bytes) + 18 java.util.zip.ZipFile::access$100 (5 bytes) + 19 java.util.HashMap::indexFor (6 bytes) + 20 java.util.HashMap::hash (23 bytes) + 21 java.lang.String::<init> (20 bytes) + 22 java.lang.String::substring (83 bytes) + 23 java.util.HashMap::get (79 bytes) + 24 java.lang.String::lastIndexOf (12 bytes) +--- n java.util.zip.ZipFile::freeEntry (static) +--- n java.util.zip.ZipEntry::initFields + 25 java.util.zip.ZipFile::access$800 (6 bytes) + 26 java.util.jar.JarFile$JarFileEntry::<init> (11 bytes) + 27 java.util.jar.JarEntry::<init> (6 bytes) +--- n java.util.zip.ZipFile::getNextEntry (static) + 28 java.util.zip.ZipEntry::<init> (102 bytes) + 29 java.util.jar.JarFile$1::hasMoreElements (10 bytes) + 30 ! java.util.zip.ZipFile$2::hasMoreElements (41 bytes) + 31 sun.misc.URLClassPath::getResourceMapKey (55 bytes) + 32 java.util.jar.JarFile$1::nextElement (5 bytes) + 33 java.util.jar.JarFile$1::nextElement (26 bytes) + 34 java.util.zip.ZipFile$2::nextElement (5 bytes) + 35 ! java.util.zip.ZipFile$2::nextElement (211 bytes) + 36 java.util.zip.ZipFile::access$400 (6 bytes) + 37 java.util.zip.ZipEntry::<init> (43 bytes) + 38 sun.misc.URLClassPath$JarLoader::addJarEntriesToEntryMap (202 bytes) + 39 java.util.ArrayList::get (11 bytes) + 40 java.util.ArrayList::rangeCheck (22 bytes) + 41 java.util.ArrayList::elementData (7 bytes) + 23 made not entrant (2) java.util.HashMap::get (79 bytes) + 42 java.lang.String::replace (142 bytes) + 43 java.util.Properties$LineReader::readLine (452 bytes) + 44 java.lang.String::startsWith (78 bytes) +--- n java.lang.System::arraycopy (static) + 45 java.util.HashMap::get (79 bytes) + 46 java.io.DataOutputStream::writeUTF (435 bytes) + 47 ! sun.reflect.generics.parser.SignatureParser::current (40 bytes) + 48 java.io.DataOutputStream::incCount (20 bytes) + 49 s java.io.ByteArrayOutputStream::write (55 bytes) + 50 java.lang.CharacterData::of (120 bytes) + 51 java.lang.CharacterDataLatin1::getProperties (11 bytes) + 52 java.lang.String::getChars (66 bytes) + 53 java.lang.Math::min (11 bytes) + 54 java.lang.AbstractStringBuilder::append (40 bytes) + 55 java.util.jar.Manifest$FastInputStream::readLine (167 bytes) + 56 net.sf.cglib.asm.ByteVector::putUTF8 (394 bytes) + 57 net.sf.cglib.asm.Type::a (253 bytes) + 58 s java.lang.StringBuffer::append (8 bytes) + 59 net.sf.cglib.asm.Type::a (214 bytes) + 60 net.sf.cglib.asm.Type::getArgumentTypes (131 bytes) + 61 java.util.Arrays::copyOfRange (63 bytes) + 62 java.lang.AbstractStringBuilder::append (60 bytes) + 63 java.lang.String::<init> (72 bytes) + 64 java.lang.Character::isWhitespace (5 bytes) + 65 java.lang.Character::isWhitespace (9 bytes) + 66 java.lang.CharacterDataLatin1::isWhitespace (23 bytes) + 67 sun.reflect.generics.parser.SignatureParser::parseIdentifier (115 bytes) + 68 java.lang.StringBuilder::append (8 bytes) + 43 made not entrant (2) java.util.Properties$LineReader::readLine (452 bytes) + 69 java.lang.StringBuilder::append (8 bytes) + 3% java.util.Properties$LineReader::readLine @ 21 (452 bytes) + 70 java.util.HashMap::put (126 bytes) + 71 sun.reflect.generics.parser.SignatureParser::advance (37 bytes) + 72 java.util.zip.ZStreamRef::address (5 bytes) + 73 java.lang.String::compareTo (150 bytes) + 74 sun.misc.MetaIndex::mayContain (51 bytes) + 75 java.lang.String::startsWith (7 bytes) + 23 made zombie (2) java.util.HashMap::get (79 bytes) + 76 java.lang.AbstractStringBuilder::<init> (12 bytes) + 77 java.util.ArrayList$Itr::hasNext (20 bytes) + 78 java.util.zip.InflaterInputStream::ensureOpen (18 bytes) + 79 java.util.HashMap::transfer (83 bytes) + 80 java.util.ArrayList$Itr::checkForComodification (23 bytes) + 81 java.util.ArrayList$Itr::next (66 bytes) + 82 sun.reflect.ClassFileAssembler::emitByte (11 bytes) + 83 sun.reflect.ByteVectorImpl::add (38 bytes) + 84 java.util.ArrayList::ensureCapacity (58 bytes) + 85 java.util.ArrayList::add (29 bytes) + 86 java.util.HashMap$HashIterator::nextEntry (99 bytes) + 87 java.lang.Class::searchMethods (90 bytes) + 88 java.util.HashMap$HashIterator::<init> (63 bytes) + 89 java.lang.reflect.AccessibleObject::<init> (5 bytes) + 43 made zombie (2) java.util.Properties$LineReader::readLine (452 bytes) + 90 sun.reflect.ReflectionFactory::langReflectAccess (15 bytes) + 91 java.lang.Class::copyMethods (36 bytes) + 92 java.lang.reflect.ReflectAccess::copyMethod (5 bytes) + 93 java.lang.reflect.Method::copy (67 bytes) + 94 java.lang.reflect.Method::<init> (68 bytes) + 95 sun.reflect.ReflectionFactory::copyMethod (10 bytes) + 96 java.lang.System::getSecurityManager (4 bytes) + 97 ! com.sun.jersey.core.reflection.ReflectionHelper::findMethodOnClass (94 bytes) + 98 java.util.HashMap$HashIterator::hasNext (13 bytes) + 99 java.lang.StringBuilder::toString (17 bytes) +100 java.lang.reflect.Method::equals (107 bytes) +101 java.lang.ref.SoftReference::get (18 bytes) +--- n java.lang.Object::clone +102 s java.lang.reflect.Method::declaredAnnotations (39 bytes) +103 java.lang.Class::clearCachesOnClassRedefinition (70 bytes) +104 java.lang.Class::checkInitted (19 bytes) +105 com.sun.jersey.core.reflection.AnnotatedMethod::hasParameterAnnotations (80 bytes) +--- n java.lang.Class::getClassLoader0 +106 java.lang.Class$MethodArray::addIfNotPresent (47 bytes) +107 java.lang.Class::getMethod0 (97 bytes) +--- n java.lang.String::intern +108 java.lang.Class::getName (20 bytes) +109 java.util.Collections$EmptyMap::get (2 bytes) +110 java.lang.reflect.Method::getAnnotation (26 bytes) +111 java.util.HashMap$KeyIterator::next (8 bytes) +110 made not entrant (2) java.lang.reflect.Method::getAnnotation (26 bytes) +112 java.lang.Class::privateGetDeclaredMethods (119 bytes) +--- n java.lang.Class::getInterfaces +113 com.sun.jersey.core.reflection.AnnotatedMethod::hasMethodAnnotations (43 bytes) +114 java.lang.StringBuilder::<init> (7 bytes) +115 java.util.AbstractCollection::<init> (5 bytes) +116 java.lang.Character::toLowerCase (9 bytes) +117 java.lang.CharacterDataLatin1::toLowerCase (39 bytes) +--- n java.lang.Class::isInterface +118 java.lang.Class::argumentTypesToString (78 bytes) +119 java.lang.Class::checkMemberAccess (78 bytes) +--- n java.lang.Class::getSuperclass +120 sun.reflect.generics.tree.SimpleClassTypeSignature::make (11 bytes) +121 sun.reflect.generics.tree.SimpleClassTypeSignature::<init> (20 bytes) +122 sun.reflect.generics.parser.SignatureParser::parseClassTypeSignatureSuffix (53 bytes) +123 sun.reflect.generics.parser.SignatureParser::parseSimpleClassTypeSignature (71 bytes) +124 java.util.HashMap$Entry::<init> (26 bytes) +125 sun.reflect.generics.visitor.Reifier::visitClassTypeSignature (381 bytes) +126 java.util.Properties$LineReader::readLine (452 bytes) +127 java.util.Properties::loadConvert (505 bytes) +128 sun.reflect.ClassFileAssembler::emitConstantPoolUTF8 (50 bytes) +129 ! sun.reflect.UTF8::encode (189 bytes) +130 sun.reflect.UTF8::utf8Length (81 bytes) +131 java.util.AbstractList::<init> (10 bytes) +132 java.util.regex.Pattern$BmpCharProperty::match (50 bytes) +133 java.util.regex.Matcher::search (109 bytes) +134 java.util.regex.Pattern$Curly::match0 (174 bytes) +132 made not entrant (2) java.util.regex.Pattern$BmpCharProperty::match (50 bytes) +135 java.lang.Character::charCount (12 bytes) +136 java.lang.Character::isHighSurrogate (18 bytes) +137 java.lang.String::codePointAt (44 bytes) +138 java.lang.Character::codePointAtImpl (41 bytes) +139 java.util.regex.Pattern$Start::match (109 bytes) +140 com.google.common.io.LineBuffer::add (201 bytes) +141 java.util.regex.Pattern$Curly::match (86 bytes) +142 java.util.zip.Inflater::ensureOpen (47 bytes) +143 java.util.regex.Pattern::has (15 bytes) +144 java.util.regex.Matcher::reset (83 bytes) +145 java.util.regex.Pattern$Node::match (27 bytes) +146 java.util.regex.Pattern$CharProperty::match (56 bytes) +147 java.lang.Character::codePointAt (51 bytes) +148 java.util.regex.Pattern$Ctype::isSatisfiedBy (24 bytes) +149 java.util.regex.ASCII::isType (15 bytes) +150 java.util.regex.ASCII::getType (17 bytes) +151 java.util.regex.Pattern$Slice::match (79 bytes) +152 java.util.regex.Pattern$Dot::isSatisfiedBy (34 bytes) +153 java.util.regex.Matcher::match (109 bytes) +154 java.util.regex.Pattern$BmpCharProperty::match (50 bytes) +155 java.util.regex.Pattern$Single::isSatisfiedBy (14 bytes) +--- n java.lang.Thread::currentThread (static) +156 com.google.common.collect.AbstractIndexedListIterator::hasNext (17 bytes) +157 java.lang.String::indexOf (7 bytes) + 70 made not entrant (2) java.util.HashMap::put (126 bytes) +133 made not entrant (2) java.util.regex.Matcher::search (109 bytes) +158 java.util.regex.Matcher::search (109 bytes) +146 made not entrant (2) java.util.regex.Pattern$CharProperty::match (56 bytes) +159 java.lang.String::toUpperCase (442 bytes) +160 java.lang.Character::toUpperCaseEx (30 bytes) +161 java.lang.CharacterDataLatin1::toUpperCaseEx (71 bytes) +110 made zombie (2) java.lang.reflect.Method::getAnnotation (26 bytes) +162 java.nio.Buffer::position (43 bytes) +163 java.nio.charset.CoderResult::isUnderflow (13 bytes) +164 java.nio.Buffer::limit (62 bytes) +165 java.nio.Buffer::<init> (121 bytes) +166 java.nio.charset.CoderResult::isOverflow (14 bytes) +167 java.nio.Buffer::hasRemaining (17 bytes) +168 java.nio.CharBuffer::hasArray (20 bytes) +169 java.nio.ByteBuffer::hasArray (20 bytes) +170 ! java.nio.CharBuffer::wrap (20 bytes) +171 java.nio.HeapCharBuffer::<init> (14 bytes) +172 java.nio.CharBuffer::<init> (22 bytes) +173 ! java.nio.charset.CharsetEncoder::encode (285 bytes) +174 sun.nio.cs.UTF_8$Encoder::encodeLoop (28 bytes) +175 java.nio.Buffer::remaining (10 bytes) +176 sun.nio.cs.StreamEncoder::ensureOpen (18 bytes) +177 sun.nio.cs.StreamEncoder::implWrite (156 bytes) +178 ! sun.nio.cs.StreamEncoder::write (78 bytes) +179 com.google.gson.stream.JsonWriter::string (365 bytes) +180 java.io.OutputStreamWriter::write (9 bytes) +181 sun.nio.cs.StreamEncoder::write (17 bytes) +179 made not entrant (2) com.google.gson.stream.JsonWriter::string (365 bytes) +182 com.google.gson.stream.JsonWriter::string (365 bytes) +183 java.io.StringWriter::write (11 bytes) +184 s java.lang.StringBuffer::append (8 bytes) + 49 made not entrant (2) java.io.ByteArrayOutputStream::write (55 bytes) +185 sun.security.util.Cache$EqualByteArray::hashCode (57 bytes) +186 java.lang.Math::max (11 bytes) + 70 made zombie (2) java.util.HashMap::put (126 bytes) +187 s java.io.ByteArrayInputStream::read (36 bytes) +188 java.math.BigInteger::stripLeadingZeroBytes (132 bytes) +189 s java.io.ByteArrayInputStream::available (10 bytes) +132 made zombie (2) java.util.regex.Pattern$BmpCharProperty::match (50 bytes) +190 sun.security.util.DerInputStream::available (8 bytes) +191 java.io.ByteArrayInputStream::mark (9 bytes) +192 java.io.DataInputStream::readUTF (501 bytes) +193 sun.security.util.DerInputStream::getLength (111 bytes) +194 sun.security.util.ObjectIdentifier::checkValidOid (115 bytes) +195 sun.security.util.DerInputStream::getByte (12 bytes) +196 sun.security.util.ObjectIdentifier::initFromEncoding (206 bytes) +197 sun.security.util.ObjectIdentifier::getComponent (73 bytes) +198 sun.security.util.ObjectIdentifier::equals (69 bytes) +199 sun.security.util.ObjectIdentifier::hashCode (35 bytes) +200 sun.security.util.DerInputStream::<init> (19 bytes) +201 ! java.security.cert.Certificate::hashCode (34 bytes) +202 java.lang.String::toLowerCase (436 bytes) +203 java.lang.Character::toLowerCase (6 bytes) +204 java.util.HashMap::addEntry (58 bytes) +205 s java.io.ByteArrayOutputStream::write (55 bytes) +206 java.io.BufferedInputStream::getBufIfOpen (21 bytes) +207 s java.io.BufferedInputStream::read (49 bytes) +208 java.io.DataInputStream::readChar (40 bytes) + 4% sun.text.normalizer.NormalizerDataReader::read @ 12 (86 bytes) +208 made not entrant (2) java.io.DataInputStream::readChar (40 bytes) +209 java.io.DataInputStream::readChar (40 bytes) +210 java.math.BigInteger::mulAdd (81 bytes) + 5% com.sun.crypto.provider.AESCrypt::<clinit> @ 724 (1577 bytes) + 50 made not entrant (2) java.lang.CharacterData::of (120 bytes) + 64 made not entrant (2) java.lang.Character::isWhitespace (5 bytes) + 65 made not entrant (2) java.lang.Character::isWhitespace (9 bytes) + 67 made not entrant (2) sun.reflect.generics.parser.SignatureParser::parseIdentifier (115 bytes) +116 made not entrant (2) java.lang.Character::toLowerCase (9 bytes) +160 made not entrant (2) java.lang.Character::toUpperCaseEx (30 bytes) +203 made not entrant (2) java.lang.Character::toLowerCase (6 bytes) +159 made not entrant (2) java.lang.String::toUpperCase (442 bytes) +202 made not entrant (2) java.lang.String::toLowerCase (436 bytes) +211 java.lang.CharacterData::of (120 bytes) +212 java.lang.String::toLowerCase (436 bytes) +213 java.lang.String::toUpperCase (442 bytes) +201 made not entrant (2) java.security.cert.Certificate::hashCode (34 bytes) +198 made not entrant (2) sun.security.util.ObjectIdentifier::equals (69 bytes) +214 java.lang.Character::toUpperCaseEx (30 bytes) +146 made zombie (2) java.util.regex.Pattern$CharProperty::match (56 bytes) +133 made zombie (2) java.util.regex.Matcher::search (109 bytes) +179 made zombie (2) com.google.gson.stream.JsonWriter::string (365 bytes) +215 java.lang.Character::toLowerCase (9 bytes) +216 java.lang.Character::isWhitespace (5 bytes) +217 java.lang.Character::isWhitespace (9 bytes) +218 sun.text.normalizer.NormalizerImpl::decompose (680 bytes) +219 ! sun.security.x509.AVA::toRFC2253CanonicalString (484 bytes) +220 java.lang.String::regionMatches (157 bytes) +221 sun.security.provider.SHA::implCompress (491 bytes) +--- n sun.misc.Unsafe::getInt + 49 made zombie (2) java.io.ByteArrayOutputStream::write (55 bytes) + 6% com.sun.crypto.provider.ARCFOURCipher::crypt @ 15 (129 bytes) +208 made zombie (2) java.io.DataInputStream::readChar (40 bytes) +222 com.sun.crypto.provider.ARCFOURCipher::crypt (129 bytes) +223 java.lang.Integer::reverseBytes (26 bytes) + 45 made not entrant (2) java.util.HashMap::get (79 bytes) + 50 made zombie (2) java.lang.CharacterData::of (120 bytes) +224 java.util.HashMap$EntryIterator::next (5 bytes) +225 java.util.HashMap$EntryIterator::next (5 bytes) + 64 made zombie (2) java.lang.Character::isWhitespace (5 bytes) + 65 made zombie (2) java.lang.Character::isWhitespace (9 bytes) + 67 made zombie (2) sun.reflect.generics.parser.SignatureParser::parseIdentifier (115 bytes) +116 made zombie (2) java.lang.Character::toLowerCase (9 bytes) +226 java.util.HashMap$Entry::hashCode (38 bytes) +227 java.util.AbstractMap::hashCode (41 bytes) +160 made zombie (2) java.lang.Character::toUpperCaseEx (30 bytes) +198 made zombie (2) sun.security.util.ObjectIdentifier::equals (69 bytes) +203 made zombie (2) java.lang.Character::toLowerCase (6 bytes) +201 made zombie (2) java.security.cert.Certificate::hashCode (34 bytes) +159 made zombie (2) java.lang.String::toUpperCase (442 bytes) +202 made zombie (2) java.lang.String::toLowerCase (436 bytes)
\ No newline at end of file diff --git a/caliper/src/test/resources/com/google/caliper/bridge/jdk6-flags.txt b/caliper/src/test/resources/com/google/caliper/bridge/jdk6-flags.txt new file mode 100644 index 0000000..8a94e73 --- /dev/null +++ b/caliper/src/test/resources/com/google/caliper/bridge/jdk6-flags.txt @@ -0,0 +1,770 @@ + bool AHRByDeathCollectTimeRatio = false {product} + bool AHRByMinorPauseTimeMajorFreq = false {product} + bool AHRByPromoToAllocRatio = false {product} + bool AHRBySurvivorAge = false {product} +uintx AHRIncrementSize = 5242880 {product} +uintx AHRMaxDeathCollectTimeRatio = 55 {product} +uintx AHRMaxMinorInvocationsPerMajor = 30 {product} +uintx AHRMaxMinorPauseTimeMillis = 100 {product} +uintx AHRMaxPromoToAllocRatio = 25 {product} +uintx AHRMaxRatio = 100 {product} +uintx AHRMaxSize = 104857600 {product} +uintx AHRMaxSurvivorAge = 10 {product} +uintx AHRMinDeathCollectTimeRatio = 50 {product} +uintx AHRMinMinorInvocationsPerMajor = 5 {product} +uintx AHRMinMinorPauseTimeMillis = 150 {product} +uintx AHRMinPromoToAllocRatio = 2 {product} +uintx AHRMinSurvivorAge = 2 {product} + bool AdaptiveHeapRebalance = false {product} +uintx AdaptivePermSizeWeight = 20 {product} +uintx AdaptiveSizeDecrementScaleFactor = 4 {product} +uintx AdaptiveSizeMajorGCDecayTimeScale = 10 {product} +uintx AdaptiveSizePausePolicy = 0 {product} +uintx AdaptiveSizePolicyCollectionCostMargin = 50 {product} +uintx AdaptiveSizePolicyInitializingSteps = 20 {product} +uintx AdaptiveSizePolicyOutputInterval = 0 {product} +uintx AdaptiveSizePolicyWeight = 10 {product} +uintx AdaptiveSizeThroughPutPolicy = 0 {product} +uintx AdaptiveTimeWeight = 25 {product} + bool AdjustConcurrency = false {product} + bool AggressiveOpts = false {product} + intx AliasLevel = 3 {product} + intx AllocatePrefetchDistance = 192 {product} + intx AllocatePrefetchInstr = 0 {product} + intx AllocatePrefetchLines = 4 {product} + intx AllocatePrefetchStepSize = 64 {product} + intx AllocatePrefetchStyle = 1 {product} + bool AllowJNIEnvProxy = false {product} + bool AllowParallelDefineClass = false {product} + bool AllowUserSignalHandlers = false {product} + bool AlwaysActAsServerClassMachine = false {product} + bool AlwaysCompileLoopMethods = false {product} + intx AlwaysInflate = 0 {product} + bool AlwaysLockClassLoader = false {product} + bool AlwaysPreTouch = false {product} + bool AlwaysRestoreFPU = false {product} + bool AlwaysTenure = false {product} + bool AnonymousClasses = false {product} + bool AssertOnSuspendWaitFailure = false {product} + intx Atomics = 0 {product} + intx AutoBoxCacheMax = 128 {C2 product} +uintx AutoGCSelectPauseMillis = 5000 {product} + intx BCEATraceLevel = 0 {product} + intx BackEdgeThreshold = 100000 {pd product} + bool BackgroundCompilation = true {pd product} +uintx BaseFootPrintEstimate = 268435456 {product} + intx BiasedLockingBulkRebiasThreshold = 20 {product} + intx BiasedLockingBulkRevokeThreshold = 40 {product} + intx BiasedLockingDecayTime = 25000 {product} + intx BiasedLockingStartupDelay = 4000 {product} + bool BindCMSThreadToCPU = false {diagnostic} + bool BindGCTaskThreadsToCPUs = false {product} + bool BlockLayoutByFrequency = true {C2 product} + intx BlockLayoutMinDiamondPercentage = 20 {C2 product} + bool BlockLayoutRotateLoops = true {C2 product} + bool BlockOffsetArrayUseUnallocatedBlock = true {product} + bool BranchOnRegister = false {C2 product} + bool BytecodeVerificationLocal = false {product} + bool BytecodeVerificationRemote = true {product} + intx CICompilerCount = 2 {product} + bool CICompilerCountPerCPU = false {product} + bool CITime = false {product} + bool CMSAbortSemantics = false {product} +uintx CMSAbortablePrecleanMinWorkPerIteration = 100 {product} + intx CMSAbortablePrecleanWaitMillis = 100 {product} +uintx CMSBitMapYieldQuantum = 10485760 {product} +uintx CMSBootstrapOccupancy = 50 {product} + bool CMSClassUnloadingEnabled = false {product} +uintx CMSClassUnloadingMaxInterval = 0 {product} + bool CMSCleanOnEnter = true {product} + bool CMSCompactWhenClearAllSoftRefs = true {product} +uintx CMSConcMarkMultiple = 32 {product} + bool CMSConcurrentMTEnabled = true {product} +uintx CMSCoordinatorYieldSleepCount = 10 {product} + bool CMSDumpAtPromotionFailure = false {product} +uintx CMSExpAvgFactor = 50 {product} + bool CMSExtrapolateSweep = false {product} +uintx CMSFullGCsBeforeCompaction = 0 {product} +uintx CMSIncrementalDutyCycle = 10 {product} +uintx CMSIncrementalDutyCycleMin = 0 {product} + bool CMSIncrementalMode = false {product} +uintx CMSIncrementalOffset = 0 {product} + bool CMSIncrementalPacing = true {product} +uintx CMSIncrementalSafetyFactor = 10 {product} +uintx CMSIndexedFreeListReplenish = 4 {product} + intx CMSInitiatingOccupancyFraction = -1 {product} + intx CMSInitiatingOccupancyFractionDelta = -1 {product} + intx CMSInitiatingPermOccupancyFraction = -1 {product} + intx CMSInitiatingPermOccupancyFractionDelta = -1 {product} + intx CMSInitiatingRamLimitFraction = -1 {product} + intx CMSIsTooFullPercentage = 98 {product} +double CMSLargeCoalSurplusPercent = {product} +double CMSLargeSplitSurplusPercent = {product} + bool CMSLoopWarn = false {product} +uintx CMSMaxAbortablePrecleanLoops = 0 {product} + intx CMSMaxAbortablePrecleanTime = 5000 {product} +uintx CMSOldPLABMax = 1024 {product} +uintx CMSOldPLABMin = 16 {product} +uintx CMSOldPLABNumRefills = 4 {product} +uintx CMSOldPLABReactivityCeiling = 10 {product} +uintx CMSOldPLABReactivityFactor = 2 {product} + bool CMSOldPLABResizeQuicker = false {product} +uintx CMSOldPLABToleranceFactor = 4 {product} + bool CMSPLABRecordAlways = true {product} +uintx CMSParPromoteBlocksToClaim = 16 {product} + bool CMSParallelRemarkEnabled = true {product} + bool CMSParallelSurvivorRemarkEnabled = true {product} + bool CMSPermGenPrecleaningEnabled = true {product} +uintx CMSPrecleanDenominator = 3 {product} +uintx CMSPrecleanIter = 3 {product} +uintx CMSPrecleanNumerator = 2 {product} + bool CMSPrecleanRefLists1 = true {product} + bool CMSPrecleanRefLists2 = false {product} + bool CMSPrecleanSurvivors1 = false {product} + bool CMSPrecleanSurvivors2 = true {product} +uintx CMSPrecleanThreshold = 1000 {product} + bool CMSPrecleaningEnabled = true {product} + bool CMSPrintChunksInDump = false {product} + bool CMSPrintObjectsInDump = false {product} +uintx CMSRemarkVerifyVariant = 1 {product} + bool CMSReplenishIntermediate = true {product} +uintx CMSRescanMultiple = 32 {product} +uintx CMSRevisitStackSize = 1048576 {product} +uintx CMSSamplingGrain = 16384 {product} + bool CMSScavengeBeforeRemark = false {product} +uintx CMSScheduleRemarkEdenPenetration = 50 {product} +uintx CMSScheduleRemarkEdenSizeThreshold = 2097152 {product} +uintx CMSScheduleRemarkSamplingRatio = 5 {product} +double CMSSmallCoalSurplusPercent = {product} +double CMSSmallSplitSurplusPercent = {product} + bool CMSSplitIndexedFreeListBlocks = true {product} + intx CMSTriggerPermRatio = 80 {product} + intx CMSTriggerRatio = 80 {product} + bool CMSUseGCOverheadLimit = true {product} + bool CMSUseOldDefaults = false {product} + intx CMSWaitDuration = 2000 {product} +uintx CMSWorkQueueDrainThreshold = 10 {product} + bool CMSYield = true {product} +uintx CMSYieldSleepCount = 0 {product} + intx CMSYoungGenPerWorker = 16777216 {product} +uintx CMS_FLSPadding = 1 {product} +uintx CMS_FLSWeight = 75 {product} +uintx CMS_SweepPadding = 1 {product} +uintx CMS_SweepTimerThresholdMillis = 10 {product} +uintx CMS_SweepWeight = 75 {product} +uintx CPUForCMSThread = 0 {diagnostic} + bool CheckJNICalls = false {product} + bool ClassUnloading = true {product} + intx ClearFPUAtPark = 0 {product} + bool ClipInlining = true {product} +uintx CodeCacheExpansionSize = 32768 {pd product} +uintx CodeCacheFlushingMinimumFreeSpace = 1536000 {product} +uintx CodeCacheMinimumFreeSpace = 512000 {product} + bool CollectGen0First = false {product} + bool CompactFields = true {product} + intx CompilationPolicyChoice = 0 {product} + intx CompilationRepeat = 0 {C1 product} +ccstrlist CompileCommand = {product} +ccstr CompileCommandFile = {product} +ccstrlist CompileOnly = {product} + intx CompileThreshold = 10000 {pd product} + bool CompilerThreadHintNoPreempt = true {product} + intx CompilerThreadPriority = -1 {product} + intx CompilerThreadStackSize = 0 {pd product} +uintx ConcGCThreads = 0 {product} + intx ConditionalMoveLimit = 3 {C2 pd product} + bool ConvertSleepToYield = true {pd product} + bool ConvertYieldToSleep = false {product} + bool CycleTime = false {product} + bool DTraceAllocProbes = false {product} + bool DTraceMethodProbes = false {product} + bool DTraceMonitorProbes = false {product} + bool DeallocateHeapPages = true {product} + bool DeallocateStackPages = true {product} +uintx DeallocateStackPagesMinIntervalMs = 100 {product} + bool DebugContinuation = false {diagnostic} + bool DebugInlinedCalls = true {diagnostic} + bool DebugNonSafepoints = false {diagnostic} +uintx DefaultMaxRAMFraction = 4 {product} + intx DefaultThreadPriority = -1 {product} + bool DeferInitialCardMark = false {diagnostic} + intx DeferPollingPageLoopCount = -1 {product} + intx DeferThrSuspendLoopCount = 4000 {product} + bool DeoptimizeRandom = false {product} + bool DisableAttachMechanism = false {product} + bool DisableExplicitGC = false {product} +ccstrlist DisableIntrinsic = {diagnostic} + bool DisplayVMOutput = true {diagnostic} + bool DisplayVMOutputToStderr = false {product} + bool DisplayVMOutputToStdout = false {product} + bool DoEscapeAnalysis = true {C2 product} + intx DominatorSearchLimit = 1000 {C2 diagnostic} + bool DontCompileHugeMethods = true {product} + bool DontYieldALot = false {pd product} + bool DumpSharedSpaces = false {product} + bool EagerXrunInit = false {product} + intx EliminateAllocationArraySizeLimit = 64 {C2 product} + bool EliminateAllocations = true {C2 product} + bool EliminateAutoBox = false {C2 diagnostic} + bool EliminateLocks = true {C2 product} + intx EmitSync = 0 {product} +uintx ErgoHeapSizeLimit = 0 {product} +ccstr ErrorFile = {product} + bool EstimateArgEscape = true {product} + intx EventLogLength = 2000 {product} + bool ExplicitGCInvokesConcurrent = false {product} + bool ExplicitGCInvokesConcurrentAndUnloadsClasses = false {product} + bool ExtendedDTraceProbes = false {product} + bool FLSAlwaysCoalesceLarge = false {product} +uintx FLSCoalescePolicy = 2 {product} +double FLSLargestBlockCoalesceProximity = {product} + bool FLSVerifyAllHeapReferences = false {diagnostic} + bool FLSVerifyIndexTable = false {diagnostic} + bool FLSVerifyLists = false {diagnostic} + bool FailOverToOldVerifier = true {product} + bool FastCardTableScan = false {product} + bool FastTLABRefill = true {product} + intx FenceInstruction = 0 {product} + intx FieldsAllocationStyle = 1 {product} + bool FilterSpuriousWakeups = true {product} + bool ForceFullGCJVMTIEpilogues = false {product} + bool ForceNUMA = false {product} + bool ForceSharedSpaces = false {product} + bool ForceTimeHighResolution = false {product} + intx FreqInlineSize = 325 {pd product} + bool FullProfileOnReInterpret = true {diagnostic} + intx G1ConcRefinementGreenZone = 0 {product} + intx G1ConcRefinementRedZone = 0 {product} + intx G1ConcRefinementServiceIntervalMillis = 300 {product} +uintx G1ConcRefinementThreads = 0 {product} + intx G1ConcRefinementThresholdStep = 0 {product} + intx G1ConcRefinementYellowZone = 0 {product} + intx G1ConfidencePercent = 50 {product} +uintx G1HeapRegionSize = 0 {product} + intx G1MarkRegionStackSize = 1048576 {product} + bool G1PrintHeapRegions = false {diagnostic} + intx G1RSetRegionEntries = 0 {product} +uintx G1RSetScanBlockSize = 64 {product} + intx G1RSetSparseRegionEntries = 0 {product} + intx G1RSetUpdatingPauseTimePercent = 10 {product} + intx G1ReservePercent = 10 {product} + intx G1SATBBufferSize = 1024 {product} + bool G1SummarizeConcMark = false {diagnostic} + bool G1SummarizeRSetStats = false {diagnostic} + intx G1SummarizeRSetStatsPeriod = 0 {diagnostic} + bool G1SummarizeZFStats = false {diagnostic} + bool G1TraceConcRefinement = false {diagnostic} + intx G1UpdateBufferSize = 256 {product} + bool G1UseAdaptiveConcRefinement = true {product} +uintx GCDrainStackTargetSize = 64 {product} +uintx GCHeapFreeLimit = 2 {product} + bool GCLockerInvokesConcurrent = false {product} + bool GCOverheadReporting = false {product} + intx GCOverheadReportingPeriodMS = 100 {product} + bool GCParallelVerificationEnabled = true {diagnostic} +uintx GCPauseIntervalMillis = 0 {product} +uintx GCTaskTimeStampEntries = 200 {product} +uintx GCTimeLimit = 98 {product} +uintx GCTimeRatio = 99 {product} + bool GoogleAgent = true {product} +ccstr GoogleAgentFlags = {product} + bool GoogleAgentWS = false {product} +ccstr GoogleAgentWSFlags = {product} +uintx GoogleGCHeapFreeLimitPolicy = 2 {product} + bool GoogleHeapInstrumentation = false {product} + bool GoogleHeapMonitor = true {product} + bool GoogleInheritAltSigStack = false {product} +uintx GoogleLogRotationSize = 0 {diagnostic} +uintx GoogleMaxGarbageHeapzTraces = 200 {product} + bool GoogleScoping = false {product} +uintx GoogleSoftRefLRUPolicy = 0 {product} +uintx GoogleSoftRefLRUPolicyExcludedMB = 0 {product} +uintx GoogleUseConstantOMProvision = 0 {product} + bool GoogleUseLibunwind = false {pd product} + bool GoogleUseSSEForVM = false {product} +ccstr HPILibPath = {product} + bool HandlePromotionFailure = true {product} +uintx HeapBaseMinAddress = 2147483648 {pd product} + bool HeapDumpAfterFullGC = false {manageable} + bool HeapDumpBeforeFullGC = false {manageable} + bool HeapDumpOnOutOfMemoryError = false {manageable} +ccstr HeapDumpPath = {manageable} +uintx HeapFirstMaximumCompactionCount = 3 {product} +uintx HeapMaximumCompactionInterval = 20 {product} + bool HoistFinalLoads = false {product} + bool IgnoreUnrecognizedVMOptions = false {product} +uintx InitialCodeCacheSize = 2359296 {pd product} + bool InitialCompileFast = false {diagnostic} + bool InitialCompileReallyFast = false {diagnostic} +uintx InitialHeapSize := 67108864 {product} +uintx InitialRAMFraction = 64 {product} +uintx InitialSurvivorRatio = 8 {product} + intx InitialTenuringThreshold = 7 {product} +uintx InitiatingHeapOccupancyPercent = 45 {product} + bool Inline = true {product} + bool InlineMethodsWithNullUnloadedTypesInSignature = true {product} + intx InlineSmallCode = 1000 {pd product} + bool InsertMemBarAfterArraycopy = true {C2 product} + intx InteriorEntryAlignment = 4 {C2 pd product} + intx InterpreterProfilePercentage = 33 {product} + bool JNIDetachReleasesMonitors = true {product} + bool JavaMonitorsInStackTrace = true {product} + intx JavaPriority10_To_OSPriority = -1 {product} + intx JavaPriority1_To_OSPriority = -1 {product} + intx JavaPriority2_To_OSPriority = -1 {product} + intx JavaPriority3_To_OSPriority = -1 {product} + intx JavaPriority4_To_OSPriority = -1 {product} + intx JavaPriority5_To_OSPriority = -1 {product} + intx JavaPriority6_To_OSPriority = -1 {product} + intx JavaPriority7_To_OSPriority = -1 {product} + intx JavaPriority8_To_OSPriority = -1 {product} + intx JavaPriority9_To_OSPriority = -1 {product} + bool LIRFillDelaySlots = false {C1 pd product} +uintx LargePageHeapSizeThreshold = 134217728 {product} +ccstr LargePageMountPoint = {product} +uintx LargePageSizeInBytes = 0 {product} + bool LazyBootClassLoader = true {product} + bool LinkWellKnownClasses = false {diagnostic} + bool LogAHR = false {product} + bool LogCompilation = false {diagnostic} +ccstr LogFile = {diagnostic} + bool LogGCOverheadLimit = false {product} + bool LogVMOutput = false {diagnostic} + intx LoopOptsCount = 43 {C2 product} + intx LoopUnrollLimit = 50 {C2 pd product} + intx LoopUnrollMin = 4 {C2 product} + bool LoopUnswitching = true {C2 product} + intx MallocVerifyInterval = 0 {diagnostic} + intx MallocVerifyStart = 0 {diagnostic} + bool ManagementServer = false {product} +uintx MarkStackSize = 32768 {product} +uintx MarkStackSizeMax = 4194304 {product} + intx MarkSweepAlwaysCompactCount = 4 {product} +uintx MarkSweepDeadRatio = 5 {product} + intx MaxBCEAEstimateLevel = 5 {product} + intx MaxBCEAEstimateSize = 150 {product} + intx MaxDirectMemorySize = -1 {product} + bool MaxFDLimit = true {product} +uintx MaxGCMinorPauseMillis = 4294967295 {product} +uintx MaxGCPauseMillis = 4294967295 {product} +uintx MaxHeapFreeRatio = 70 {product} +uintx MaxHeapSize := 1073741824 {product} + intx MaxInlineLevel = 9 {product} + intx MaxInlineSize = 35 {product} + intx MaxJavaStackTraceDepth = 1024 {product} + intx MaxJumpTableSize = 65000 {C2 product} + intx MaxJumpTableSparseness = 5 {C2 product} + intx MaxLabelRootDepth = 1100 {C2 product} +uintx MaxLiveObjectEvacuationRatio = 100 {product} + intx MaxLoopPad = 11 {C2 product} +uintx MaxNewSize = 4294901760 {product} + intx MaxNodeLimit = 65000 {C2 product} +uintx MaxPermHeapExpansion = 4194304 {product} +uintx MaxPermSize = 67108864 {pd product} +uint64_t MaxRAM = 0 {pd product} +uintx MaxRAMFraction = 4 {product} + intx MaxRecursiveInlineLevel = 1 {product} + intx MaxTenuringThreshold = 15 {product} + intx MaxTrivialSize = 6 {product} + bool MethodFlushing = true {product} + intx MethodHandlePushLimit = 3 {diagnostic} + intx MinCodeCacheFlushingInterval = 30 {product} +uintx MinHeapDeltaBytes = 131072 {product} +uintx MinHeapFreeRatio = 40 {product} + intx MinInliningThreshold = 250 {product} + intx MinJumpTableSize = 18 {C2 product} +uintx MinPermHeapExpansion = 262144 {product} +uintx MinRAMFraction = 2 {product} +uintx MinSurvivorRatio = 3 {product} +uintx MinTLABSize = 2048 {product} + bool MixedModeThreadDump = true {product} + intx MonitorBound = 0 {product} + bool MonitorInUseLists = false {product} + intx MultiArrayExpandLimit = 6 {C2 product} + bool MustCallLoadClassInternal = false {product} + intx NUMAChunkResizeWeight = 20 {product} + intx NUMAPageScanRate = 256 {product} + intx NUMASpaceResizeRate = 1073741824 {product} + bool NUMAStats = false {product} + intx NativeMonitorFlags = 0 {product} + intx NativeMonitorSpinLimit = 20 {product} + intx NativeMonitorTimeout = -1 {product} + bool NeedsDeoptSuspend = false {pd product} + bool NeverActAsServerClassMachine = false {pd product} + bool NeverTenure = false {product} + intx NewRatio = 2 {product} +uintx NewSize = 1048576 {product} +uintx NewSizeThreadIncrease = 4096 {pd product} + intx NmethodSweepCheckInterval = 5 {product} + intx NmethodSweepFraction = 4 {product} + intx NodeLimitFudgeFactor = 1000 {C2 product} + intx NumberOfLoopInstrToAlign = 4 {C2 product} + bool ObjLifeTracking = false {product} +uintx ObjLifeTrackingCpuFraction = 100 {product} +uintx ObjLifetimeHistoBucketCount = 1024 {product} + bool ObjLifetimeHistoExportVarPerBucket = false {product} +uintx OldPLABSize = 1024 {product} +uintx OldPLABWeight = 50 {product} +uintx OldSize = 4194304 {product} + bool OmitStackTraceInFastThrow = true {product} +ccstrlist OnError = {product} +ccstrlist OnOutOfMemoryError = {product} + intx OnStackReplacePercentage = 140 {pd product} + bool OptimizeFill = false {C2 product} + bool OptimizeMethodHandles = true {diagnostic} + bool OptimizeStringConcat = false {C2 product} + bool OptoBundling = false {C2 pd product} + intx OptoLoopAlignment = 16 {pd product} + bool OptoScheduling = false {C2 pd product} +uintx PLABWeight = 75 {product} + bool PSChunkLargeArrays = true {product} + bool PSResizeByFreeRatio = false {product} + bool PSResizeByFreeRatioWithSystemGC = false {product} + intx ParGCArrayScanChunk = 50 {product} + intx ParGCCardsPerStrideChunk = 256 {diagnostic} +uintx ParGCDesiredObjsFromOverflowList = 20 {product} + intx ParGCStridesPerThread = 2 {diagnostic} + bool ParGCTrimOverflow = true {product} + bool ParGCUseLocalOverflow = false {product} + intx ParallelGCBufferWastePct = 10 {product} + bool ParallelGCRetainPLAB = true {product} +uintx ParallelGCThreads := 10 {product} + bool ParallelGCVerbose = false {product} +uintx ParallelOldDeadWoodLimiterMean = 50 {product} +uintx ParallelOldDeadWoodLimiterStdDev = 80 {product} + bool ParallelRefProcBalancingEnabled = true {product} + bool ParallelRefProcEnabled = false {product} + bool PartialPeelAtUnsignedTests = true {C2 product} + bool PartialPeelLoop = true {C2 product} + intx PartialPeelNewPhiDelta = 0 {C2 product} + bool PauseAtStartup = false {diagnostic} +ccstr PauseAtStartupFile = {diagnostic} +uintx PausePadding = 1 {product} + intx PerBytecodeRecompilationCutoff = 200 {product} + intx PerBytecodeTrapLimit = 4 {product} + intx PerMethodRecompilationCutoff = 400 {product} + intx PerMethodTrapLimit = 100 {product} + bool PerfAllowAtExitRegistration = false {product} + bool PerfBypassFileSystemCheck = false {product} + intx PerfDataMemorySize = 32768 {product} + intx PerfDataSamplingInterval = 50 {product} +ccstr PerfDataSaveFile = {product} + bool PerfDataSaveToFile = false {product} + bool PerfDisableSharedMem = false {product} + intx PerfMaxStringConstLength = 1024 {product} +uintx PermGenPadding = 3 {product} +uintx PermMarkSweepDeadRatio = 20 {product} +uintx PermSize = 16777216 {pd product} + bool PostSpinYield = true {product} + intx PreBlockSpin = 10 {product} + intx PreInflateSpin = 10 {pd product} + bool PreSpinYield = false {product} + bool PreferInterpreterNativeStubs = false {pd product} + intx PrefetchCopyIntervalInBytes = -1 {product} + intx PrefetchFieldsAhead = -1 {product} + intx PrefetchScanIntervalInBytes = -1 {product} + bool PreserveAllAnnotations = false {product} +uintx PreserveMarkStackSize = 1024 {product} +uintx PretenureSizeThreshold = 0 {product} + bool PrintAdapterHandlers = false {diagnostic} + bool PrintAdaptiveSizePolicy = false {product} + bool PrintAssembly = false {diagnostic} +ccstr PrintAssemblyOptions = {diagnostic} + bool PrintBiasedLockingStatistics = false {diagnostic} + bool PrintCMSInitiationCause = false {product} + bool PrintCMSInitiationStatistics = false {product} + intx PrintCMSStatistics = 0 {product} + bool PrintCardTableStats = false {product} + bool PrintClassHistogram = false {manageable} + bool PrintClassHistogramAfterFullGC = false {manageable} + bool PrintClassHistogramBeforeFullGC = false {manageable} + bool PrintCommandLineFlags = false {product} + bool PrintCompilation = false {product} + bool PrintCompressedOopsMode = false {diagnostic} + bool PrintConcurrentLocks = false {manageable} + bool PrintDTraceDOF = false {diagnostic} + intx PrintFLSCensus = 0 {product} + intx PrintFLSStatistics = 0 {product} + bool PrintFlagsFinal := true {product} + bool PrintFlagsInitial = false {product} + bool PrintGC = false {manageable} + bool PrintGCApplicationConcurrentTime = false {product} + bool PrintGCApplicationStoppedTime = false {product} + bool PrintGCDateStamps = false {manageable} + bool PrintGCDetails = false {manageable} + bool PrintGCTaskTimeStamps = false {product} + bool PrintGCTimeStamps = false {manageable} + bool PrintHeapAtGC = false {product rw} + bool PrintHeapAtGCExtended = false {product rw} + bool PrintHeapAtSIGBREAK = true {product} + bool PrintInlining = false {diagnostic} + bool PrintInterpreter = false {diagnostic} + bool PrintIntrinsics = false {diagnostic} + bool PrintJNIGCStalls = false {product} + bool PrintJNIResolving = false {product} + bool PrintNMethods = false {diagnostic} + bool PrintNativeNMethods = false {diagnostic} + bool PrintOldPLAB = false {product} + bool PrintOopAddress = false {product} + bool PrintPLAB = false {product} + bool PrintParallelOldGCPhaseTimes = false {product} + bool PrintPreciseBiasedLockingStatistics = false {C2 diagnostic} + bool PrintPromotionFailure = false {product} + bool PrintReferenceGC = false {product} + bool PrintRevisitStats = false {product} + bool PrintSafepointStatistics = false {product} + intx PrintSafepointStatisticsCount = 300 {product} + intx PrintSafepointStatisticsTimeout = -1 {product} + bool PrintSharedSpaces = false {product} + bool PrintSignatureHandlers = false {diagnostic} + bool PrintStubCode = false {diagnostic} + bool PrintTLAB = false {product} + bool PrintTenuringDistribution = false {product} + bool PrintVMOptions = false {product} + bool PrintVMQWaitTime = false {product} +uintx ProcessDistributionStride = 4 {product} + bool ProfileInterpreter = true {pd product} + bool ProfileIntervals = false {product} + intx ProfileIntervalsTicks = 100 {product} + intx ProfileMaturityPercentage = 20 {product} + bool ProfileVM = false {product} + bool ProfilerPrintByteCodeStatistics = false {product} + bool ProfilerRecordPC = false {product} +uintx PromotedPadding = 3 {product} + intx QueuedAllocationWarningCount = 0 {product} +uintx RamLimit = 0 {product} + bool RangeCheckElimination = true {product} + intx ReadPrefetchInstr = 0 {product} + intx ReadSpinIterations = 100 {product} + bool ReassociateInvariants = true {C2 product} + bool ReduceBulkZeroing = true {C2 product} + bool ReduceFieldZeroing = true {C2 product} + bool ReduceInitialCardMarks = true {C2 product} + bool ReduceSignalUsage = false {product} + intx RefDiscoveryPolicy = 0 {product} + bool ReflectionWrapResolutionErrors = true {product} + bool RegisterFinalizersAtInit = true {product} + bool RelaxAccessControlCheck = false {product} + bool RequireSharedSpaces = false {product} +uintx ReservedCodeCacheSize = 50331648 {pd product} + bool ResizeOldPLAB = true {product} + bool ResizePLAB = true {product} + bool ResizeTLAB = true {pd product} + bool RestoreMXCSROnJNICalls = false {product} + bool RewriteBytecodes = true {pd product} + bool RewriteFrequentPairs = true {pd product} + intx SafepointPollOffset = 256 {C1 pd product} + intx SafepointSpinBeforeYield = 2000 {product} + bool SafepointTimeout = false {product} + intx SafepointTimeoutDelay = 10000 {product} + bool ScavengeBeforeFullGC = true {product} + intx ScavengeRootsInCode = 0 {diagnostic} + intx SelfDestructTimer = 0 {product} + bool SerializeVMOutput = true {diagnostic} +uintx SharedDummyBlockSize = 536870912 {product} +uintx SharedMiscCodeSize = 4194304 {product} +uintx SharedMiscDataSize = 4194304 {product} + bool SharedOptimizeColdStart = true {diagnostic} +uintx SharedReadOnlySize = 10485760 {product} +uintx SharedReadWriteSize = 12582912 {product} + bool SharedSkipVerify = false {diagnostic} + bool ShowMessageBoxOnError = false {product} + intx SoftRefLRUPolicyMSPerMB = 1000 {product} + bool SplitIfBlocks = true {product} + intx StackRedPages = 1 {pd product} + intx StackShadowPages = 3 {pd product} + bool StackTraceInThrowable = true {product} + intx StackYellowPages = 2 {pd product} + bool StartAttachListener = false {product} + bool StartSuspended = false {product} + intx StarvationMonitorInterval = 200 {product} + bool StressLdcRewrite = false {product} + bool StressTieredRuntime = false {product} + bool SuppressFatalErrorMessage = false {product} +uintx SurvivorPadding = 3 {product} + intx SurvivorRatio = 8 {product} + intx SuspendRetryCount = 50 {product} + intx SuspendRetryDelay = 5 {product} + intx SyncFlags = 0 {product} +ccstr SyncKnobs = {product} + intx SyncVerbose = 0 {product} +uintx TLABAllocationWeight = 35 {product} +uintx TLABRefillWasteFraction = 64 {product} +uintx TLABSize = 0 {product} + bool TLABStats = true {product} +uintx TLABWasteIncrement = 4 {product} +uintx TLABWasteTargetPercent = 1 {product} + intx TargetPLABWastePct = 10 {product} + intx TargetSurvivorRatio = 50 {product} +uintx TenuredGenerationSizeIncrement = 20 {product} +uintx TenuredGenerationSizeSupplement = 80 {product} +uintx TenuredGenerationSizeSupplementDecay = 2 {product} + intx ThreadPriorityPolicy = 0 {product} + bool ThreadPriorityVerbose = false {product} +uintx ThreadSafetyMargin = 52428800 {product} + intx ThreadStackSize = 320 {pd product} +uintx ThresholdTolerance = 10 {product} + intx Tier1BytecodeLimit = 10 {product} + intx Tier1FreqInlineSize = 35 {C2 product} + intx Tier1Inline = 0 {C2 product} + intx Tier1LoopOptsCount = 0 {C2 product} + intx Tier1MaxInlineSize = 8 {C2 product} + bool Tier1OptimizeVirtualCallProfiling = true {C1 product} + bool Tier1ProfileBranches = true {C1 product} + bool Tier1ProfileCalls = true {C1 product} + bool Tier1ProfileCheckcasts = true {C1 product} + bool Tier1ProfileInlinedCalls = true {C1 product} + bool Tier1ProfileVirtualCalls = true {C1 product} + bool Tier1UpdateMethodData = true {product} + intx Tier2BackEdgeThreshold = 100000 {pd product} + intx Tier2CompileThreshold = 10000 {pd product} + intx Tier3BackEdgeThreshold = 100000 {pd product} + intx Tier3CompileThreshold = 20000 {pd product} + intx Tier4BackEdgeThreshold = 100000 {pd product} + intx Tier4CompileThreshold = 40000 {pd product} + bool TieredCompilation = false {pd product} + bool TimeLinearScan = false {C1 product} + bool TraceBiasedLocking = false {product} + bool TraceClassLoading = false {product rw} + bool TraceClassLoadingPreorder = false {product} + bool TraceClassResolution = false {product} + bool TraceClassUnloading = false {product rw} + bool TraceCompileTriggered = false {diagnostic} + bool TraceGen0Time = false {product} + bool TraceGen1Time = false {product} +ccstr TraceJVMTI = {product} + bool TraceJVMTIObjectTagging = false {diagnostic} + bool TraceLoaderConstraints = false {product rw} + bool TraceMonitorInflation = false {product} + bool TraceNMethodInstalls = false {diagnostic} + bool TraceOSRBreakpoint = false {diagnostic} + bool TraceParallelOldGCTasks = false {product} + intx TraceRedefineClasses = 0 {product} + bool TraceRedundantCompiles = false {diagnostic} + bool TraceSafepointCleanupTime = false {product} + bool TraceSuperWord = false {C2 product} + bool TraceSuspendWaitFailures = false {product} + bool TraceTriggers = false {diagnostic} + intx TrackedInitializationLimit = 50 {C2 product} + intx TypeProfileMajorReceiverPercent = 90 {product} + intx TypeProfileWidth = 2 {product} + intx UnguardOnExecutionViolation = 0 {product} + bool UnlockDiagnosticVMOptions = true {diagnostic} + bool UnsyncloadClass = false {diagnostic} + bool Use486InstrsOnly = false {product} + bool UseAdaptiveGCBoundary = false {product} + bool UseAdaptiveGenerationSizePolicyAtMajorCollection = true {product} + bool UseAdaptiveGenerationSizePolicyAtMinorCollection = true {product} + bool UseAdaptiveNUMAChunkSizing = true {product} + bool UseAdaptiveSizeDecayMajorGCCost = true {product} + bool UseAdaptiveSizePolicy = true {product} + bool UseAdaptiveSizePolicyFootprintGoal = true {product} + bool UseAdaptiveSizePolicyWithSystemGC = false {product} + bool UseAddressNop = true {product} + bool UseAltSigs = false {product} + bool UseAutoGCSelectPolicy = false {product} + bool UseBiasedLocking = true {product} + bool UseBimorphicInlining = true {C2 product} + bool UseBoundThreads = true {product} + bool UseCMSBestFit = true {product} + bool UseCMSCollectionPassing = true {product} + bool UseCMSCompactAtFullCollection = true {product} + bool UseCMSInitiatingOccupancyOnly = false {product} + bool UseCodeCacheFlushing = false {product} + bool UseCompiler = true {product} + bool UseCompilerSafepoints = true {product} + bool UseConcMarkSweepGC = false {product} + bool UseCountLeadingZerosInstruction = false {product} + bool UseCounterDecay = true {product} + bool UseDivMod = true {C2 product} + bool UseFPUForSpilling = false {C2 product} + bool UseFastAccessorMethods = true {product} + bool UseFastEmptyMethods = true {product} + bool UseFastJNIAccessors = true {product} + bool UseG1GC = false {product} + bool UseGCOverheadLimit = true {product} + bool UseGCTaskAffinity = false {product} + bool UseHeavyMonitors = false {product} + bool UseIncDec = true {diagnostic} + bool UseInlineCaches = true {product} + bool UseInterpreter = true {product} + bool UseJumpTables = true {C2 product} + bool UseLWPSynchronization = true {product} + bool UseLargePages = false {pd product} + bool UseLargePagesIndividualAllocation = false {pd product} + bool UseLinuxPosixThreadCPUClocks = false {product} + bool UseLoopCounter = true {product} + bool UseLoopPredicate = true {C2 product} + bool UseMaximumCompactionOnSystemGC = true {product} + bool UseMembar = false {product} + bool UseNUMA = false {product} + bool UseNewCode = false {diagnostic} + bool UseNewCode2 = false {diagnostic} + bool UseNewCode3 = false {diagnostic} + bool UseNewFeature1 = false {C1 product} + bool UseNewFeature2 = false {C1 product} + bool UseNewFeature3 = false {C1 product} + bool UseNewFeature4 = false {C1 product} + bool UseNewLongLShift = false {product} + bool UseNiagaraInstrs = false {product} + bool UseOSErrorReporting = false {pd product} + bool UseOldInlining = true {C2 product} + bool UseOnStackReplacement = true {pd product} + bool UseOnlyInlinedBimorphic = true {C2 product} + bool UseOprofile = false {product} + bool UseOptoBiasInlining = true {C2 product} + bool UsePSAdaptiveSurvivorSizePolicy = true {product} + bool UseParNewGC = false {product} + bool UseParallelDensePrefixUpdate = true {product} + bool UseParallelGC := true {product} + bool UseParallelOldGC = false {product} + bool UseParallelOldGCCompacting = true {product} + bool UseParallelOldGCDensePrefix = true {product} + bool UsePerfData = true {product} + bool UsePopCountInstruction = true {product} + intx UseSSE = 4 {product} + bool UseSSE42Intrinsics = true {product} + bool UseSeparateVSpacesInYoungGen = true {product} + bool UseSerialGC = false {product} + bool UseSharedSpaces = false {product} + bool UseSignalChaining = true {product} + bool UseSpinning = false {product} + bool UseSplitVerifier = true {product} + bool UseStoreImmI16 = false {product} + bool UseStringCache = false {product} + bool UseSuperWord = true {C2 product} + bool UseTLAB = true {pd product} + bool UseThreadPriorities = true {pd product} + bool UseTypeProfile = true {product} + bool UseUnalignedLoadStores = true {product} + bool UseVMInterruptibleIO = true {product} + bool UseVectoredExceptions = false {pd product} + bool UseXMMForArrayCopy = true {product} + bool UseXmmI2D = false {product} + bool UseXmmI2F = false {product} + bool UseXmmLoadAndClearUpper = true {product} + bool UseXmmRegToRegMoveAll = true {product} + bool VMThreadHintNoPreempt = false {product} + intx VMThreadPriority = -1 {product} + intx VMThreadStackSize = 512 {pd product} + intx ValueMapInitialSize = 11 {C1 product} + intx ValueMapMaxLoopSize = 8 {C1 product} + intx ValueSearchLimit = 1000 {C2 product} + bool VerifyAfterGC = false {diagnostic} + bool VerifyBeforeExit = false {diagnostic} + bool VerifyBeforeGC = false {diagnostic} + bool VerifyBeforeIteration = false {diagnostic} + bool VerifyDuringGC = false {diagnostic} + intx VerifyGCLevel = 0 {diagnostic} +uintx VerifyGCStartAt = 0 {diagnostic} + bool VerifyMergedCPBytecodes = true {product} + bool VerifyMethodHandles = false {diagnostic} + bool VerifyObjectStartArray = true {diagnostic} + bool VerifyRememberedSets = false {diagnostic} + intx WorkAroundNPTLTimedWaitHang = 1 {product} +uintx YoungGenerationSizeIncrement = 20 {product} +uintx YoungGenerationSizeSupplement = 80 {product} +uintx YoungGenerationSizeSupplementDecay = 8 {product} +uintx YoungPLABSize = 4096 {product} + bool ZeroTLAB = false {product} + intx hashCode = 0 {product}
\ No newline at end of file diff --git a/caliper/src/test/resources/com/google/caliper/bridge/jdk6-gc.txt b/caliper/src/test/resources/com/google/caliper/bridge/jdk6-gc.txt new file mode 100644 index 0000000..fd9d07c --- /dev/null +++ b/caliper/src/test/resources/com/google/caliper/bridge/jdk6-gc.txt @@ -0,0 +1,200 @@ +[GC 987K->384K(62848K), 0.0012320 secs] +[Full GC 384K->288K(62848K), 0.0054550 secs] +[GC 288K->288K(62848K), 0.0004450 secs] +[Full GC 288K->288K(62848K), 0.0049580 secs] +[GC 288K->288K(62848K), 0.0004590 secs] +[Full GC 288K->288K(62848K), 0.0048240 secs] +[GC 288K->288K(62848K), 0.0005700 secs] +[Full GC 288K->288K(62848K), 0.0063250 secs] +[GC 288K->288K(62848K), 0.0003540 secs] +[Full GC 288K->288K(62848K), 0.0048210 secs] +[GC 288K->288K(62848K), 0.0003700 secs] +[Full GC 288K->288K(62848K), 0.0049430 secs] +[GC 288K->288K(62848K), 0.0004250 secs] +[Full GC 288K->288K(62848K), 0.0047700 secs] +[GC 288K->288K(62848K), 0.0003550 secs] +[Full GC 288K->288K(62848K), 0.0047090 secs] +[GC 288K->288K(62848K), 0.0003850 secs] +[Full GC 288K->288K(62848K), 0.0047220 secs] +[GC 288K->288K(62848K), 0.0003830 secs] +[Full GC 288K->288K(62848K), 0.0047330 secs] +[GC 288K->288K(62848K), 0.0004230 secs] +[Full GC 288K->288K(62848K), 0.0047130 secs] +[GC 288K->288K(62848K), 0.0003140 secs] +[Full GC 288K->288K(62848K), 0.0047360 secs] +[GC 288K->288K(62848K), 0.0003540 secs] +[Full GC 288K->288K(62848K), 0.0047210 secs] +[GC 288K->288K(62848K), 0.0003610 secs] +[Full GC 288K->288K(62848K), 0.0047280 secs] +[GC 288K->288K(62848K), 0.0004640 secs] +[Full GC 288K->288K(62848K), 0.0046920 secs] +[GC 288K->288K(62848K), 0.0004700 secs] +[Full GC 288K->288K(62848K), 0.0047080 secs] +[GC 288K->288K(62848K), 0.0003220 secs] +[Full GC 288K->288K(62848K), 0.0047020 secs] +[GC 288K->288K(62848K), 0.0004100 secs] +[Full GC 288K->288K(62848K), 0.0047860 secs] +[GC 288K->288K(62848K), 0.0004340 secs] +[Full GC 288K->288K(62848K), 0.0047310 secs] +[GC 288K->288K(62848K), 0.0003980 secs] +[Full GC 288K->288K(62848K), 0.0047440 secs] +[GC 288K->288K(62848K), 0.0004540 secs] +[Full GC 288K->288K(62848K), 0.0047260 secs] +[GC 288K->288K(62848K), 0.0003340 secs] +[Full GC 288K->288K(62848K), 0.0047130 secs] +[GC 288K->288K(62848K), 0.0003420 secs] +[Full GC 288K->288K(62848K), 0.0046830 secs] +[GC 288K->288K(62848K), 0.0003560 secs] +[Full GC 288K->288K(62848K), 0.0047030 secs] +[GC 288K->288K(62848K), 0.0003440 secs] +[Full GC 288K->288K(62848K), 0.0047340 secs] +[GC 288K->288K(62848K), 0.0004980 secs] +[Full GC 288K->288K(62848K), 0.0048440 secs] +[GC 288K->288K(62848K), 0.0003500 secs] +[Full GC 288K->288K(62848K), 0.0048000 secs] +[GC 288K->288K(62848K), 0.0004090 secs] +[Full GC 288K->288K(62848K), 0.0048140 secs] +[GC 288K->288K(62848K), 0.0004960 secs] +[Full GC 288K->288K(62848K), 0.0048130 secs] +[GC 288K->288K(62848K), 0.0003140 secs] +[Full GC 288K->288K(62848K), 0.0047990 secs] +[GC 288K->288K(62848K), 0.0003350 secs] +[Full GC 288K->288K(62848K), 0.0048460 secs] +[GC 288K->288K(62848K), 0.0002190 secs] +[Full GC 288K->288K(62848K), 0.0047700 secs] +[GC 288K->288K(62848K), 0.0003200 secs] +[Full GC 288K->288K(62848K), 0.0047690 secs] +[GC 288K->288K(62848K), 0.0003220 secs] +[Full GC 288K->288K(62848K), 0.0047490 secs] +[GC 288K->288K(62848K), 0.0003210 secs] +[Full GC 288K->288K(62848K), 0.0047590 secs] +[GC 288K->288K(62848K), 0.0002560 secs] +[Full GC 288K->288K(62848K), 0.0047250 secs] +[GC 288K->288K(62848K), 0.0003560 secs] +[Full GC 288K->288K(62848K), 0.0047430 secs] +[GC 288K->288K(62848K), 0.0002860 secs] +[Full GC 288K->288K(62848K), 0.0047320 secs] +[GC 288K->288K(62848K), 0.0003470 secs] +[Full GC 288K->288K(62848K), 0.0047370 secs] +[GC 288K->288K(62848K), 0.0003020 secs] +[Full GC 288K->288K(62848K), 0.0047140 secs] +[GC 288K->288K(62848K), 0.0002670 secs] +[Full GC 288K->288K(62848K), 0.0047510 secs] +[GC 288K->288K(62848K), 0.0003510 secs] +[Full GC 288K->288K(62848K), 0.0047140 secs] +[GC 288K->288K(62848K), 0.0002680 secs] +[Full GC 288K->288K(62848K), 0.0047100 secs] +[GC 288K->288K(62848K), 0.0002390 secs] +[Full GC 288K->288K(62848K), 0.0047820 secs] +[GC 288K->288K(62848K), 0.0002780 secs] +[Full GC 288K->288K(62848K), 0.0047480 secs] +[GC 288K->288K(62848K), 0.0002590 secs] +[Full GC 288K->288K(62848K), 0.0048950 secs] +[GC 288K->288K(62848K), 0.0004100 secs] +[Full GC 288K->288K(62848K), 0.0047580 secs] +[GC 288K->288K(62848K), 0.0003630 secs] +[Full GC 288K->288K(62848K), 0.0047230 secs] +[GC 288K->288K(62848K), 0.0003200 secs] +[Full GC 288K->288K(62848K), 0.0047490 secs] +[GC 288K->288K(62848K), 0.0002940 secs] +[Full GC 288K->288K(62848K), 0.0047300 secs] +[GC 288K->288K(62848K), 0.0016660 secs] +[Full GC 288K->288K(62848K), 0.0052520 secs] +[GC 288K->288K(62848K), 0.0003090 secs] +[Full GC 288K->288K(62848K), 0.0051710 secs] +[GC 288K->288K(62848K), 0.0003220 secs] +[Full GC 288K->288K(62848K), 0.0050270 secs] +[GC 288K->288K(62848K), 0.0003390 secs] +[Full GC 288K->288K(62848K), 0.0048450 secs] +[GC 288K->288K(62848K), 0.0003830 secs] +[Full GC 288K->288K(62848K), 0.0047870 secs] +[GC 288K->288K(62848K), 0.0003800 secs] +[Full GC 288K->288K(62848K), 0.0051750 secs] +[GC 288K->288K(62848K), 0.0004380 secs] +[Full GC 288K->288K(62848K), 0.0052900 secs] +[GC 288K->288K(62848K), 0.0003550 secs] +[Full GC 288K->288K(62848K), 0.0053350 secs] +[GC 288K->288K(62848K), 0.0003090 secs] +[Full GC 288K->288K(62848K), 0.0053060 secs] +[GC 288K->288K(62848K), 0.0003030 secs] +[Full GC 288K->288K(62848K), 0.0052720 secs] +[GC 288K->288K(62848K), 0.0003220 secs] +[Full GC 288K->288K(62848K), 0.0053230 secs] +[GC 288K->288K(62848K), 0.0003610 secs] +[Full GC 288K->288K(62848K), 0.0053970 secs] +[GC 288K->288K(62848K), 0.0003590 secs] +[Full GC 288K->288K(62848K), 0.0053740 secs] +[GC 288K->288K(62848K), 0.0003610 secs] +[Full GC 288K->288K(62848K), 0.0054130 secs] +[GC 288K->288K(62848K), 0.0004750 secs] +[Full GC 288K->288K(62848K), 0.0053560 secs] +[GC 288K->288K(62848K), 0.0003510 secs] +[Full GC 288K->288K(62848K), 0.0053140 secs] +[GC 288K->288K(62848K), 0.0004350 secs] +[Full GC 288K->288K(62848K), 0.0053260 secs] +[GC 288K->288K(62848K), 0.0004010 secs] +[Full GC 288K->288K(62848K), 0.0054420 secs] +[GC 288K->288K(62848K), 0.0004300 secs] +[Full GC 288K->288K(62848K), 0.0053740 secs] +[GC 288K->288K(62848K), 0.0003610 secs] +[Full GC 288K->288K(62848K), 0.0070060 secs] +[GC 288K->288K(62848K), 0.0003240 secs] +[Full GC 288K->288K(62848K), 0.0067830 secs] +[GC 288K->288K(62848K), 0.0003500 secs] +[Full GC 288K->288K(62848K), 0.0068030 secs] +[GC 288K->288K(62848K), 0.0003260 secs] +[Full GC 288K->288K(62848K), 0.0066850 secs] +[GC 288K->288K(62848K), 0.0004600 secs] +[Full GC 288K->288K(62848K), 0.0067520 secs] +[GC 288K->288K(62848K), 0.0003550 secs] +[Full GC 288K->288K(62848K), 0.0067010 secs] +[GC 288K->288K(62848K), 0.0004590 secs] +[Full GC 288K->288K(62848K), 0.0069550 secs] +[GC 288K->288K(62848K), 0.0004450 secs] +[Full GC 288K->288K(62848K), 0.0068770 secs] +[GC 288K->288K(62848K), 0.0005830 secs] +[Full GC 288K->288K(62848K), 0.0067410 secs] +[GC 288K->288K(62848K), 0.0004850 secs] +[Full GC 288K->288K(62848K), 0.0067660 secs] +[GC 288K->288K(62848K), 0.0004270 secs] +[Full GC 288K->288K(62848K), 0.0067770 secs] +[GC 288K->288K(62848K), 0.0004140 secs] +[Full GC 288K->288K(62848K), 0.0067090 secs] +[GC 288K->288K(62848K), 0.0004360 secs] +[Full GC 288K->288K(62848K), 0.0067760 secs] +[GC 288K->288K(62848K), 0.0005740 secs] +[Full GC 288K->288K(62848K), 0.0067000 secs] +[GC 288K->288K(62848K), 0.0004730 secs] +[Full GC 288K->288K(62848K), 0.0066820 secs] +[GC 288K->288K(62848K), 0.0003490 secs] +[Full GC 288K->288K(62848K), 0.0067010 secs] +[GC 288K->288K(62848K), 0.0004610 secs] +[Full GC 288K->288K(62848K), 0.0067730 secs] +[GC 288K->288K(62848K), 0.0005560 secs] +[Full GC 288K->288K(62848K), 0.0067720 secs] +[GC 288K->288K(62848K), 0.0004240 secs] +[Full GC 288K->288K(62848K), 0.0066300 secs] +[GC 288K->288K(62848K), 0.0005200 secs] +[Full GC 288K->288K(62848K), 0.0069920 secs] +[GC 288K->288K(62848K), 0.0004240 secs] +[Full GC 288K->288K(62848K), 0.0076550 secs] +[GC 617K->304K(62848K), 0.0004960 secs] +[Full GC 304K->288K(62848K), 0.0076840 secs] +[GC 617K->304K(62848K), 0.0003870 secs] +[Full GC 304K->288K(62848K), 0.0077080 secs] +[GC 288K->288K(62848K), 0.0004140 secs] +[Full GC 288K->288K(62848K), 0.0073410 secs] +[GC 288K->288K(62848K), 0.0004640 secs] +[Full GC 288K->288K(62848K), 0.0066710 secs] +[GC 288K->288K(62848K), 0.0004830 secs] +[Full GC 288K->288K(62848K), 0.0066390 secs] +[GC 288K->288K(62848K), 0.0006970 secs] +[Full GC 288K->288K(62848K), 0.0066070 secs] +[GC 288K->288K(62848K), 0.0003500 secs] +[Full GC 288K->288K(62848K), 0.0065460 secs] +[GC 288K->288K(62848K), 0.0004160 secs] +[Full GC 288K->288K(62848K), 0.0065420 secs] +[GC 288K->288K(62848K), 0.0003710 secs] +[Full GC 288K->288K(62848K), 0.0066060 secs] +[GC 288K->288K(62848K), 0.0003510 secs] +[Full GC 288K->288K(62848K), 0.0065420 secs]
\ No newline at end of file diff --git a/caliper/src/test/resources/com/google/caliper/bridge/jdk7-compilation.txt b/caliper/src/test/resources/com/google/caliper/bridge/jdk7-compilation.txt new file mode 100644 index 0000000..02acb05 --- /dev/null +++ b/caliper/src/test/resources/com/google/caliper/bridge/jdk7-compilation.txt @@ -0,0 +1,352 @@ + 91 1 b java.lang.String::hashCode (67 bytes) + 106 2 b sun.nio.cs.UTF_8$Decoder::decode (640 bytes) + 130 3 b java.lang.String::lastIndexOf (68 bytes) + 132 4 b java.lang.String::indexOf (87 bytes) + 136 5 b java.io.UnixFileSystem::normalize (75 bytes) + 138 1 % b java.io.UnixFileSystem::normalize @ 10 (75 bytes) + 142 6 b sun.nio.cs.UTF_8$Encoder::encode (361 bytes) + 149 2 % b sun.nio.cs.UTF_8$Encoder::encode @ 20 (361 bytes) + 162 2 sun.nio.cs.UTF_8$Decoder::decode (640 bytes) made not entrant + 165 7 b sun.nio.cs.UTF_8$Decoder::decode (640 bytes) + 185 8 b sun.net.www.ParseUtil::encodePath (336 bytes) + 200 9 b java.lang.String::indexOf (166 bytes) + 236 10 b java.lang.String::equals (88 bytes) + 253 11 b java.util.Arrays::binarySearch0 (72 bytes) + 255 12 b com.google.common.base.CharMatcher$10::matches (17 bytes) + 256 13 b java.util.Arrays::binarySearch (9 bytes) + 257 3 % b com.google.common.base.CharMatcher::setBits @ 3 (28 bytes) +made not compilable com.google.common.base.CharMatcher::matches + 263 14 b com.google.common.base.CharMatcher::setBits (28 bytes) + 363 15 b java.lang.Object::<init> (1 bytes) + 365 16 b java.lang.String::charAt (33 bytes) + 483 17 b java.lang.String::length (5 bytes) + 487 18 n java.util.zip.ZipFile::getEntryBytes (0 bytes) (static) + 524 19 b java.util.zip.ZipFile::ensureOpen (37 bytes) + 525 20 b java.util.zip.ZipFile::access$400 (5 bytes) + 527 21 b java.util.zip.ZipFile::access$200 (5 bytes) + 527 22 b java.util.zip.ZipFile::access$300 (5 bytes) + 534 23 n java.lang.System::arraycopy (0 bytes) (static) + 550 24 b java.lang.Math::min (11 bytes) + 557 25 b java.util.HashMap::indexFor (6 bytes) + 559 26 b java.util.HashMap::hash (23 bytes) + 561 27 b java.lang.String::<init> (20 bytes) + 563 28 b java.lang.String::substring (83 bytes) + 566 29 b java.util.HashMap::get (79 bytes) + 569 30 b java.util.Arrays::copyOfRange (63 bytes) + 573 31 b java.lang.String::<init> (72 bytes) + 577 32 b java.lang.String::lastIndexOf (12 bytes) + 588 33 n java.util.zip.ZipFile::getEntrySize (0 bytes) (static) + 588 34 b java.nio.charset.CharsetDecoder::maxCharsPerByte (5 bytes) + 589 35 n java.util.zip.ZipFile::getEntryCSize (0 bytes) (static) + 589 36 n java.util.zip.ZipFile::getEntryMethod (0 bytes) (static) + 589 37 n java.util.zip.ZipFile::freeEntry (0 bytes) (static) + 590 38 b java.util.zip.ZipFile::getZipEntry (245 bytes) +made not compilable java.nio.charset.Charset::newDecoder + 612 39 b java.util.zip.ZipEntry::<init> (43 bytes) + 613 40 n java.util.zip.ZipFile::getEntryFlag (0 bytes) (static) + 613 41 b java.util.zip.ZipCoder::isUTF8 (5 bytes) + 613 42 n java.util.zip.ZipFile::getEntryTime (0 bytes) (static) + 613 43 n java.util.zip.ZipFile::getEntryCrc (0 bytes) (static) + 613 44 b java.util.zip.ZipFile::access$1000 (6 bytes) + 614 45 b java.util.jar.JarFile$JarFileEntry::<init> (11 bytes) + 615 46 b java.util.jar.JarEntry::<init> (6 bytes) + 615 47 b java.util.zip.ZipEntry::<init> (115 bytes) + 616 48 b sun.misc.URLClassPath::getResourceMapKey (55 bytes) + 621 49 b java.util.jar.JarFile$1::hasMoreElements (10 bytes) + 623 50 !b java.util.zip.ZipFile$1::hasMoreElements (41 bytes) + 624 51 b java.util.zip.ZipEntry::getName (5 bytes) + 625 52 b java.util.jar.JarFile$1::nextElement (5 bytes) + 629 53 b java.util.jar.JarFile$1::nextElement (26 bytes) + 633 54 b java.util.zip.ZipFile$1::nextElement (5 bytes) + 636 55 !b java.util.zip.ZipFile$1::nextElement (212 bytes) + 639 56 b java.util.zip.ZipFile::access$500 (6 bytes) + 639 57 n java.util.zip.ZipFile::getNextEntry (0 bytes) (static) + 639 58 b java.util.zip.ZipFile::access$900 (7 bytes) + 640 59 b java.util.ArrayList::size (5 bytes) + 641 60 b sun.misc.URLClassPath$JarLoader::addJarEntriesToEntryMap (202 bytes) + 664 61 b java.util.ArrayList::get (11 bytes) + 664 62 b java.util.ArrayList::rangeCheck (22 bytes) + 665 63 b java.util.ArrayList::elementData (7 bytes) + 707 29 java.util.HashMap::get (79 bytes) made not entrant + 719 64 b java.lang.String::replace (142 bytes) + 749 65 b java.lang.String::startsWith (78 bytes) + 865 66 b java.util.HashMap::get (79 bytes) + 867 67 b java.io.DataOutputStream::writeUTF (435 bytes) + 913 68 b java.lang.String::getChars (66 bytes) + 917 69 !b sun.reflect.generics.parser.SignatureParser::current (40 bytes) + 918 70 b java.lang.AbstractStringBuilder::ensureCapacityInternal (16 bytes) + 922 71 b java.io.ByteArrayOutputStream::ensureCapacity (16 bytes) + 922 72 b java.io.DataOutputStream::incCount (20 bytes) + 923 73 s b java.io.ByteArrayOutputStream::write (32 bytes) + 933 74 b java.lang.CharacterData::of (120 bytes) + 934 75 b java.lang.CharacterDataLatin1::getProperties (11 bytes) + 967 76 b java.util.concurrent.ConcurrentSkipListMap::findPredecessor (121 bytes) + 979 77 b java.lang.Integer::compareTo (9 bytes) + 980 78 b java.lang.Integer::compareTo (12 bytes) + 980 79 b java.lang.Integer::compare (20 bytes) + 1030 80 b java.lang.AbstractStringBuilder::append (29 bytes) + 1046 81 b java.util.jar.Manifest$FastInputStream::readLine (167 bytes) + 1073 82 b net.sf.cglib.asm.ByteVector::putUTF8 (394 bytes) + 1081 83 b net.sf.cglib.asm.Type::a (253 bytes) + 1087 84 s b java.lang.StringBuffer::append (8 bytes) + 1091 85 b net.sf.cglib.asm.Type::a (214 bytes) + 1116 86 b java.util.ArrayList::access$100 (5 bytes) + 1118 87 b java.lang.AbstractStringBuilder::append (48 bytes) + 1127 88 b net.sf.cglib.asm.Type::getArgumentTypes (131 bytes) + 1154 89 b sun.misc.MetaIndex::mayContain (51 bytes) + 1181 90 b sun.reflect.generics.parser.SignatureParser::advance (37 bytes) + 1189 91 b java.lang.Character::isWhitespace (5 bytes) + 1189 92 b java.lang.Character::isWhitespace (9 bytes) + 1190 93 b java.lang.CharacterDataLatin1::isWhitespace (23 bytes) + 1190 94 b sun.reflect.generics.parser.SignatureParser::parseIdentifier (115 bytes) + 1253 95 b java.util.Properties$LineReader::readLine (452 bytes) +made not compilable java.io.Reader::read + 1287 96 b java.util.zip.ZStreamRef::address (5 bytes) + 1314 97 b java.lang.StringBuilder::append (8 bytes) + 1323 98 n java.lang.Thread::currentThread (0 bytes) (static) + 1327 99 b java.lang.String::compareTo (150 bytes) + 1346 100 b sun.misc.URLClassPath::access$100 (5 bytes) + 1353 101 b java.util.HashMap::put (126 bytes) + 1377 102 b java.lang.AbstractStringBuilder::<init> (12 bytes) + 1396 103 b java.util.zip.InflaterInputStream::ensureOpen (18 bytes) + 1399 104 b java.util.concurrent.ConcurrentSkipListMap::findNode (108 bytes) + 1407 105 b java.util.HashMap::transfer (83 bytes) + 1413 106 b sun.reflect.ClassFileAssembler::emitByte (11 bytes) + 1414 107 b sun.reflect.ByteVectorImpl::add (38 bytes) + 1422 108 b java.util.HashMap$HashIterator::nextEntry (99 bytes) + 1425 109 b java.lang.reflect.Method::getName (5 bytes) + 1428 110 n java.lang.Object::hashCode (0 bytes) + 1443 111 b java.lang.String::startsWith (7 bytes) + 1452 112 b java.lang.ref.Reference::get (5 bytes) + 1453 113 b java.lang.Class::searchMethods (90 bytes) + 1463 114 b java.util.ArrayList$Itr::hasNext (20 bytes) + 1463 115 b java.util.HashMap$HashIterator::<init> (63 bytes) + 1473 116 b java.util.ArrayList::ensureCapacityInternal (26 bytes) + 1478 117 b java.util.ArrayList::add (29 bytes) + 1484 118 n java.lang.Object::clone (0 bytes) + 1489 119 b java.lang.reflect.AccessibleObject::<init> (5 bytes) + 1490 120 b sun.reflect.ReflectionFactory::langReflectAccess (15 bytes) + 1493 121 b java.lang.Class::copyMethods (36 bytes) + 1501 122 b java.util.ArrayList$Itr::checkForComodification (23 bytes) + 1503 123 b java.util.Arrays::hashCode (56 bytes) + 1505 124 b java.lang.reflect.ReflectAccess::copyMethod (5 bytes) + 1507 125 b java.lang.reflect.Method::copy (67 bytes) + 1508 126 b java.lang.reflect.Method::<init> (68 bytes) + 1510 127 b java.lang.reflect.Method::getDeclaringClass (5 bytes) + 1521 128 b java.lang.reflect.Method::getParameterTypes (14 bytes) + 1524 129 !b com.sun.jersey.core.reflection.ReflectionHelper::findMethodOnClass (94 bytes) + 1545 130 b java.lang.System::getSecurityManager (4 bytes) + 1546 131 b java.util.ArrayList$Itr::next (66 bytes) + 1547 132 b java.util.ArrayList::access$200 (5 bytes) + 1553 133 b java.util.HashMap$HashIterator::hasNext (13 bytes) + 1555 134 b java.util.HashMap$Entry::<init> (26 bytes) + 1558 135 b java.lang.Integer::valueOf (54 bytes) + 1560 136 b java.lang.ref.SoftReference::get (29 bytes) + 1565 137 s b java.lang.reflect.Method::declaredAnnotations (39 bytes) + 1569 138 b java.lang.StringBuilder::toString (17 bytes) + 1574 139 b java.lang.Class::clearCachesOnClassRedefinition (70 bytes) + 1580 140 b java.util.AbstractCollection::<init> (5 bytes) + 1581 141 b java.lang.Class::checkInitted (19 bytes) + 1595 142 b com.sun.jersey.core.reflection.AnnotatedMethod::hasParameterAnnotations (80 bytes) + 1604 143 n java.lang.Class::getClassLoader0 (0 bytes) + 1607 144 b java.lang.Class::getMethod0 (97 bytes) + 1634 145 b java.util.HashMap$Entry::getKey (5 bytes) + 1634 146 b java.util.Collections$EmptyMap::get (2 bytes) + 1636 147 n java.lang.String::intern (0 bytes) + 1636 148 b java.lang.reflect.Method::getAnnotation (26 bytes) + 1640 148 java.lang.reflect.Method::getAnnotation (26 bytes) made not entrant + 1641 149 b java.util.HashMap$KeyIterator::next (8 bytes) + 1646 150 b java.lang.reflect.Method::getReturnType (5 bytes) + 1647 151 b java.lang.Class::getName (21 bytes) + 1658 152 n java.lang.Class::getInterfaces (0 bytes) + 1659 153 b java.util.AbstractList::<init> (10 bytes) + 1660 154 b com.sun.jersey.core.reflection.AnnotatedMethod::hasMethodAnnotations (43 bytes) + 1676 155 b java.lang.Character::toLowerCase (9 bytes) + 1677 156 b java.lang.CharacterDataLatin1::toLowerCase (39 bytes) + 1688 157 b java.util.concurrent.locks.AbstractQueuedSynchronizer::getState (5 bytes) + 1695 158 b java.lang.Class::argumentTypesToString (78 bytes) + 1701 2 sun.nio.cs.UTF_8$Decoder::decode (640 bytes) made zombie + 1701 29 java.util.HashMap::get (79 bytes) made zombie + 1702 159 b java.lang.Class::checkMemberAccess (78 bytes) + 1704 160 b sun.reflect.generics.parser.SignatureParser::parsePackageNameAndSimpleClassTypeSignature (139 bytes) + 1734 161 s!b sun.misc.URLClassPath::getLoader (182 bytes) + 1772 162 b java.util.Properties::loadConvert (505 bytes) + 1789 163 b sun.reflect.ClassFileAssembler::emitConstantPoolUTF8 (50 bytes) + 1809 164 !b sun.reflect.UTF8::encode (189 bytes) + 1818 165 b sun.reflect.UTF8::utf8Length (81 bytes) + 1841 166 b sun.nio.cs.UTF_8$Decoder::decodeArrayLoop (543 bytes) + 1869 167 b java.util.regex.Pattern$BmpCharProperty::match (50 bytes) +made not compilable java.util.regex.Pattern$CharProperty::isSatisfiedBy + 1882 168 b java.util.regex.Matcher::search (109 bytes) + 2023 169 b java.lang.Character::charCount (12 bytes) + 2023 170 b java.lang.Character::isHighSurrogate (18 bytes) + 2024 171 b java.lang.String::codePointAt (44 bytes) + 2025 172 b java.lang.Character::codePointAtImpl (41 bytes) + 2036 173 b java.nio.Buffer::checkIndex (22 bytes) + 2036 174 b java.nio.DirectLongBufferU::ix (10 bytes) + 2040 175 b java.util.concurrent.locks.AbstractOwnableSynchronizer::setExclusiveOwnerThread (6 bytes) + 2041 176 b sun.misc.URLClassPath::getResource (79 bytes) +made not compilable java.net.URLStreamHandler::openConnection + 2059 176 sun.misc.URLClassPath::getResource (79 bytes) made not entrant + 2063 177 b com.google.common.io.LineBuffer::add (201 bytes) +made not compilable com.google.common.io.LineBuffer::handleLine + 2073 178 b java.util.regex.Pattern::has (15 bytes) + 2074 179 b java.util.ArrayList::<init> (44 bytes) + 2080 180 b java.net.URL::getHost (5 bytes) + 2089 168 java.util.regex.Matcher::search (109 bytes) made not entrant + 2089 167 java.util.regex.Pattern$BmpCharProperty::match (50 bytes) made not entrant + 2124 181 b java.util.HashMap::addEntry (58 bytes) + 2140 182 n java.lang.System::nanoTime (0 bytes) (static) + 2145 183 b java.util.zip.Inflater::ensureOpen (47 bytes) + 2204 184 b java.lang.String::trim (87 bytes) + 3019 185 b java.lang.String::indexOf (7 bytes) + 3023 186 b java.util.regex.Pattern$Curly::match0 (174 bytes) + 3030 187 b java.util.regex.Matcher::reset (83 bytes) + 3035 188 b java.util.regex.Pattern$Node::match (27 bytes) + 3036 148 java.lang.reflect.Method::getAnnotation (26 bytes) made zombie + 3037 189 b java.util.regex.Pattern$BmpCharProperty::match (50 bytes) + 3039 190 b java.util.regex.Pattern$Ctype::isSatisfiedBy (24 bytes) + 3039 191 b java.util.regex.ASCII::isType (15 bytes) + 3040 192 b java.util.regex.ASCII::getType (17 bytes) + 3042 193 b java.util.regex.Pattern$CharProperty::match (56 bytes) + 3045 194 b java.util.regex.Pattern$Slice::match (79 bytes) + 3052 195 b java.util.regex.Matcher::match (109 bytes) + 3062 196 b java.util.regex.Pattern$Curly::match (86 bytes) + 3074 197 b com.google.common.collect.AbstractIndexedListIterator::hasNext (17 bytes) + 3078 198 b java.util.concurrent.ConcurrentHashMap$Segment::rehash (262 bytes) + 3094 101 java.util.HashMap::put (126 bytes) made not entrant + 15172 199 b java.util.concurrent.locks.AbstractOwnableSynchronizer::getExclusiveOwnerThread (5 bytes) + 17427 200 n sun.misc.Unsafe::compareAndSwapInt (0 bytes) + 17431 193 java.util.regex.Pattern$CharProperty::match (56 bytes) made not entrant + 17436 201 b java.lang.String::toUpperCase (442 bytes) + 17484 202 !b sun.misc.URLClassPath$JarLoader::getResource (91 bytes) + 17518 203 b java.nio.Buffer::position (43 bytes) + 17519 167 java.util.regex.Pattern$BmpCharProperty::match (50 bytes) made zombie + 17519 176 sun.misc.URLClassPath::getResource (79 bytes) made zombie + 17519 168 java.util.regex.Matcher::search (109 bytes) made zombie + 17519 204 b java.nio.ByteBuffer::arrayOffset (35 bytes) + 17520 205 b java.nio.CharBuffer::arrayOffset (35 bytes) + 17524 206 b java.nio.Buffer::position (5 bytes) + 17524 207 b sun.nio.cs.UTF_8$Encoder::encodeArrayLoop (489 bytes) + 17531 208 b java.nio.charset.CoderResult::isUnderflow (13 bytes) + 17539 209 b java.nio.Buffer::limit (62 bytes) + 17540 210 b java.nio.Buffer::<init> (121 bytes) + 17541 211 b java.nio.Buffer::remaining (10 bytes) + 17542 212 b java.nio.charset.CoderResult::isOverflow (14 bytes) + 17542 213 b java.nio.Buffer::hasRemaining (17 bytes) + 17542 214 b java.nio.CharBuffer::hasArray (20 bytes) + 17543 215 b java.nio.ByteBuffer::hasArray (20 bytes) + 17543 216 !b java.nio.CharBuffer::wrap (20 bytes) + 17545 217 b java.nio.HeapCharBuffer::<init> (14 bytes) + 17546 218 b java.nio.CharBuffer::<init> (22 bytes) + 17547 219 b sun.nio.cs.StreamEncoder::ensureOpen (18 bytes) + 17547 220 b sun.nio.cs.StreamEncoder::implWrite (156 bytes) + 17556 221 !b java.nio.charset.CharsetEncoder::encode (285 bytes) + 17561 222 b sun.nio.cs.UTF_8$Encoder::encodeLoop (28 bytes) + 17562 223 !b sun.nio.cs.StreamEncoder::write (78 bytes) + 17565 224 b com.google.gson.stream.JsonWriter::string (365 bytes) + 17577 225 b sun.nio.cs.StreamEncoder::write (37 bytes) + 17588 224 com.google.gson.stream.JsonWriter::string (365 bytes) made not entrant + 17589 226 b com.google.gson.stream.JsonWriter::string (365 bytes) + 17618 227 b java.lang.String::toLowerCase (477 bytes) + 17637 10 java.lang.String::equals (88 bytes) made not entrant + 17641 73 s java.io.ByteArrayOutputStream::write (32 bytes) made not entrant + 17656 7 sun.nio.cs.UTF_8$Decoder::decode (640 bytes) made not entrant + 17656 34 java.nio.charset.CharsetDecoder::maxCharsPerByte (5 bytes) made not entrant + 17656 38 java.util.zip.ZipFile::getZipEntry (245 bytes) made not entrant + 17658 228 b sun.nio.cs.UTF_8$Decoder::decode (640 bytes) + 17672 229 b java.lang.StringBuilder::<init> (7 bytes) + 17680 230 b java.math.BigInteger::destructiveMulAdd (150 bytes) + 17685 101 java.util.HashMap::put (126 bytes) made zombie + 17685 231 b java.util.regex.Pattern$Single::isSatisfiedBy (14 bytes) + 17687 232 b java.lang.Integer::parseInt (261 bytes) + 17693 233 b java.lang.Character::digit (6 bytes) + 17693 234 b java.lang.Character::digit (10 bytes) + 17694 235 b java.lang.CharacterDataLatin1::digit (91 bytes) + 17698 236 b sun.security.util.Cache$EqualByteArray::hashCode (57 bytes) + 17721 237 b java.lang.String::equals (88 bytes) + 17733 238 b java.math.BigInteger::stripLeadingZeroBytes (132 bytes) + 17740 239 s b java.io.ByteArrayInputStream::read (36 bytes) + 17749 240 s b java.io.ByteArrayInputStream::available (10 bytes) + 17756 241 b java.io.ByteArrayInputStream::mark (9 bytes) + 17768 242 b sun.security.util.DerInputStream::getLength (111 bytes) +made not compilable java.io.InputStream::read + 17774 243 b java.util.Arrays::copyOf (19 bytes) + 17781 244 b java.io.DataInputStream::readUTF (501 bytes) + 17822 245 b sun.security.util.ObjectIdentifier::check (78 bytes) + 17829 246 s b java.io.ByteArrayOutputStream::write (32 bytes) + 17854 247 b sun.security.util.DerInputStream::<init> (19 bytes) + 17858 248 b sun.security.util.DerInputStream::available (8 bytes) + 17863 249 !b java.security.cert.Certificate::hashCode (34 bytes) + 17889 202 ! sun.misc.URLClassPath$JarLoader::getResource (91 bytes) made not entrant + 17919 250 b java.util.Arrays::equals (54 bytes) + 17930 251 b java.util.Arrays::hashCode (44 bytes) + 17936 252 b java.math.BigInteger::mulAdd (81 bytes) + 17957 195 java.util.regex.Matcher::match (109 bytes) made not entrant + 17965 4 % b com.sun.crypto.provider.AESCrypt::<clinit> @ 724 (1577 bytes) + 18073 232 java.lang.Integer::parseInt (261 bytes) made not entrant + 18142 233 java.lang.Character::digit (6 bytes) made not entrant + 18142 74 java.lang.CharacterData::of (120 bytes) made not entrant + 18142 91 java.lang.Character::isWhitespace (5 bytes) made not entrant + 18142 94 sun.reflect.generics.parser.SignatureParser::parseIdentifier (115 bytes) made not entrant + 18142 92 java.lang.Character::isWhitespace (9 bytes) made not entrant + 18142 155 java.lang.Character::toLowerCase (9 bytes) made not entrant + 18142 201 java.lang.String::toUpperCase (442 bytes) made not entrant + 18142 234 java.lang.Character::digit (10 bytes) made not entrant + 18142 227 java.lang.String::toLowerCase (477 bytes) made not entrant + 18143 253 b java.lang.Character::toUpperCaseEx (30 bytes) + 18145 10 java.lang.String::equals (88 bytes) made zombie + 18148 254 b java.io.BufferedInputStream::getBufIfOpen (21 bytes) + 18148 255 s b java.io.BufferedInputStream::read (49 bytes) + 18151 256 b java.io.DataInputStream::readChar (40 bytes) + 18158 193 java.util.regex.Pattern$CharProperty::match (56 bytes) made zombie + 18158 224 com.google.gson.stream.JsonWriter::string (365 bytes) made zombie + 18158 5 % b sun.text.normalizer.NormalizerDataReader::read @ 11 (83 bytes) + 18165 256 java.io.DataInputStream::readChar (40 bytes) made not entrant + 18169 257 b java.io.DataInputStream::readInt (72 bytes) + 18180 258 b java.io.DataInputStream::readChar (40 bytes) + 18192 259 b java.lang.StringBuilder::append (8 bytes) + 18203 260 b java.lang.CharacterData::of (120 bytes) + 18206 261 b java.lang.Character::toLowerCase (6 bytes) +made not compilable java.lang.CharacterData::toLowerCase + 18208 262 b java.lang.String::toLowerCase (477 bytes) + 18229 263 b java.lang.String::toUpperCase (442 bytes) + 18251 264 b java.lang.Character::isWhitespace (5 bytes) +made not compilable java.lang.CharacterData::isWhitespace + 18251 265 b java.lang.Character::isWhitespace (9 bytes) + 18255 266 b sun.text.normalizer.NormalizerBase::normalize (223 bytes) + 18262 267 !b sun.security.x509.AVA::toRFC2253CanonicalString (484 bytes) +made not compilable java.nio.charset.spi.CharsetProvider::charsetForName + 18334 268 b java.lang.String::regionMatches (157 bytes) +made not compilable java.lang.CharacterData::toUpperCase + 18347 7 sun.nio.cs.UTF_8$Decoder::decode (640 bytes) made zombie + 18347 34 java.nio.charset.CharsetDecoder::maxCharsPerByte (5 bytes) made zombie + 18347 73 s java.io.ByteArrayOutputStream::write (32 bytes) made zombie + 18347 38 java.util.zip.ZipFile::getZipEntry (245 bytes) made zombie + 18348 232 java.lang.Integer::parseInt (261 bytes) made zombie + 18348 249 ! java.security.cert.Certificate::hashCode (34 bytes) made not entrant + 18357 269 b sun.security.provider.SHA::implCompress (491 bytes) + 18429 6 % b com.sun.crypto.provider.ARCFOURCipher::crypt @ 15 (129 bytes) + 18435 270 n sun.misc.Unsafe::getInt (0 bytes) + 18436 271 b java.lang.Integer::reverseBytes (26 bytes) + 18436 272 b com.sun.crypto.provider.ARCFOURCipher::crypt (129 bytes) + 18442 202 ! sun.misc.URLClassPath$JarLoader::getResource (91 bytes) made zombie + 18442 195 java.util.regex.Matcher::match (109 bytes) made zombie + 49381 233 java.lang.Character::digit (6 bytes) made zombie + 49381 74 java.lang.CharacterData::of (120 bytes) made zombie + 49381 91 java.lang.Character::isWhitespace (5 bytes) made zombie + 49381 94 sun.reflect.generics.parser.SignatureParser::parseIdentifier (115 bytes) made zombie + 49381 92 java.lang.Character::isWhitespace (9 bytes) made zombie + 49381 155 java.lang.Character::toLowerCase (9 bytes) made zombie + 49381 161 s! sun.misc.URLClassPath::getLoader (182 bytes) made not entrant + 49381 201 java.lang.String::toUpperCase (442 bytes) made zombie + 49381 234 java.lang.Character::digit (10 bytes) made zombie + 49381 227 java.lang.String::toLowerCase (477 bytes) made zombie + 49382 273 b sun.misc.URLClassPath$LoaderSearchCursor::nextLoader (211 bytes) + 49406 66 java.util.HashMap::get (79 bytes) made not entrant + 49420 274 b java.util.HashMap$EntryIterator::next (5 bytes) + 49422 275 b java.util.HashMap$EntryIterator::next (5 bytes) + 49434 276 b java.util.HashMap$Entry::hashCode (38 bytes) + 49436 256 java.io.DataInputStream::readChar (40 bytes) made zombie + 49436 277 b java.util.AbstractMap::hashCode (43 bytes) +made not compilable java.util.AbstractMap::entrySet
\ No newline at end of file diff --git a/caliper/src/test/resources/com/google/caliper/bridge/jdk7-flags.txt b/caliper/src/test/resources/com/google/caliper/bridge/jdk7-flags.txt new file mode 100644 index 0000000..a09cf57 --- /dev/null +++ b/caliper/src/test/resources/com/google/caliper/bridge/jdk7-flags.txt @@ -0,0 +1,832 @@ + bool AHRByDeathCollectTimeRatio = false {product} + bool AHRByMinorPauseTimeMajorFreq = false {product} + bool AHRByPromoToAllocRatio = false {product} + bool AHRBySurvivorAge = false {product} + uintx AHRIncrementSize = 5242880 {product} + uintx AHRMaxDeathCollectTimeRatio = 55 {product} + uintx AHRMaxMinorInvocationsPerMajor = 30 {product} + uintx AHRMaxMinorPauseTimeMillis = 100 {product} + uintx AHRMaxPromoToAllocRatio = 25 {product} + uintx AHRMaxRatio = 100 {product} + uintx AHRMaxSize = 104857600 {product} + uintx AHRMaxSurvivorAge = 10 {product} + uintx AHRMinDeathCollectTimeRatio = 50 {product} + uintx AHRMinMinorInvocationsPerMajor = 5 {product} + uintx AHRMinMinorPauseTimeMillis = 150 {product} + uintx AHRMinPromoToAllocRatio = 2 {product} + uintx AHRMinSurvivorAge = 2 {product} + bool AdaptiveHeapRebalance = false {product} + uintx AdaptivePermSizeWeight = 20 {product} + uintx AdaptiveSizeDecrementScaleFactor = 4 {product} + uintx AdaptiveSizeMajorGCDecayTimeScale = 10 {product} + uintx AdaptiveSizePausePolicy = 0 {product} + uintx AdaptiveSizePolicyCollectionCostMargin = 50 {product} + uintx AdaptiveSizePolicyInitializingSteps = 20 {product} + uintx AdaptiveSizePolicyOutputInterval = 0 {product} + uintx AdaptiveSizePolicyWeight = 10 {product} + uintx AdaptiveSizeThroughPutPolicy = 0 {product} + uintx AdaptiveTimeWeight = 25 {product} + bool AdjustConcurrency = false {product} + bool AggressiveOpts = false {product} + intx AliasLevel = 3 {product} + intx AllocateInstancePrefetchLines = 1 {product} + intx AllocatePrefetchDistance = 192 {product} + intx AllocatePrefetchInstr = 0 {product} + intx AllocatePrefetchLines = 4 {product} + intx AllocatePrefetchStepSize = 64 {product} + intx AllocatePrefetchStyle = 1 {product} + bool AllowJNIEnvProxy = false {product} + bool AllowParallelDefineClass = false {product} + bool AllowUserSignalHandlers = false {product} + bool AlwaysActAsServerClassMachine = false {product} + bool AlwaysCompileLoopMethods = false {product} + intx AlwaysInflate = 0 {product} + bool AlwaysLockClassLoader = false {product} + bool AlwaysPreTouch = false {product} + bool AlwaysRestoreFPU = false {product} + bool AlwaysTenure = false {product} + bool AnonymousClasses = false {product} + uintx ArraycopyDstPrefetchDistance = 0 {product} + uintx ArraycopySrcPrefetchDistance = 0 {product} + bool AssertOnSuspendWaitFailure = false {product} + intx Atomics = 0 {product} + intx AutoBoxCacheMax = 128 {C2 product} + uintx AutoGCSelectPauseMillis = 5000 {product} + intx BCEATraceLevel = 0 {product} + intx BackEdgeThreshold = 100000 {pd product} + bool BackgroundCompilation = true {pd product} + uintx BaseFootPrintEstimate = 268435456 {product} + intx BiasedLockingBulkRebiasThreshold = 20 {product} + intx BiasedLockingBulkRevokeThreshold = 40 {product} + intx BiasedLockingDecayTime = 25000 {product} + intx BiasedLockingStartupDelay = 4000 {product} + bool BindCMSThreadToCPU = false {diagnostic} + bool BindGCTaskThreadsToCPUs = false {product} + intx BlockCopyLowLimit = 2048 {product} + bool BlockLayoutByFrequency = true {C2 product} + intx BlockLayoutMinDiamondPercentage = 20 {C2 product} + bool BlockLayoutRotateLoops = true {C2 product} + bool BlockOffsetArrayUseUnallocatedBlock = false {diagnostic} + intx BlockZeroingLowLimit = 2048 {product} + bool BranchOnRegister = false {C2 product} + bool BytecodeVerificationLocal = false {product} + bool BytecodeVerificationRemote = true {product} + bool C1OptimizeVirtualCallProfiling = true {C1 product} + bool C1ProfileBranches = true {C1 product} + bool C1ProfileCalls = true {C1 product} + bool C1ProfileCheckcasts = true {C1 product} + bool C1ProfileInlinedCalls = true {C1 product} + bool C1ProfileVirtualCalls = true {C1 product} + bool C1UpdateMethodData = true {C1 product} + intx CICompilerCount = 2 {product} + bool CICompilerCountPerCPU = false {product} + bool CITime = false {product} + bool CMSAbortSemantics = false {product} + uintx CMSAbortablePrecleanMinWorkPerIteration = 100 {product} + intx CMSAbortablePrecleanWaitMillis = 100 {manageable} + uintx CMSBitMapYieldQuantum = 10485760 {product} + uintx CMSBootstrapOccupancy = 50 {product} + bool CMSClassUnloadingEnabled = false {product} + uintx CMSClassUnloadingMaxInterval = 0 {product} + bool CMSCleanOnEnter = true {product} + bool CMSCompactWhenClearAllSoftRefs = true {product} + uintx CMSConcMarkMultiple = 32 {product} + bool CMSConcurrentMTEnabled = true {product} + uintx CMSCoordinatorYieldSleepCount = 10 {product} + bool CMSDumpAtPromotionFailure = false {product} + bool CMSEdenChunksRecordAlways = false {product} + uintx CMSExpAvgFactor = 50 {product} + bool CMSExtrapolateSweep = false {product} + uintx CMSFullGCsBeforeCompaction = 0 {product} + uintx CMSIncrementalDutyCycle = 10 {product} + uintx CMSIncrementalDutyCycleMin = 0 {product} + bool CMSIncrementalMode = false {product} + uintx CMSIncrementalOffset = 0 {product} + bool CMSIncrementalPacing = true {product} + uintx CMSIncrementalSafetyFactor = 10 {product} + uintx CMSIndexedFreeListReplenish = 4 {product} + intx CMSInitiatingOccupancyFraction = -1 {product} + intx CMSInitiatingOccupancyFractionDelta = -1 {product} + intx CMSInitiatingPermOccupancyFraction = -1 {product} + intx CMSInitiatingPermOccupancyFractionDelta = -1 {product} + intx CMSInitiatingRamLimitFraction = -1 {product} + intx CMSIsTooFullPercentage = 98 {product} + double CMSLargeCoalSurplusPercent = 0.950000 {product} + double CMSLargeSplitSurplusPercent = 1.000000 {product} + bool CMSLoopWarn = false {product} + uintx CMSMaxAbortablePrecleanLoops = 0 {product} + intx CMSMaxAbortablePrecleanTime = 5000 {product} + uintx CMSOldPLABMax = 1024 {product} + uintx CMSOldPLABMin = 16 {product} + uintx CMSOldPLABNumRefills = 4 {product} + uintx CMSOldPLABReactivityCeiling = 10 {product} + uintx CMSOldPLABReactivityFactor = 2 {product} + bool CMSOldPLABResizeQuicker = false {product} + uintx CMSOldPLABToleranceFactor = 4 {product} + bool CMSPLABRecordAlways = true {product} + uintx CMSParPromoteBlocksToClaim = 16 {product} + bool CMSParallelInitialMarkEnabled = false {product} + bool CMSParallelRemarkEnabled = true {product} + uintx CMSParallelSTWFullGCHeapRegionSize = 1048576 {product} + bool CMSParallelSurvivorRemarkEnabled = true {product} + bool CMSPermGenPrecleaningEnabled = true {product} + uintx CMSPrecleanDenominator = 3 {product} + uintx CMSPrecleanIter = 3 {product} + uintx CMSPrecleanNumerator = 2 {product} + bool CMSPrecleanRefLists1 = true {product} + bool CMSPrecleanRefLists2 = false {product} + bool CMSPrecleanSurvivors1 = false {product} + bool CMSPrecleanSurvivors2 = true {product} + uintx CMSPrecleanThreshold = 1000 {product} + bool CMSPrecleaningEnabled = true {product} + bool CMSPrintChunksInDump = false {product} + bool CMSPrintEdenSurvivorChunks = false {product} + bool CMSPrintObjectsInDump = false {product} + uintx CMSRemarkVerifyVariant = 1 {product} + bool CMSReplenishIntermediate = true {product} + uintx CMSRescanMultiple = 32 {product} + uintx CMSRevisitStackSize = 1048576 {product} + uintx CMSSamplingGrain = 16384 {product} + bool CMSScavengeBeforeRemark = false {product} + uintx CMSScheduleRemarkEdenPenetration = 50 {product} + uintx CMSScheduleRemarkEdenSizeThreshold = 2097152 {product} + uintx CMSScheduleRemarkSamplingRatio = 5 {product} + double CMSSmallCoalSurplusPercent = 1.050000 {product} + double CMSSmallSplitSurplusPercent = 1.100000 {product} + bool CMSSplitIndexedFreeListBlocks = true {product} + intx CMSTriggerPermRatio = 80 {product} + intx CMSTriggerRatio = 80 {product} + bool CMSUseGCOverheadLimit = true {product} + intx CMSWaitDuration = 2000 {manageable} + uintx CMSWorkQueueDrainThreshold = 10 {product} + bool CMSYield = true {product} + uintx CMSYieldSleepCount = 0 {product} + intx CMSYoungGenPerWorker = 67108864 {pd product} + uintx CMS_FLSPadding = 1 {product} + uintx CMS_FLSWeight = 75 {product} + uintx CMS_SweepPadding = 1 {product} + uintx CMS_SweepTimerThresholdMillis = 10 {product} + uintx CMS_SweepWeight = 75 {product} + uintx CPUForCMSThread = 0 {diagnostic} + bool CheckJNICalls = false {product} + bool ClassUnloading = true {product} + intx ClearFPUAtPark = 0 {product} + bool ClipInlining = true {product} + uintx CodeCacheExpansionSize = 32768 {pd product} + uintx CodeCacheFlushingMinimumFreeSpace = 1536000 {product} + uintx CodeCacheMinimumFreeSpace = 512000 {product} + bool CollectGen0First = false {product} + bool CompactFields = true {product} + intx CompilationPolicyChoice = 0 {product} + intx CompilationRepeat = 0 {C1 product} +ccstrlist CompileCommand = {product} + ccstr CompileCommandFile = {product} +ccstrlist CompileOnly = {product} + intx CompileThreshold = 10000 {pd product} + bool CompilerThreadHintNoPreempt = true {product} + intx CompilerThreadPriority = -1 {product} + intx CompilerThreadStackSize = 0 {pd product} + uintx ConcGCThreads = 0 {product} + intx ConditionalMoveLimit = 3 {C2 pd product} + bool ConvertSleepToYield = true {pd product} + bool ConvertYieldToSleep = false {product} + bool CreateMinidumpOnCrash = false {product} + bool CycleTime = false {product} + bool DTraceAllocProbes = false {product} + bool DTraceMethodProbes = false {product} + bool DTraceMonitorProbes = false {product} + bool DeallocateHeapPages = true {product} + bool DeallocateStackPages = true {product} + uintx DeallocateStackPagesMinIntervalMs = 100 {product} + bool DebugContinuation = false {diagnostic} + bool DebugInlinedCalls = true {diagnostic} + bool DebugNonSafepoints = false {diagnostic} + uintx DefaultMaxRAMFraction = 4 {product} + intx DefaultThreadPriority = -1 {product} + bool DeferInitialCardMark = false {diagnostic} + intx DeferPollingPageLoopCount = -1 {product} + intx DeferThrSuspendLoopCount = 4000 {product} + bool DeoptimizeRandom = false {product} + bool DisableAttachMechanism = false {product} + bool DisableExplicitGC = false {product} +ccstrlist DisableIntrinsic = {diagnostic} + bool DisplayVMOutput = true {diagnostic} + bool DisplayVMOutputToStderr = false {product} + bool DisplayVMOutputToStdout = false {product} + bool DoEscapeAnalysis = true {C2 product} + intx DominatorSearchLimit = 1000 {C2 diagnostic} + bool DontCompileHugeMethods = true {product} + bool DontYieldALot = false {pd product} + bool DumpSharedSpaces = false {product} + bool EagerXrunInit = false {product} + intx EliminateAllocationArraySizeLimit = 64 {C2 product} + bool EliminateAllocations = true {C2 product} + bool EliminateAutoBox = false {C2 diagnostic} + bool EliminateLocks = true {C2 product} + intx EmitSync = 0 {product} + bool EnableInvokeDynamic = true {diagnostic} + uintx ErgoHeapSizeLimit = 0 {product} + ccstr ErrorFile = {product} + ccstr ErrorReportServer = {product} + bool EstimateArgEscape = true {product} + intx EventLogLength = 2000 {product} + bool ExplicitGCInvokesConcurrent = false {product} + bool ExplicitGCInvokesConcurrentAndUnloadsClasses = false {product} + bool ExtendedDTraceProbes = false {product} + bool FLSAlwaysCoalesceLarge = false {product} + uintx FLSCoalescePolicy = 2 {product} + double FLSLargestBlockCoalesceProximity = 0.990000 {product} + bool FLSVerifyAllHeapReferences = false {diagnostic} + bool FLSVerifyIndexTable = false {diagnostic} + bool FLSVerifyLists = false {diagnostic} + bool FailOverToOldVerifier = true {product} + bool FastCardTableScan = true {product} + bool FastTLABRefill = true {product} + intx FenceInstruction = 0 {product} + intx FieldsAllocationStyle = 1 {product} + bool FilterSpuriousWakeups = true {product} + bool ForceNUMA = false {product} + bool ForceTimeHighResolution = false {product} + intx FreqInlineSize = 325 {pd product} + bool FullProfileOnReInterpret = true {diagnostic} + double G1ConcMarkStepDurationMillis = 10.000000 {product} + intx G1ConcRefinementGreenZone = 0 {product} + intx G1ConcRefinementRedZone = 0 {product} + intx G1ConcRefinementServiceIntervalMillis = 300 {product} + uintx G1ConcRefinementThreads = 0 {product} + intx G1ConcRefinementThresholdStep = 0 {product} + intx G1ConcRefinementYellowZone = 0 {product} + intx G1ConfidencePercent = 50 {product} + uintx G1HeapRegionSize = 0 {product} + intx G1MarkRegionStackSize = 1048576 {product} + bool G1PrintHeapRegions = false {diagnostic} + bool G1PrintRegionLivenessInfo = false {diagnostic} + intx G1RSetRegionEntries = 0 {product} + uintx G1RSetScanBlockSize = 64 {product} + intx G1RSetSparseRegionEntries = 0 {product} + intx G1RSetUpdatingPauseTimePercent = 10 {product} + intx G1RefProcDrainInterval = 10 {product} + uintx G1ReservePercent = 10 {product} + uintx G1SATBBufferEnqueueingThresholdPercent = 60 {product} + intx G1SATBBufferSize = 1024 {product} + bool G1SummarizeConcMark = false {diagnostic} + bool G1SummarizeRSetStats = false {diagnostic} + intx G1SummarizeRSetStatsPeriod = 0 {diagnostic} + bool G1TraceConcRefinement = false {diagnostic} + intx G1UpdateBufferSize = 256 {product} + bool G1UseAdaptiveConcRefinement = true {product} + uintx GCDrainStackTargetSize = 64 {product} + uintx GCHeapFreeLimit = 2 {product} + uintx GCLockerEdenExpansionPercent = 5 {product} + bool GCLockerInvokesConcurrent = false {product} + uintx GCLogFileSize = 0 {product} + bool GCOverheadReporting = false {product} + intx GCOverheadReportingPeriodMS = 100 {product} + bool GCParallelVerificationEnabled = true {diagnostic} + uintx GCPauseIntervalMillis = 0 {product} + uintx GCTaskTimeStampEntries = 200 {product} + uintx GCTimeLimit = 98 {product} + uintx GCTimeRatio = 99 {product} + bool GoogleAdjustGCThreads = false {product} + bool GoogleAgent = true {product} + ccstr GoogleAgentFlags = {product} + bool GoogleAgentWS = false {product} + ccstr GoogleAgentWSFlags = {product} + bool GoogleAlignInterpreterCalls = true {product} + bool GoogleDetailOmittedStackTrace = true {product} + uintx GoogleGCHeapFreeLimitPolicy = 2 {product} + bool GoogleGetMethodsInOrder = false {diagnostic} + bool GoogleGetMethodsReverse = false {diagnostic} + bool GoogleHeapInstrumentation = false {product} + bool GoogleHeapMonitor = true {product} + bool GoogleHeapSamplingInstrumentation = false {product} + bool GoogleInheritAltSigStack = false {product} + uintx GoogleLogRotationSize = 0 {diagnostic} + uintx GoogleMaxGarbageHeapzTraces = 200 {product} + bool GoogleScoping = false {product} + uintx GoogleSoftRefLRUPolicy = 0 {product} + uintx GoogleSoftRefLRUPolicyExcludedMB = 0 {product} + uintx GoogleUseConstantOMProvision = 0 {product} + bool GoogleUseLibunwind = false {pd product} + bool GoogleUseSSEForVM = false {product} + intx GuaranteedSafepointInterval = 1000 {diagnostic} + ccstr HPILibPath = {product} + uintx HeapBaseMinAddress = 2147483648 {pd product} + bool HeapDumpAfterFullGC = false {manageable} + bool HeapDumpBeforeFullGC = false {manageable} + bool HeapDumpOnOutOfMemoryError = false {manageable} + ccstr HeapDumpPath = {manageable} + uintx HeapFirstMaximumCompactionCount = 3 {product} + uintx HeapMaximumCompactionInterval = 20 {product} + bool HoistFinalLoads = false {product} + bool IgnoreUnrecognizedVMOptions = false {product} + uintx InitialCodeCacheSize = 2359296 {pd product} + bool InitialCompileFast = false {diagnostic} + bool InitialCompileReallyFast = false {diagnostic} + uintx InitialHeapSize := 67108864 {product} + uintx InitialRAMFraction = 64 {product} + uintx InitialSurvivorRatio = 8 {product} + intx InitialTenuringThreshold = 7 {product} + uintx InitiatingHeapOccupancyPercent = 45 {product} + bool Inline = true {product} + bool InlineGetCurrentThreadAllocatedMemory = false {product} + bool InlineMethodsWithNullUnloadedTypesInSignature = true {product} + intx InlineSmallCode = 1000 {pd product} + bool InsertMemBarAfterArraycopy = true {C2 product} + intx InteriorEntryAlignment = 4 {C2 pd product} + intx InterpreterProfilePercentage = 33 {product} + bool JNIDetachReleasesMonitors = true {product} + bool JavaMonitorsInStackTrace = true {product} + intx JavaPriority10_To_OSPriority = -1 {product} + intx JavaPriority1_To_OSPriority = -1 {product} + intx JavaPriority2_To_OSPriority = -1 {product} + intx JavaPriority3_To_OSPriority = -1 {product} + intx JavaPriority4_To_OSPriority = -1 {product} + intx JavaPriority5_To_OSPriority = -1 {product} + intx JavaPriority6_To_OSPriority = -1 {product} + intx JavaPriority7_To_OSPriority = -1 {product} + intx JavaPriority8_To_OSPriority = -1 {product} + intx JavaPriority9_To_OSPriority = -1 {product} + bool LIRFillDelaySlots = false {C1 pd product} + uintx LargePageHeapSizeThreshold = 134217728 {product} + ccstr LargePageMountPoint = {product} + uintx LargePageSizeInBytes = 0 {product} + bool LazyBootClassLoader = true {product} + bool LinkWellKnownClasses = false {diagnostic} + bool LogAHR = false {product} + bool LogCMSParallelSTWFullGC = false {product} + bool LogCompilation = false {diagnostic} + ccstr LogFile = {diagnostic} + bool LogGCOverheadLimit = false {product} + bool LogVMOutput = false {diagnostic} + bool LoopLimitCheck = true {C2 diagnostic} + intx LoopOptsCount = 43 {C2 product} + intx LoopUnrollLimit = 50 {C2 pd product} + intx LoopUnrollMin = 4 {C2 product} + bool LoopUnswitching = true {C2 product} + intx MallocVerifyInterval = 0 {diagnostic} + intx MallocVerifyStart = 0 {diagnostic} + bool ManagementServer = false {product} + uintx MarkStackSize = 32768 {product} + uintx MarkStackSizeMax = 4194304 {product} + intx MarkSweepAlwaysCompactCount = 4 {product} + uintx MarkSweepDeadRatio = 5 {product} + intx MaxBCEAEstimateLevel = 5 {product} + intx MaxBCEAEstimateSize = 150 {product} + intx MaxDirectMemorySize = -1 {product} + bool MaxFDLimit = true {product} + uintx MaxGCMinorPauseMillis = 4294967295 {product} + uintx MaxGCPauseMillis = 4294967295 {product} + uintx MaxHeapFreeRatio = 70 {product} + uintx MaxHeapSize := 1073741824 {product} + intx MaxInlineLevel = 9 {product} + intx MaxInlineSize = 35 {product} + intx MaxJavaStackTraceDepth = 1024 {product} + intx MaxJumpTableSize = 65000 {C2 product} + intx MaxJumpTableSparseness = 5 {C2 product} + intx MaxLabelRootDepth = 1100 {C2 product} + intx MaxLoopPad = 11 {C2 product} + uintx MaxNewSize = 4294901760 {product} + intx MaxNodeLimit = 65000 {C2 product} + uintx MaxPermHeapExpansion = 4194304 {product} + uintx MaxPermSize = 67108864 {pd product} + uint64_t MaxRAM = 0 {pd product} + uintx MaxRAMFraction = 4 {product} + intx MaxRecursiveInlineLevel = 1 {product} + intx MaxTenuringThreshold = 15 {product} + intx MaxTrivialSize = 6 {product} + bool MethodFlushing = true {product} + intx MethodHandlePushLimit = 3 {diagnostic} + intx MinCodeCacheFlushingInterval = 30 {product} + uintx MinHeapDeltaBytes = 131072 {product} + uintx MinHeapFreeRatio = 40 {product} + intx MinInliningThreshold = 250 {product} + intx MinJumpTableSize = 18 {C2 product} + uintx MinPermHeapExpansion = 262144 {product} + uintx MinRAMFraction = 2 {product} + uintx MinSurvivorRatio = 3 {product} + uintx MinTLABSize = 2048 {product} + bool MixedModeThreadDump = true {product} + intx MonitorBound = 0 {product} + bool MonitorInUseLists = false {product} + intx MultiArrayExpandLimit = 6 {C2 product} + bool MustCallLoadClassInternal = false {product} + intx NUMAChunkResizeWeight = 20 {product} + uintx NUMAInterleaveGranularity = 2097152 {product} + intx NUMAPageScanRate = 256 {product} + intx NUMASpaceResizeRate = 1073741824 {product} + bool NUMAStats = false {product} + intx NativeMonitorFlags = 0 {product} + intx NativeMonitorSpinLimit = 20 {product} + intx NativeMonitorTimeout = -1 {product} + bool NeedsDeoptSuspend = false {pd product} + bool NeverActAsServerClassMachine = false {pd product} + bool NeverTenure = false {product} + intx NewRatio = 2 {product} + uintx NewSize = 1048576 {product} + uintx NewSizeThreadIncrease = 4096 {pd product} + intx NmethodSweepCheckInterval = 5 {product} + intx NmethodSweepFraction = 4 {product} + intx NodeLimitFudgeFactor = 1000 {C2 product} + uintx NumberOfGCLogFiles = 0 {product} + intx NumberOfLoopInstrToAlign = 4 {C2 product} + bool ObjLifeTracking = false {product} + uintx ObjLifeTrackingCpuFraction = 100 {product} + uintx ObjLifetimeHistoBucketCount = 1024 {product} + bool ObjLifetimeHistoExportVarPerBucket = false {product} + uintx OldPLABSize = 1024 {product} + uintx OldPLABWeight = 50 {product} + uintx OldSize = 4194304 {product} + bool OmitStackTraceInFastThrow = true {product} +ccstrlist OnError = {product} +ccstrlist OnOutOfMemoryError = {product} + intx OnStackReplacePercentage = 140 {pd product} + bool OptimizeFill = false {C2 product} + bool OptimizeMethodHandles = true {diagnostic} + bool OptimizeStringConcat = false {C2 product} + bool OptoBundling = false {C2 pd product} + intx OptoLoopAlignment = 16 {pd product} + bool OptoScheduling = false {C2 pd product} + uintx PLABWeight = 75 {product} + bool PSChunkLargeArrays = true {product} + bool PSResizeByFreeRatio = false {product} + bool PSResizeByFreeRatioWithSystemGC = false {product} + intx ParGCArrayScanChunk = 50 {product} + intx ParGCCardsPerStrideChunk = 256 {diagnostic} + uintx ParGCDesiredObjsFromOverflowList = 20 {product} + intx ParGCStridesPerThread = 2 {diagnostic} + bool ParGCTrimOverflow = true {product} + bool ParGCUseLocalOverflow = false {product} + intx ParallelGCBufferWastePct = 10 {product} + bool ParallelGCRetainPLAB = false {diagnostic} + uintx ParallelGCThreads := 10 {product} + bool ParallelGCVerbose = false {product} + uintx ParallelOldDeadWoodLimiterMean = 50 {product} + uintx ParallelOldDeadWoodLimiterStdDev = 80 {product} + bool ParallelRefProcBalancingEnabled = true {product} + bool ParallelRefProcEnabled = false {product} + bool PartialPeelAtUnsignedTests = true {C2 product} + bool PartialPeelLoop = true {C2 product} + intx PartialPeelNewPhiDelta = 0 {C2 product} + bool PauseAtExit = false {diagnostic} + bool PauseAtStartup = false {diagnostic} + ccstr PauseAtStartupFile = {diagnostic} + uintx PausePadding = 1 {product} + intx PerBytecodeRecompilationCutoff = 200 {product} + intx PerBytecodeTrapLimit = 4 {product} + intx PerMethodRecompilationCutoff = 400 {product} + intx PerMethodTrapLimit = 100 {product} + bool PerfAllowAtExitRegistration = false {product} + bool PerfBypassFileSystemCheck = false {product} + intx PerfDataMemorySize = 32768 {product} + intx PerfDataSamplingInterval = 50 {product} + ccstr PerfDataSaveFile = {product} + bool PerfDataSaveToFile = false {product} + bool PerfDisableSharedMem = false {product} + intx PerfMaxStringConstLength = 1024 {product} + uintx PermGenPadding = 3 {product} + uintx PermMarkSweepDeadRatio = 20 {product} + uintx PermSize = 16777216 {pd product} + bool PostSpinYield = true {product} + intx PreBlockSpin = 10 {product} + intx PreInflateSpin = 10 {pd product} + bool PreSpinYield = false {product} + bool PreferInterpreterNativeStubs = false {pd product} + intx PrefetchCopyIntervalInBytes = -1 {product} + intx PrefetchFieldsAhead = -1 {product} + intx PrefetchScanIntervalInBytes = -1 {product} + bool PreserveAllAnnotations = false {product} + uintx PreserveMarkStackSize = 1024 {product} + uintx PretenureSizeThreshold = 0 {product} + bool PrintAdapterHandlers = false {diagnostic} + bool PrintAdaptiveSizePolicy = false {product} + bool PrintAssembly = false {diagnostic} + ccstr PrintAssemblyOptions = {diagnostic} + bool PrintBiasedLockingStatistics = false {diagnostic} + bool PrintCMSInitiationCause = false {product} + bool PrintCMSInitiationStatistics = false {product} + intx PrintCMSStatistics = 0 {product} + bool PrintCardTableStats = false {product} + bool PrintClassHistogram = false {manageable} + bool PrintClassHistogramAfterFullGC = false {manageable} + bool PrintClassHistogramBeforeFullGC = false {manageable} + bool PrintClassLoadingDateStamps = false {product} + bool PrintClassLoadingTimeStamps = false {product} + bool PrintCommandLineFlags = false {product} + bool PrintCompilation = false {product} + bool PrintCompressedOopsMode = false {diagnostic} + bool PrintConcurrentLocks = false {manageable} + bool PrintDTraceDOF = false {diagnostic} + intx PrintFLSCensus = 0 {product} + intx PrintFLSStatistics = 0 {product} + bool PrintFlagsFinal := true {product} + bool PrintFlagsInitial = false {product} + bool PrintFullGCPhaseTimes = false {product} + bool PrintGC = false {manageable} + bool PrintGCApplicationConcurrentTime = false {product} + bool PrintGCApplicationStoppedTime = false {product} + bool PrintGCDateStamps = false {manageable} + bool PrintGCDetails = false {manageable} + bool PrintGCTaskTimeStamps = false {product} + bool PrintGCTimeStamps = false {manageable} + bool PrintHeapAtGC = false {product rw} + bool PrintHeapAtGCExtended = false {product rw} + bool PrintHeapAtSIGBREAK = true {product} + bool PrintInlining = false {diagnostic} + bool PrintInterpreter = false {diagnostic} + bool PrintIntrinsics = false {diagnostic} + bool PrintJNIGCStalls = false {product} + bool PrintJNIResolving = false {product} + bool PrintMethodHandleStubs = false {diagnostic} + bool PrintNMethods = false {diagnostic} + bool PrintNativeNMethods = false {diagnostic} + bool PrintOldPLAB = false {product} + bool PrintOopAddress = false {product} + bool PrintPLAB = false {product} + bool PrintParallelOldGCPhaseTimes = false {product} + bool PrintPreciseBiasedLockingStatistics = false {C2 diagnostic} + bool PrintPromotionFailure = false {product} + bool PrintReferenceGC = false {product} + bool PrintRevisitStats = false {product} + bool PrintSafepointStatistics = false {product} + intx PrintSafepointStatisticsCount = 300 {product} + intx PrintSafepointStatisticsTimeout = -1 {product} + bool PrintSharedSpaces = false {product} + bool PrintSignatureHandlers = false {diagnostic} + bool PrintStubCode = false {diagnostic} + bool PrintTLAB = false {product} + bool PrintTenuringDistribution = false {product} + bool PrintTieredEvents = false {product} + bool PrintVMOptions = false {product} + bool PrintVMQWaitTime = false {product} + bool PrintWarnings = true {product} + uintx ProcessDistributionStride = 4 {product} + bool ProfileDynamicTypes = true {diagnostic} + bool ProfileInterpreter = true {pd product} + bool ProfileIntervals = false {product} + intx ProfileIntervalsTicks = 100 {product} + intx ProfileMaturityPercentage = 20 {product} + bool ProfileVM = false {product} + bool ProfilerPrintByteCodeStatistics = false {product} + bool ProfilerRecordPC = false {product} + uintx PromotedPadding = 3 {product} + intx QueuedAllocationWarningCount = 0 {product} + uintx RamLimit = 0 {product} + bool RangeCheckElimination = true {product} + bool RangeLimitCheck = true {C2 diagnostic} + intx ReadPrefetchInstr = 0 {product} + intx ReadSpinIterations = 100 {product} + bool ReassociateInvariants = true {C2 product} + bool ReduceBulkZeroing = true {C2 product} + bool ReduceFieldZeroing = true {C2 product} + bool ReduceInitialCardMarks = true {C2 product} + bool ReduceSignalUsage = false {product} + intx RefDiscoveryPolicy = 0 {product} + bool ReflectionWrapResolutionErrors = true {product} + bool RegisterFinalizersAtInit = true {product} + bool RelaxAccessControlCheck = false {product} + bool RequireSharedSpaces = false {product} + uintx ReservedCodeCacheSize = 50331648 {pd product} + bool ResizeOldPLAB = true {product} + bool ResizePLAB = true {product} + bool ResizeTLAB = true {pd product} + bool RestoreMXCSROnJNICalls = false {product} + bool RewriteBytecodes = true {pd product} + bool RewriteFrequentPairs = true {pd product} + intx SafepointPollOffset = 256 {C1 pd product} + intx SafepointSpinBeforeYield = 2000 {product} + bool SafepointTimeout = false {product} + intx SafepointTimeoutDelay = 10000 {product} + bool ScavengeBeforeFullGC = true {product} + intx ScavengeRootsInCode = 1 {diagnostic} + intx SelfDestructTimer = 0 {product} + bool SerializeVMOutput = true {diagnostic} + bool ShareCMSMarkBitMapWithParalleSTWFullGC = true {product} + uintx SharedDummyBlockSize = 536870912 {product} + uintx SharedMiscCodeSize = 4194304 {product} + uintx SharedMiscDataSize = 4194304 {product} + bool SharedOptimizeColdStart = true {diagnostic} + uintx SharedReadOnlySize = 10485760 {product} + uintx SharedReadWriteSize = 12582912 {product} + bool SharedSkipVerify = false {diagnostic} + bool ShowMessageBoxOnError = false {product} + intx SoftRefLRUPolicyMSPerMB = 1000 {product} + bool SplitIfBlocks = true {product} + intx StackRedPages = 1 {pd product} + intx StackShadowPages = 3 {pd product} + bool StackTraceInThrowable = true {product} + intx StackYellowPages = 2 {pd product} + bool StartAttachListener = false {product} + bool StartSuspended = false {product} + intx StarvationMonitorInterval = 200 {product} + bool StressLdcRewrite = false {product} + bool StressTieredRuntime = false {product} + uintx StringTableSize = 1009 {product} + bool SuppressFatalErrorMessage = false {product} + uintx SurvivorPadding = 3 {product} + intx SurvivorRatio = 8 {product} + intx SuspendRetryCount = 50 {product} + intx SuspendRetryDelay = 5 {product} + intx SyncFlags = 0 {product} + ccstr SyncKnobs = {product} + intx SyncVerbose = 0 {product} + uintx TLABAllocationWeight = 35 {product} + uintx TLABRefillWasteFraction = 64 {product} + uintx TLABSize = 0 {product} + bool TLABStats = true {product} + uintx TLABWasteIncrement = 4 {product} + uintx TLABWasteTargetPercent = 1 {product} + intx TargetPLABWastePct = 10 {product} + intx TargetSurvivorRatio = 50 {product} + uintx TenuredGenerationSizeIncrement = 20 {product} + uintx TenuredGenerationSizeSupplement = 80 {product} + uintx TenuredGenerationSizeSupplementDecay = 2 {product} + intx ThreadPriorityPolicy = 0 {product} + bool ThreadPriorityVerbose = false {product} + uintx ThreadSafetyMargin = 52428800 {product} + intx ThreadStackSize = 320 {pd product} + uintx ThresholdTolerance = 10 {product} + intx Tier0BackedgeNotifyFreqLog = 10 {product} + intx Tier0InvokeNotifyFreqLog = 7 {product} + intx Tier0ProfilingStartPercentage = 200 {product} + intx Tier1FreqInlineSize = 35 {C2 product} + intx Tier1Inline = 0 {C2 product} + intx Tier1LoopOptsCount = 0 {C2 product} + intx Tier1MaxInlineSize = 8 {C2 product} + intx Tier2BackEdgeThreshold = 0 {product} + intx Tier2BackedgeNotifyFreqLog = 14 {product} + intx Tier2CompileThreshold = 0 {product} + intx Tier2InvokeNotifyFreqLog = 11 {product} + intx Tier3BackEdgeThreshold = 7000 {product} + intx Tier3BackedgeNotifyFreqLog = 13 {product} + intx Tier3CompileThreshold = 2000 {product} + intx Tier3DelayOff = 2 {product} + intx Tier3DelayOn = 5 {product} + intx Tier3InvocationThreshold = 200 {product} + intx Tier3InvokeNotifyFreqLog = 10 {product} + intx Tier3LoadFeedback = 5 {product} + intx Tier3MinInvocationThreshold = 100 {product} + intx Tier4BackEdgeThreshold = 40000 {product} + intx Tier4CompileThreshold = 15000 {product} + intx Tier4InvocationThreshold = 5000 {product} + intx Tier4LoadFeedback = 3 {product} + intx Tier4MinInvocationThreshold = 600 {product} + bool TieredCompilation = false {pd product} + intx TieredCompileTaskTimeout = 50 {product} + intx TieredRateUpdateMaxTime = 25 {product} + intx TieredRateUpdateMinTime = 1 {product} + intx TieredStopAtLevel = 4 {product} + bool TimeLinearScan = false {C1 product} + bool TraceBiasedLocking = false {product} + bool TraceClassLoading = false {product rw} + bool TraceClassLoadingPreorder = false {product} + bool TraceClassResolution = false {product} + bool TraceClassUnloading = false {product rw} + bool TraceCompileTriggered = false {diagnostic} + bool TraceGen0Time = false {product} + bool TraceGen1Time = false {product} + ccstr TraceJVMTI = {product} + bool TraceJVMTIObjectTagging = false {diagnostic} + bool TraceLoaderConstraints = false {product rw} + bool TraceMonitorInflation = false {product} + bool TraceNMethodInstalls = false {diagnostic} + bool TraceOSRBreakpoint = false {diagnostic} + bool TraceParallelOldGCTasks = false {product} + intx TraceRedefineClasses = 0 {product} + bool TraceRedundantCompiles = false {diagnostic} + bool TraceSafepointCleanupTime = false {product} + bool TraceSuperWord = false {C2 product} + bool TraceSuspendWaitFailures = false {product} + bool TraceTriggers = false {diagnostic} + intx TrackedInitializationLimit = 50 {C2 product} + bool TransmitErrorReport = false {product} + intx TypeProfileMajorReceiverPercent = 90 {product} + intx TypeProfileWidth = 2 {product} + intx UnguardOnExecutionViolation = 0 {product} + bool UnlinkSymbolsALot = false {product} + bool UnlockDiagnosticVMOptions = true {diagnostic} + bool UnrollLimitCheck = true {C2 diagnostic} + bool UnsyncloadClass = false {diagnostic} + bool Use486InstrsOnly = false {product} + bool UseAdaptiveGCBoundary = false {product} + bool UseAdaptiveGenerationSizePolicyAtMajorCollection = true {product} + bool UseAdaptiveGenerationSizePolicyAtMinorCollection = true {product} + bool UseAdaptiveNUMAChunkSizing = true {product} + bool UseAdaptiveSizeDecayMajorGCCost = true {product} + bool UseAdaptiveSizePolicy = true {product} + bool UseAdaptiveSizePolicyFootprintGoal = true {product} + bool UseAdaptiveSizePolicyWithSystemGC = false {product} + bool UseAddressNop = true {product} + bool UseAltSigs = false {product} + bool UseAutoGCSelectPolicy = false {product} + bool UseBiasedLocking = true {product} + bool UseBimorphicInlining = true {C2 product} + bool UseBlockCopy = false {product} + bool UseBlockZeroing = false {product} + bool UseBoundThreads = true {product} + bool UseCBCond = false {product} + bool UseCMSBestFit = true {product} + bool UseCMSCollectionPassing = true {product} + bool UseCMSCompactAtFullCollection = true {product} + bool UseCMSInitiatingOccupancyOnly = false {product} + bool UseCMSParallelSTWFullGC = false {product} + bool UseCodeCacheFlushing = false {product} + bool UseCompiler = true {product} + bool UseCompilerSafepoints = true {product} + bool UseConcMarkSweepGC = false {product} + bool UseCondCardMark = false {product} + bool UseCountLeadingZerosInstruction = false {product} + bool UseCounterDecay = true {product} + bool UseDivMod = true {C2 product} + bool UseFPUForSpilling = false {C2 product} + bool UseFastAccessorMethods = false {product} + bool UseFastEmptyMethods = false {product} + bool UseFastJNIAccessors = true {product} + bool UseG1GC = false {product} + bool UseGCLogFileRotation = false {product} + bool UseGCOverheadLimit = true {product} + bool UseGCTaskAffinity = false {product} + bool UseHeavyMonitors = false {product} + bool UseHugeTLBFS = false {product} + bool UseIncDec = true {diagnostic} + bool UseInlineCaches = true {product} + bool UseInterpreter = true {product} + bool UseJumpTables = true {C2 product} + bool UseLWPSynchronization = true {product} + bool UseLargePages = false {pd product} + bool UseLargePagesIndividualAllocation = false {pd product} + bool UseLinuxPosixThreadCPUClocks = false {product} + bool UseLoopCounter = true {product} + bool UseLoopPredicate = true {C2 product} + bool UseMaximumCompactionOnSystemGC = true {product} + bool UseMembar = false {pd product} + bool UseNUMA = false {product} + bool UseNUMAInterleaving = false {product} + bool UseNewCode = false {diagnostic} + bool UseNewCode2 = false {diagnostic} + bool UseNewCode3 = false {diagnostic} + bool UseNewLongLShift = false {product} + bool UseNiagaraInstrs = false {product} + bool UseOSErrorReporting = false {pd product} + bool UseOldInlining = true {C2 product} + bool UseOnStackReplacement = true {pd product} + bool UseOnlyInlinedBimorphic = true {C2 product} + bool UseOprofile = false {product} + bool UseOptoBiasInlining = true {C2 product} + bool UsePPCLWSYNC = true {product} + bool UsePSAdaptiveSurvivorSizePolicy = true {product} + bool UseParNewGC = false {product} + bool UseParallelGC := true {product} + bool UseParallelOldGC = false {product} + bool UsePerfData = true {product} + bool UsePopCountInstruction = true {product} + bool UseRDPCForConstantTableBase = false {C2 product} + bool UseRicochetFrames = true {diagnostic} + bool UseSHM = false {product} + intx UseSSE = 4 {product} + bool UseSSE42Intrinsics = true {product} + bool UseSeparateVSpacesInYoungGen = true {product} + bool UseSerialGC = false {product} + bool UseSharedSpaces = false {product} + bool UseSignalChaining = true {product} + bool UseSpinning = false {product} + bool UseSplitVerifier = true {product} + bool UseStoreImmI16 = false {product} + bool UseStringCache = false {product} + bool UseSuperWord = true {C2 product} + bool UseTLAB = true {pd product} + bool UseThreadPriorities = true {pd product} + bool UseTypeProfile = true {product} + bool UseUnalignedLoadStores = true {product} + intx UseVIS = 99 {product} + bool UseVMInterruptibleIO = false {product} + bool UseVectoredExceptions = false {pd product} + bool UseXMMForArrayCopy = true {product} + bool UseXmmI2D = false {product} + bool UseXmmI2F = false {product} + bool UseXmmLoadAndClearUpper = true {product} + bool UseXmmRegToRegMoveAll = true {product} + bool VMThreadHintNoPreempt = false {product} + intx VMThreadPriority = -1 {product} + intx VMThreadStackSize = 512 {pd product} + intx ValueMapInitialSize = 11 {C1 product} + intx ValueMapMaxLoopSize = 8 {C1 product} + intx ValueSearchLimit = 1000 {C2 product} + bool VerifyAfterGC = false {diagnostic} + bool VerifyBeforeExit = false {diagnostic} + bool VerifyBeforeGC = false {diagnostic} + bool VerifyBeforeIteration = false {diagnostic} + bool VerifyDuringGC = false {diagnostic} + intx VerifyGCLevel = 0 {diagnostic} + uintx VerifyGCStartAt = 0 {diagnostic} + bool VerifyMergedCPBytecodes = true {product} + bool VerifyMethodHandles = false {diagnostic} + bool VerifyObjectStartArray = true {diagnostic} + bool VerifyRememberedSets = false {diagnostic} + intx WorkAroundNPTLTimedWaitHang = 1 {product} + uintx YoungGenerationSizeIncrement = 20 {product} + uintx YoungGenerationSizeSupplement = 80 {product} + uintx YoungGenerationSizeSupplementDecay = 8 {product} + uintx YoungPLABSize = 4096 {product} + bool ZeroTLAB = false {product} + intx hashCode = 0 {product}
\ No newline at end of file diff --git a/caliper/src/test/resources/com/google/caliper/bridge/jdk7-gc.txt b/caliper/src/test/resources/com/google/caliper/bridge/jdk7-gc.txt new file mode 100644 index 0000000..b3afcf1 --- /dev/null +++ b/caliper/src/test/resources/com/google/caliper/bridge/jdk7-gc.txt @@ -0,0 +1,200 @@ +2013-02-11T20:15:26.706-0600: 0.098: [GC 1316K->576K(62848K), 0.0014240 secs] +2013-02-11T20:15:26.708-0600: 0.099: [Full GC 576K->486K(62848K), 0.0044860 secs] +2013-02-11T20:15:26.713-0600: 0.104: [GC 486K->486K(62848K), 0.0005000 secs] +2013-02-11T20:15:26.713-0600: 0.105: [Full GC 486K->486K(62848K), 0.0039840 secs] +2013-02-11T20:15:26.717-0600: 0.109: [GC 486K->486K(62848K), 0.0007650 secs] +2013-02-11T20:15:26.718-0600: 0.110: [Full GC 486K->486K(62848K), 0.0038350 secs] +2013-02-11T20:15:26.722-0600: 0.113: [GC 486K->486K(62848K), 0.0005430 secs] +2013-02-11T20:15:26.723-0600: 0.114: [Full GC 486K->486K(62848K), 0.0045480 secs] +2013-02-11T20:15:26.727-0600: 0.119: [GC 486K->486K(62848K), 0.0003950 secs] +2013-02-11T20:15:26.728-0600: 0.119: [Full GC 486K->486K(62848K), 0.0036570 secs] +2013-02-11T20:15:26.731-0600: 0.123: [GC 486K->486K(62848K), 0.0005850 secs] +2013-02-11T20:15:26.732-0600: 0.123: [Full GC 486K->486K(62848K), 0.0036650 secs] +2013-02-11T20:15:26.736-0600: 0.127: [GC 486K->486K(62848K), 0.0004640 secs] +2013-02-11T20:15:26.736-0600: 0.128: [Full GC 486K->486K(62848K), 0.0035990 secs] +2013-02-11T20:15:26.740-0600: 0.131: [GC 486K->486K(62848K), 0.0005500 secs] +2013-02-11T20:15:26.740-0600: 0.132: [Full GC 486K->486K(62848K), 0.0038150 secs] +2013-02-11T20:15:26.744-0600: 0.136: [GC 486K->486K(62848K), 0.0004460 secs] +2013-02-11T20:15:26.745-0600: 0.136: [Full GC 486K->486K(62848K), 0.0036000 secs] +2013-02-11T20:15:26.748-0600: 0.140: [GC 486K->486K(62848K), 0.0003120 secs] +2013-02-11T20:15:26.749-0600: 0.140: [Full GC 486K->486K(62848K), 0.0035820 secs] +2013-02-11T20:15:26.752-0600: 0.144: [GC 486K->486K(62848K), 0.0003340 secs] +2013-02-11T20:15:26.753-0600: 0.144: [Full GC 486K->486K(62848K), 0.0036170 secs] +2013-02-11T20:15:26.756-0600: 0.148: [GC 486K->486K(62848K), 0.0004370 secs] +2013-02-11T20:15:26.757-0600: 0.148: [Full GC 486K->486K(62848K), 0.0036000 secs] +2013-02-11T20:15:26.761-0600: 0.152: [GC 486K->486K(62848K), 0.0003760 secs] +2013-02-11T20:15:26.761-0600: 0.152: [Full GC 486K->486K(62848K), 0.0035950 secs] +2013-02-11T20:15:26.765-0600: 0.156: [GC 486K->486K(62848K), 0.0004000 secs] +2013-02-11T20:15:26.765-0600: 0.157: [Full GC 486K->486K(62848K), 0.0035760 secs] +2013-02-11T20:15:26.769-0600: 0.160: [GC 486K->486K(62848K), 0.0003480 secs] +2013-02-11T20:15:26.769-0600: 0.161: [Full GC 486K->486K(62848K), 0.0035710 secs] +2013-02-11T20:15:26.773-0600: 0.164: [GC 486K->486K(62848K), 0.0003370 secs] +2013-02-11T20:15:26.773-0600: 0.164: [Full GC 486K->486K(62848K), 0.0035910 secs] +2013-02-11T20:15:26.777-0600: 0.168: [GC 486K->486K(62848K), 0.0003840 secs] +2013-02-11T20:15:26.777-0600: 0.169: [Full GC 486K->486K(62848K), 0.0035760 secs] +2013-02-11T20:15:26.781-0600: 0.172: [GC 486K->486K(62848K), 0.0003560 secs] +2013-02-11T20:15:26.781-0600: 0.173: [Full GC 486K->486K(62848K), 0.0035920 secs] +2013-02-11T20:15:26.785-0600: 0.176: [GC 486K->486K(62848K), 0.0003520 secs] +2013-02-11T20:15:26.785-0600: 0.177: [Full GC 486K->486K(62848K), 0.0035650 secs] +2013-02-11T20:15:26.789-0600: 0.180: [GC 486K->486K(62848K), 0.0004280 secs] +2013-02-11T20:15:26.789-0600: 0.181: [Full GC 486K->486K(62848K), 0.0035460 secs] +2013-02-11T20:15:26.793-0600: 0.184: [GC 815K->502K(62848K), 0.0003340 secs] +2013-02-11T20:15:26.793-0600: 0.185: [Full GC 502K->486K(62848K), 0.0037010 secs] +2013-02-11T20:15:26.797-0600: 0.188: [GC 486K->486K(62848K), 0.0004180 secs] +2013-02-11T20:15:26.797-0600: 0.189: [Full GC 486K->486K(62848K), 0.0035310 secs] +2013-02-11T20:15:26.801-0600: 0.193: [GC 486K->486K(62848K), 0.0003840 secs] +2013-02-11T20:15:26.801-0600: 0.193: [Full GC 486K->486K(62848K), 0.0035550 secs] +2013-02-11T20:15:26.805-0600: 0.197: [GC 486K->486K(62848K), 0.0003240 secs] +2013-02-11T20:15:26.805-0600: 0.197: [Full GC 486K->486K(62848K), 0.0035560 secs] +2013-02-11T20:15:26.809-0600: 0.201: [GC 486K->486K(62848K), 0.0002990 secs] +2013-02-11T20:15:26.809-0600: 0.201: [Full GC 486K->486K(62848K), 0.0035600 secs] +2013-02-11T20:15:26.813-0600: 0.204: [GC 486K->486K(62848K), 0.0002760 secs] +2013-02-11T20:15:26.813-0600: 0.205: [Full GC 486K->486K(62848K), 0.0035470 secs] +2013-02-11T20:15:26.817-0600: 0.208: [GC 486K->486K(62848K), 0.0002980 secs] +2013-02-11T20:15:26.817-0600: 0.209: [Full GC 486K->486K(62848K), 0.0035440 secs] +2013-02-11T20:15:26.821-0600: 0.212: [GC 486K->486K(62848K), 0.0002770 secs] +2013-02-11T20:15:26.821-0600: 0.213: [Full GC 486K->486K(62848K), 0.0035510 secs] +2013-02-11T20:15:26.825-0600: 0.216: [GC 486K->486K(62848K), 0.0002340 secs] +2013-02-11T20:15:26.825-0600: 0.216: [Full GC 486K->486K(62848K), 0.0035280 secs] +2013-02-11T20:15:26.828-0600: 0.220: [GC 486K->486K(62848K), 0.0002440 secs] +2013-02-11T20:15:26.829-0600: 0.220: [Full GC 486K->486K(62848K), 0.0035330 secs] +2013-02-11T20:15:26.832-0600: 0.224: [GC 486K->486K(62848K), 0.0003140 secs] +2013-02-11T20:15:26.833-0600: 0.224: [Full GC 486K->486K(62848K), 0.0035600 secs] +2013-02-11T20:15:26.836-0600: 0.228: [GC 486K->486K(62848K), 0.0002780 secs] +2013-02-11T20:15:26.837-0600: 0.228: [Full GC 486K->486K(62848K), 0.0035240 secs] +2013-02-11T20:15:26.840-0600: 0.232: [GC 486K->486K(62848K), 0.0003130 secs] +2013-02-11T20:15:26.840-0600: 0.232: [Full GC 486K->486K(62848K), 0.0035350 secs] +2013-02-11T20:15:26.844-0600: 0.236: [GC 815K->502K(62848K), 0.0002610 secs] +2013-02-11T20:15:26.844-0600: 0.236: [Full GC 502K->487K(62848K), 0.0035270 secs] +2013-02-11T20:15:26.848-0600: 0.239: [GC 487K->487K(62848K), 0.0002930 secs] +2013-02-11T20:15:26.848-0600: 0.240: [Full GC 487K->487K(62848K), 0.0035470 secs] +2013-02-11T20:15:26.852-0600: 0.243: [GC 487K->487K(62848K), 0.0002730 secs] +2013-02-11T20:15:26.852-0600: 0.244: [Full GC 487K->487K(62848K), 0.0035470 secs] +2013-02-11T20:15:26.856-0600: 0.247: [GC 487K->487K(62848K), 0.0003420 secs] +2013-02-11T20:15:26.856-0600: 0.248: [Full GC 487K->487K(62848K), 0.0035390 secs] +2013-02-11T20:15:26.860-0600: 0.251: [GC 487K->487K(62848K), 0.0002320 secs] +2013-02-11T20:15:26.860-0600: 0.252: [Full GC 487K->487K(62848K), 0.0035050 secs] +2013-02-11T20:15:26.864-0600: 0.255: [GC 487K->487K(62848K), 0.0002880 secs] +2013-02-11T20:15:26.864-0600: 0.255: [Full GC 487K->487K(62848K), 0.0035190 secs] +2013-02-11T20:15:26.867-0600: 0.259: [GC 487K->487K(62848K), 0.0002650 secs] +2013-02-11T20:15:26.868-0600: 0.259: [Full GC 487K->487K(62848K), 0.0034950 secs] +2013-02-11T20:15:26.871-0600: 0.263: [GC 487K->487K(62848K), 0.0003850 secs] +2013-02-11T20:15:26.872-0600: 0.263: [Full GC 487K->487K(62848K), 0.0035450 secs] +2013-02-11T20:15:26.875-0600: 0.267: [GC 487K->487K(62848K), 0.0003630 secs] +2013-02-11T20:15:26.876-0600: 0.267: [Full GC 487K->487K(62848K), 0.0035380 secs] +2013-02-11T20:15:26.879-0600: 0.271: [GC 487K->487K(62848K), 0.0002800 secs] +2013-02-11T20:15:26.879-0600: 0.271: [Full GC 487K->487K(62848K), 0.0035340 secs] +2013-02-11T20:15:26.883-0600: 0.275: [GC 487K->487K(62848K), 0.0002580 secs] +2013-02-11T20:15:26.883-0600: 0.275: [Full GC 487K->487K(62848K), 0.0035550 secs] +2013-02-11T20:15:26.887-0600: 0.278: [GC 487K->487K(62848K), 0.0003880 secs] +2013-02-11T20:15:26.887-0600: 0.279: [Full GC 487K->487K(62848K), 0.0035690 secs] +2013-02-11T20:15:26.891-0600: 0.283: [GC 487K->487K(62848K), 0.0003040 secs] +2013-02-11T20:15:26.891-0600: 0.283: [Full GC 487K->487K(62848K), 0.0035720 secs] +2013-02-11T20:15:26.895-0600: 0.286: [GC 487K->487K(62848K), 0.0002750 secs] +2013-02-11T20:15:26.895-0600: 0.287: [Full GC 487K->487K(62848K), 0.0035700 secs] +2013-02-11T20:15:26.899-0600: 0.290: [GC 816K->503K(62848K), 0.0002830 secs] +2013-02-11T20:15:26.899-0600: 0.291: [Full GC 503K->487K(62848K), 0.0037630 secs] +2013-02-11T20:15:26.903-0600: 0.295: [GC 487K->487K(62848K), 0.0002890 secs] +2013-02-11T20:15:26.903-0600: 0.295: [Full GC 487K->487K(62848K), 0.0037330 secs] +2013-02-11T20:15:26.907-0600: 0.299: [GC 487K->487K(62848K), 0.0002580 secs] +2013-02-11T20:15:26.907-0600: 0.299: [Full GC 487K->487K(62848K), 0.0035480 secs] +2013-02-11T20:15:26.911-0600: 0.303: [GC 816K->503K(62848K), 0.0003540 secs] +2013-02-11T20:15:26.911-0600: 0.303: [Full GC 503K->487K(62848K), 0.0035610 secs] +2013-02-11T20:15:26.915-0600: 0.307: [GC 487K->487K(62848K), 0.0004160 secs] +2013-02-11T20:15:26.915-0600: 0.307: [Full GC 487K->487K(62848K), 0.0035390 secs] +2013-02-11T20:15:26.919-0600: 0.311: [GC 816K->519K(62848K), 0.0003650 secs] +2013-02-11T20:15:26.920-0600: 0.311: [Full GC 519K->487K(62848K), 0.0035430 secs] +2013-02-11T20:15:26.923-0600: 0.315: [GC 487K->487K(62848K), 0.0003340 secs] +2013-02-11T20:15:26.924-0600: 0.315: [Full GC 487K->487K(62848K), 0.0035600 secs] +2013-02-11T20:15:26.927-0600: 0.319: [GC 816K->503K(62848K), 0.0003750 secs] +2013-02-11T20:15:26.928-0600: 0.319: [Full GC 503K->487K(62848K), 0.0035330 secs] +2013-02-11T20:15:26.931-0600: 0.323: [GC 487K->487K(62848K), 0.0003790 secs] +2013-02-11T20:15:26.932-0600: 0.323: [Full GC 487K->487K(62848K), 0.0035490 secs] +2013-02-11T20:15:26.935-0600: 0.327: [GC 816K->503K(62848K), 0.0002930 secs] +2013-02-11T20:15:26.936-0600: 0.327: [Full GC 503K->487K(62848K), 0.0035400 secs] +2013-02-11T20:15:26.939-0600: 0.331: [GC 487K->487K(62848K), 0.0002870 secs] +2013-02-11T20:15:26.939-0600: 0.331: [Full GC 487K->487K(62848K), 0.0035630 secs] +2013-02-11T20:15:26.943-0600: 0.335: [GC 487K->487K(62848K), 0.0003790 secs] +2013-02-11T20:15:26.943-0600: 0.335: [Full GC 487K->487K(62848K), 0.0035950 secs] +2013-02-11T20:15:26.947-0600: 0.339: [GC 816K->503K(62848K), 0.0003640 secs] +2013-02-11T20:15:26.948-0600: 0.339: [Full GC 503K->487K(62848K), 0.0038120 secs] +2013-02-11T20:15:26.951-0600: 0.343: [GC 487K->487K(62848K), 0.0003520 secs] +2013-02-11T20:15:26.952-0600: 0.343: [Full GC 487K->487K(62848K), 0.0035580 secs] +2013-02-11T20:15:26.955-0600: 0.347: [GC 487K->487K(62848K), 0.0003620 secs] +2013-02-11T20:15:26.956-0600: 0.347: [Full GC 487K->487K(62848K), 0.0035290 secs] +2013-02-11T20:15:26.959-0600: 0.351: [GC 487K->487K(62848K), 0.0003630 secs] +2013-02-11T20:15:26.960-0600: 0.351: [Full GC 487K->487K(62848K), 0.0035080 secs] +2013-02-11T20:15:26.963-0600: 0.355: [GC 487K->487K(62848K), 0.0003310 secs] +2013-02-11T20:15:26.964-0600: 0.355: [Full GC 487K->482K(62848K), 0.0038060 secs] +2013-02-11T20:15:26.968-0600: 0.359: [GC 482K->482K(62848K), 0.0003650 secs] +2013-02-11T20:15:26.968-0600: 0.359: [Full GC 482K->482K(62848K), 0.0035340 secs] +2013-02-11T20:15:26.972-0600: 0.363: [GC 482K->482K(62848K), 0.0003300 secs] +2013-02-11T20:15:26.972-0600: 0.363: [Full GC 482K->482K(62848K), 0.0035340 secs] +2013-02-11T20:15:26.976-0600: 0.367: [GC 482K->482K(62848K), 0.0003220 secs] +2013-02-11T20:15:26.976-0600: 0.367: [Full GC 482K->482K(62848K), 0.0035230 secs] +2013-02-11T20:15:26.979-0600: 0.371: [GC 482K->482K(62848K), 0.0003220 secs] +2013-02-11T20:15:26.980-0600: 0.371: [Full GC 482K->482K(62848K), 0.0035200 secs] +2013-02-11T20:15:26.983-0600: 0.375: [GC 482K->482K(62848K), 0.0003030 secs] +2013-02-11T20:15:26.984-0600: 0.375: [Full GC 482K->482K(62848K), 0.0035370 secs] +2013-02-11T20:15:26.987-0600: 0.379: [GC 482K->482K(62848K), 0.0003230 secs] +2013-02-11T20:15:26.988-0600: 0.379: [Full GC 482K->482K(62848K), 0.0035150 secs] +2013-02-11T20:15:26.991-0600: 0.383: [GC 482K->482K(62848K), 0.0003050 secs] +2013-02-11T20:15:26.992-0600: 0.383: [Full GC 482K->482K(62848K), 0.0035080 secs] +2013-02-11T20:15:26.995-0600: 0.387: [GC 482K->482K(62848K), 0.0003320 secs] +2013-02-11T20:15:26.995-0600: 0.387: [Full GC 482K->482K(62848K), 0.0035060 secs] +2013-02-11T20:15:26.999-0600: 0.391: [GC 482K->482K(62848K), 0.0002520 secs] +2013-02-11T20:15:26.999-0600: 0.391: [Full GC 482K->482K(62848K), 0.0035190 secs] +2013-02-11T20:15:27.003-0600: 0.394: [GC 482K->482K(62848K), 0.0003050 secs] +2013-02-11T20:15:27.003-0600: 0.395: [Full GC 482K->482K(62848K), 0.0035430 secs] +2013-02-11T20:15:27.007-0600: 0.398: [GC 482K->482K(62848K), 0.0002800 secs] +2013-02-11T20:15:27.007-0600: 0.399: [Full GC 482K->482K(62848K), 0.0035210 secs] +2013-02-11T20:15:27.011-0600: 0.402: [GC 482K->482K(62848K), 0.0003130 secs] +2013-02-11T20:15:27.011-0600: 0.403: [Full GC 482K->482K(62848K), 0.0035530 secs] +2013-02-11T20:15:27.015-0600: 0.406: [GC 482K->482K(62848K), 0.0004020 secs] +2013-02-11T20:15:27.015-0600: 0.407: [Full GC 482K->482K(62848K), 0.0035340 secs] +2013-02-11T20:15:27.019-0600: 0.410: [GC 482K->482K(62848K), 0.0003310 secs] +2013-02-11T20:15:27.019-0600: 0.411: [Full GC 482K->482K(62848K), 0.0035390 secs] +2013-02-11T20:15:27.023-0600: 0.414: [GC 482K->482K(62848K), 0.0003630 secs] +2013-02-11T20:15:27.023-0600: 0.414: [Full GC 482K->482K(62848K), 0.0035500 secs] +2013-02-11T20:15:27.027-0600: 0.418: [GC 482K->482K(62848K), 0.0003430 secs] +2013-02-11T20:15:27.027-0600: 0.418: [Full GC 482K->482K(62848K), 0.0035220 secs] +2013-02-11T20:15:27.031-0600: 0.422: [GC 482K->482K(62848K), 0.0003260 secs] +2013-02-11T20:15:27.031-0600: 0.422: [Full GC 482K->482K(62848K), 0.0035230 secs] +2013-02-11T20:15:27.034-0600: 0.426: [GC 482K->482K(62848K), 0.0003650 secs] +2013-02-11T20:15:27.035-0600: 0.426: [Full GC 482K->482K(62848K), 0.0035010 secs] +2013-02-11T20:15:27.038-0600: 0.430: [GC 482K->482K(62848K), 0.0003000 secs] +2013-02-11T20:15:27.039-0600: 0.430: [Full GC 482K->482K(62848K), 0.0035090 secs] +2013-02-11T20:15:27.042-0600: 0.434: [GC 482K->482K(62848K), 0.0004570 secs] +2013-02-11T20:15:27.043-0600: 0.434: [Full GC 482K->482K(62848K), 0.0036010 secs] +2013-02-11T20:15:27.046-0600: 0.438: [GC 482K->482K(62848K), 0.0004570 secs] +2013-02-11T20:15:27.047-0600: 0.438: [Full GC 482K->482K(62848K), 0.0036090 secs] +2013-02-11T20:15:27.051-0600: 0.442: [GC 482K->482K(62848K), 0.0003740 secs] +2013-02-11T20:15:27.051-0600: 0.442: [Full GC 482K->482K(62848K), 0.0036140 secs] +2013-02-11T20:15:27.055-0600: 0.446: [GC 482K->482K(62848K), 0.0003630 secs] +2013-02-11T20:15:27.055-0600: 0.446: [Full GC 482K->482K(62848K), 0.0036080 secs] +2013-02-11T20:15:27.059-0600: 0.450: [GC 482K->482K(62848K), 0.0003770 secs] +2013-02-11T20:15:27.059-0600: 0.451: [Full GC 482K->482K(62848K), 0.0036060 secs] +2013-02-11T20:15:27.063-0600: 0.454: [GC 482K->482K(62848K), 0.0003410 secs] +2013-02-11T20:15:27.063-0600: 0.455: [Full GC 482K->482K(62848K), 0.0036300 secs] +2013-02-11T20:15:27.067-0600: 0.458: [GC 482K->482K(62848K), 0.0002990 secs] +2013-02-11T20:15:27.067-0600: 0.459: [Full GC 482K->482K(62848K), 0.0036160 secs] +2013-02-11T20:15:27.071-0600: 0.462: [GC 482K->482K(62848K), 0.0003970 secs] +2013-02-11T20:15:27.071-0600: 0.463: [Full GC 482K->482K(62848K), 0.0036660 secs] +2013-02-11T20:15:27.075-0600: 0.466: [GC 482K->482K(62848K), 0.0003160 secs] +2013-02-11T20:15:27.075-0600: 0.467: [Full GC 482K->482K(62848K), 0.0036080 secs] +2013-02-11T20:15:27.079-0600: 0.470: [GC 482K->482K(62848K), 0.0003140 secs] +2013-02-11T20:15:27.079-0600: 0.471: [Full GC 482K->482K(62848K), 0.0035740 secs] +2013-02-11T20:15:27.083-0600: 0.474: [GC 482K->482K(62848K), 0.0003450 secs] +2013-02-11T20:15:27.083-0600: 0.475: [Full GC 482K->482K(62848K), 0.0036240 secs] +2013-02-11T20:15:27.087-0600: 0.478: [GC 482K->482K(62848K), 0.0004670 secs] +2013-02-11T20:15:27.087-0600: 0.479: [Full GC 482K->482K(62848K), 0.0037790 secs] +2013-02-11T20:15:27.091-0600: 0.483: [GC 482K->482K(62848K), 0.0002690 secs] +2013-02-11T20:15:27.091-0600: 0.483: [Full GC 482K->482K(62848K), 0.0036290 secs] +2013-02-11T20:15:27.095-0600: 0.487: [GC 482K->482K(62848K), 0.0003340 secs] +2013-02-11T20:15:27.096-0600: 0.487: [Full GC 482K->482K(62848K), 0.0035950 secs] +2013-02-11T20:15:27.099-0600: 0.491: [GC 482K->482K(62848K), 0.0002480 secs] +2013-02-11T20:15:27.099-0600: 0.491: [Full GC 482K->482K(62848K), 0.0035940 secs] +2013-02-11T20:15:27.103-0600: 0.495: [GC 482K->482K(62848K), 0.0003510 secs] +2013-02-11T20:15:27.104-0600: 0.495: [Full GC 482K->482K(62848K), 0.0037060 secs] +2013-02-11T20:15:27.107-0600: 0.499: [GC 482K->482K(62848K), 0.0003680 secs] +2013-02-11T20:15:27.108-0600: 0.499: [Full GC 482K->482K(62848K), 0.0035920 secs]
\ No newline at end of file diff --git a/examples/pom.xml b/examples/pom.xml index 7352d7b..0e904cb 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -1,19 +1,57 @@ -<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/maven-v4_0_0.xsd"> +<?xml version="1.0" encoding="UTF-8"?> +<!-- + ~ Copyright 2011 Google Inc. + ~ + ~ 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. + --> + +<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/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> - <groupId>com.google.caliper</groupId> - <artifactId>caliper-examples</artifactId> - <packaging>jar</packaging> - <version>0.5-rc1</version> - <inceptionYear>2009</inceptionYear> - <name>Caliper Examples</name> <parent> <groupId>org.sonatype.oss</groupId> <artifactId>oss-parent</artifactId> - <version>5</version> + <version>7</version> </parent> - <url>http://code.google.com/p/caliper/</url> + + <groupId>com.google.caliper</groupId> + <artifactId>caliper-examples</artifactId> + <version>1.0-SNAPSHOT</version> + <packaging>jar</packaging> + + <name>Caliper Examples</name> <description>Caliper Examples</description> + + <url>http://code.google.com/p/caliper/</url> + + <inceptionYear>2009</inceptionYear> + + <organization> + <name>Google Inc.</name> + <url>http://www.google.com</url> + </organization> + + <developers> + <developer> + <name>Gregory Kick</name> + <organization>Google Inc.</organization> + </developer> + <developer> + <name>Jesse Wilson</name> + </developer> + </developers> + <licenses> <license> <name>The Apache Software License, Version 2.0</name> @@ -21,24 +59,23 @@ <distribution>repo</distribution> </license> </licenses> + <scm> <connection>scm:git:http://code.google.com/p/caliper/examples</connection> <developerConnection>scm:git:git:http://code.google.com/p/caliper/examples</developerConnection> <url>http://caliper.codegoogle.com/svn/trunk/examples</url> </scm> + <issueManagement> <system>Google Code Issue Tracking</system> <url>http://code.google.com/p/caliper/issues/list</url> </issueManagement> - <organization> - <name>Google, Inc.</name> - <url>http://www.google.com</url> - </organization> + <dependencies> <dependency> <groupId>com.google.caliper</groupId> <artifactId>caliper</artifactId> - <version>0.5-rc1</version> + <version>1.0-SNAPSHOT</version> </dependency> <dependency> <groupId>junit</groupId> @@ -47,13 +84,14 @@ <scope>test</scope> </dependency> </dependencies> + <build> <defaultGoal>package</defaultGoal> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> - <version>2.3.2</version> + <version>3.2</version> <configuration> <source>1.6</source> <target>1.6</target> @@ -62,7 +100,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-eclipse-plugin</artifactId> - <version>2.8</version> + <version>2.9</version> <configuration> <downloadSources>true</downloadSources> <downloadJavadocs>true</downloadJavadocs> @@ -72,17 +110,12 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-release-plugin</artifactId> - <version>2.1</version> + <version>2.5.1</version> <configuration> <arguments>-DenableCiProfile=true</arguments> </configuration> </plugin> </plugins> </build> - <developers> - <developer> - <name>Jesse Wilson</name> - <organization>Google Inc.</organization> - </developer> - </developers> + </project> diff --git a/examples/src/main/java/examples/ArraySortBenchmark.java b/examples/src/main/java/examples/ArraySortBenchmark.java index f42390f..81be2a6 100644 --- a/examples/src/main/java/examples/ArraySortBenchmark.java +++ b/examples/src/main/java/examples/ArraySortBenchmark.java @@ -16,16 +16,17 @@ package examples; +import com.google.caliper.BeforeExperiment; +import com.google.caliper.Benchmark; import com.google.caliper.Param; -import com.google.caliper.Runner; -import com.google.caliper.SimpleBenchmark; + import java.util.Arrays; import java.util.Random; /** * Measures sorting on different distributions of integers. */ -public class ArraySortBenchmark extends SimpleBenchmark { +public class ArraySortBenchmark { @Param({"10", "100", "1000", "10000"}) private int length; @@ -34,12 +35,12 @@ public class ArraySortBenchmark extends SimpleBenchmark { private int[] values; private int[] copy; - @Override protected void setUp() throws Exception { + @BeforeExperiment void setUp() throws Exception { values = distribution.create(length); copy = new int[length]; } - public void timeSort(int reps) { + @Benchmark void sort(int reps) { for (int i = 0; i < reps; i++) { System.arraycopy(values, 0, copy, 0, values.length); Arrays.sort(copy); @@ -95,8 +96,4 @@ public class ArraySortBenchmark extends SimpleBenchmark { abstract int[] create(int length); } - - public static void main(String[] args) throws Exception { - Runner.main(ArraySortBenchmark.class, args); - } } diff --git a/examples/src/main/java/examples/BitSetBenchmark.java b/examples/src/main/java/examples/BitSetBenchmark.java index 3154b01..98cdcc8 100644 --- a/examples/src/main/java/examples/BitSetBenchmark.java +++ b/examples/src/main/java/examples/BitSetBenchmark.java @@ -16,8 +16,10 @@ package examples; -import com.google.caliper.Runner; -import com.google.caliper.SimpleBenchmark; + +import com.google.caliper.BeforeExperiment; +import com.google.caliper.Benchmark; + import java.util.BitSet; import java.util.Random; @@ -44,17 +46,17 @@ import java.util.Random; * BaselineIteration 68 XX||||||||||||||||| * </pre> * - * <p>Initially things look simple. The {@link #timeSetBitSetX64(int)} benchmark - * takes approximately twice as long as {@link #timeSetMaskX64(int)}. However + * <p>Initially things look simple. The {@link #setBitSetX64(int)} benchmark + * takes approximately twice as long as {@link #setMaskX64(int)}. However * the inner loops in these benchmarks have almost no content, so a more * 'real world' benchmark was devised in an attempt to back up these results. * - * <p>The {@link #timeCharsToMask(int)} and {@link #timeCharsToBitSet(int)} + * <p>The {@link #charsToMask(int)} and {@link #charsToBitSet(int)} * benchmarks convert a simple char[] of '1's and '0's to a corresponding BitSet * or bit mask. These also processes 64 bits per iteration and so appears to be * doing the same amount of work as the first benchmarks. * - * <p>Additionally the {@link BitSetBenchmark#timeBaselineIteration(int)} + * <p>Additionally the {@link BitSetBenchmark#baselineIteration(int)} * benchmark attempts to measure the raw cost of looping through and reading the * source data. * @@ -83,11 +85,11 @@ import java.util.Random; * <p><b>2:</b>Overly simplistic benchmarks can give a very false impression of * performance. */ -public class BitSetBenchmark extends SimpleBenchmark { +public class BitSetBenchmark { private BitSet bitSet; private char[] bitString; - @Override protected void setUp() throws Exception { + @BeforeExperiment void setUp() throws Exception { bitSet = new BitSet(64); bitString = new char[64]; Random r = new Random(); @@ -99,7 +101,7 @@ public class BitSetBenchmark extends SimpleBenchmark { /** * This benchmark attempts to measure performance of {@link BitSet#set}. */ - public int timeSetBitSetX64(int reps) { + @Benchmark int setBitSetX64(int reps) { long count = 64L * reps; for (int i = 0; i < count; i++) { bitSet.set(i & 0x3F, true); @@ -110,7 +112,7 @@ public class BitSetBenchmark extends SimpleBenchmark { /** * This benchmark attempts to measure performance of direct bit-manipulation. */ - public long timeSetMaskX64(int reps) { + @Benchmark long setMaskX64(int reps) { long count = 64L * reps; long bitMask = 0L; for (int i = 0; i < count; i++) { @@ -122,9 +124,9 @@ public class BitSetBenchmark extends SimpleBenchmark { /** * This benchmark parses a char[] of 1's and 0's into a BitSet. Results from * this benchmark should be comparable with those from - * {@link #timeCharsToMask(int)}. + * {@link #charsToMask(int)}. */ - public String timeCharsToBitSet(int reps) { + @Benchmark String charsToBitSet(int reps) { /* * This benchmark now measures the complete parsing of a char[] rather than * a single invocation of {@link BitSet#set}. However this fine because @@ -141,9 +143,9 @@ public class BitSetBenchmark extends SimpleBenchmark { /** * This benchmark parses a char[] of 1's and 0's into a bit mask. Results from * this benchmark should be comparable with those from - * {@link #timeCharsToBitSet(int)}. + * {@link #charsToBitSet(int)}. */ - public long timeCharsToMask(int reps) { + @Benchmark long charsToMask(int reps) { /* * Comparing results we see a far more realistic sounding result whereby * using a bit mask is a little over 4x faster than using BitSet. @@ -164,12 +166,12 @@ public class BitSetBenchmark extends SimpleBenchmark { /** * This benchmark attempts to measure the baseline cost of both - * {@link #timeCharsToBitSet(int)} and {@link #timeCharsToMask(int)}. + * {@link #charsToBitSet(int)} and {@link #charsToMask(int)}. * It does this by unconditionally summing the character values of the char[]. * This is as close to a no-op case as we can expect to get without unwanted * over-optimization. */ - public long timeBaselineIteration(int reps) { + @Benchmark long baselineIteration(int reps) { int badHash = 0; for (int i = 0; i < reps; i++) { for (int n = 0; n < bitString.length; n++) { @@ -178,9 +180,4 @@ public class BitSetBenchmark extends SimpleBenchmark { } return badHash; } - - // TODO: remove this from all examples when IDE plugins are ready - public static void main(String[] args) throws Exception { - Runner.main(BitSetBenchmark.class, args); - } } diff --git a/examples/src/main/java/examples/CharacterBenchmark.java b/examples/src/main/java/examples/CharacterBenchmark.java index 1e013af..82c4439 100644 --- a/examples/src/main/java/examples/CharacterBenchmark.java +++ b/examples/src/main/java/examples/CharacterBenchmark.java @@ -16,15 +16,15 @@ package examples; +import com.google.caliper.BeforeExperiment; +import com.google.caliper.Benchmark; import com.google.caliper.Param; -import com.google.caliper.Runner; -import com.google.caliper.SimpleBenchmark; /** * Tests various Character methods, intended for testing multiple * implementations against each other. */ -public class CharacterBenchmark extends SimpleBenchmark { +public class CharacterBenchmark { @Param private CharacterSet characterSet; @@ -32,7 +32,7 @@ public class CharacterBenchmark extends SimpleBenchmark { private char[] chars; - @Override protected void setUp() throws Exception { + @BeforeExperiment void setUp() throws Exception { this.chars = characterSet.chars; } @@ -51,7 +51,7 @@ public class CharacterBenchmark extends SimpleBenchmark { } // A fake benchmark to give us a baseline. - public boolean timeIsSpace(int reps) { + @Benchmark boolean isSpace(int reps) { boolean dummy = false; if (overload == Overload.CHAR) { for (int i = 0; i < reps; ++i) { @@ -69,7 +69,7 @@ public class CharacterBenchmark extends SimpleBenchmark { return dummy; } - public void timeDigit(int reps) { + @Benchmark void digit(int reps) { if (overload == Overload.CHAR) { for (int i = 0; i < reps; ++i) { for (int ch = 0; ch < 65536; ++ch) { @@ -85,7 +85,7 @@ public class CharacterBenchmark extends SimpleBenchmark { } } - public void timeGetNumericValue(int reps) { + @Benchmark void getNumericValue(int reps) { if (overload == Overload.CHAR) { for (int i = 0; i < reps; ++i) { for (int ch = 0; ch < 65536; ++ch) { @@ -101,7 +101,7 @@ public class CharacterBenchmark extends SimpleBenchmark { } } - public void timeIsDigit(int reps) { + @Benchmark void isDigit(int reps) { if (overload == Overload.CHAR) { for (int i = 0; i < reps; ++i) { for (int ch = 0; ch < 65536; ++ch) { @@ -117,7 +117,7 @@ public class CharacterBenchmark extends SimpleBenchmark { } } - public void timeIsIdentifierIgnorable(int reps) { + @Benchmark void isIdentifierIgnorable(int reps) { if (overload == Overload.CHAR) { for (int i = 0; i < reps; ++i) { for (int ch = 0; ch < 65536; ++ch) { @@ -133,7 +133,7 @@ public class CharacterBenchmark extends SimpleBenchmark { } } - public void timeIsJavaIdentifierPart(int reps) { + @Benchmark void isJavaIdentifierPart(int reps) { if (overload == Overload.CHAR) { for (int i = 0; i < reps; ++i) { for (int ch = 0; ch < 65536; ++ch) { @@ -149,7 +149,7 @@ public class CharacterBenchmark extends SimpleBenchmark { } } - public void timeIsJavaIdentifierStart(int reps) { + @Benchmark void isJavaIdentifierStart(int reps) { if (overload == Overload.CHAR) { for (int i = 0; i < reps; ++i) { for (int ch = 0; ch < 65536; ++ch) { @@ -165,7 +165,7 @@ public class CharacterBenchmark extends SimpleBenchmark { } } - public void timeIsLetter(int reps) { + @Benchmark void isLetter(int reps) { if (overload == Overload.CHAR) { for (int i = 0; i < reps; ++i) { for (int ch = 0; ch < 65536; ++ch) { @@ -181,7 +181,7 @@ public class CharacterBenchmark extends SimpleBenchmark { } } - public void timeIsLetterOrDigit(int reps) { + @Benchmark void isLetterOrDigit(int reps) { if (overload == Overload.CHAR) { for (int i = 0; i < reps; ++i) { for (int ch = 0; ch < 65536; ++ch) { @@ -197,7 +197,7 @@ public class CharacterBenchmark extends SimpleBenchmark { } } - public void timeIsLowerCase(int reps) { + @Benchmark void isLowerCase(int reps) { if (overload == Overload.CHAR) { for (int i = 0; i < reps; ++i) { for (int ch = 0; ch < 65536; ++ch) { @@ -213,7 +213,7 @@ public class CharacterBenchmark extends SimpleBenchmark { } } - public void timeIsSpaceChar(int reps) { + @Benchmark void isSpaceChar(int reps) { if (overload == Overload.CHAR) { for (int i = 0; i < reps; ++i) { for (int ch = 0; ch < 65536; ++ch) { @@ -229,7 +229,7 @@ public class CharacterBenchmark extends SimpleBenchmark { } } - public void timeIsUpperCase(int reps) { + @Benchmark void isUpperCase(int reps) { if (overload == Overload.CHAR) { for (int i = 0; i < reps; ++i) { for (int ch = 0; ch < 65536; ++ch) { @@ -245,7 +245,7 @@ public class CharacterBenchmark extends SimpleBenchmark { } } - public void timeIsWhitespace(int reps) { + @Benchmark void isWhitespace(int reps) { if (overload == Overload.CHAR) { for (int i = 0; i < reps; ++i) { for (int ch = 0; ch < 65536; ++ch) { @@ -261,7 +261,7 @@ public class CharacterBenchmark extends SimpleBenchmark { } } - public void timeToLowerCase(int reps) { + @Benchmark void toLowerCase(int reps) { if (overload == Overload.CHAR) { for (int i = 0; i < reps; ++i) { for (int ch = 0; ch < 65536; ++ch) { @@ -277,7 +277,7 @@ public class CharacterBenchmark extends SimpleBenchmark { } } - public void timeToUpperCase(int reps) { + @Benchmark void toUpperCase(int reps) { if (overload == Overload.CHAR) { for (int i = 0; i < reps; ++i) { for (int ch = 0; ch < 65536; ++ch) { @@ -292,9 +292,4 @@ public class CharacterBenchmark extends SimpleBenchmark { } } } - - // TODO: remove this from all examples when IDE plugins are ready - public static void main(String[] args) throws Exception { - Runner.main(CharacterBenchmark.class, args); - } } diff --git a/examples/src/main/java/examples/CompressionSizeBenchmark.java b/examples/src/main/java/examples/CompressionSizeBenchmark.java index 90ddd39..d4fed5e 100644 --- a/examples/src/main/java/examples/CompressionSizeBenchmark.java +++ b/examples/src/main/java/examples/CompressionSizeBenchmark.java @@ -16,10 +16,9 @@ package examples; +import com.google.caliper.Benchmark; import com.google.caliper.Param; -import com.google.caliper.api.Benchmark; import com.google.caliper.model.ArbitraryMeasurement; -import com.google.caliper.runner.CaliperMain; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -30,7 +29,7 @@ import java.util.zip.Deflater; /** * Example "arbitrary measurement" benchmark. */ -public class CompressionSizeBenchmark extends Benchmark { +public class CompressionSizeBenchmark { @Param({ "this string will compress badly", @@ -50,7 +49,7 @@ public class CompressionSizeBenchmark extends Benchmark { compressionLevelMap.put("huffmanOnly", Deflater.HUFFMAN_ONLY); } - public long timeSimpleCompression(int reps) { + @Benchmark long simpleCompression(int reps) { long dummy = 0; for (int i = 0; i < reps; i++) { dummy += compress(toCompress.getBytes()).length; @@ -83,8 +82,4 @@ public class CompressionSizeBenchmark extends Benchmark { } return bos.toByteArray(); } - - public static void main(String[] args) { - CaliperMain.main(CompressionSizeBenchmark.class, args); - } } diff --git a/examples/src/main/java/examples/ContainsBenchmark.java b/examples/src/main/java/examples/ContainsBenchmark.java index 01bb8c6..26d2eb9 100644 --- a/examples/src/main/java/examples/ContainsBenchmark.java +++ b/examples/src/main/java/examples/ContainsBenchmark.java @@ -16,20 +16,19 @@ package examples; +import com.google.caliper.BeforeExperiment; +import com.google.caliper.Benchmark; import com.google.caliper.Param; -import com.google.caliper.Runner; -import com.google.caliper.SimpleBenchmark; + import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; -import java.util.HashMap; import java.util.HashSet; import java.util.List; -import java.util.Map; import java.util.Random; import java.util.Set; -public class ContainsBenchmark extends SimpleBenchmark { +public class ContainsBenchmark { @Param({"0", "25", "50", "75", "100"}) private int percentNulls; @Param({"100", "1000", "10000"}) private int containsPerRep; @@ -39,7 +38,7 @@ public class ContainsBenchmark extends SimpleBenchmark { /** twenty-five percent nulls */ private final List<Object> queries = new ArrayList<Object>(); - @Override protected void setUp() { + @BeforeExperiment void setUp() { set.addAll(Arrays.asList("str1", "str2", "str3", "str4")); int nullThreshold = percentNulls * containsPerRep / 100; for (int i = 0; i < nullThreshold; i++) { @@ -51,28 +50,11 @@ public class ContainsBenchmark extends SimpleBenchmark { Collections.shuffle(queries, new Random(0)); } - @Override public Map<String, Integer> getTimeUnitNames() { - Map<String, Integer> unitNames = new HashMap<String, Integer>(); - unitNames.put("ns/contains", 1); - unitNames.put("us/contains", 1000); - unitNames.put("ms/contains", 1000000); - unitNames.put("s/contains", 1000000000); - return unitNames; - } - - @Override public double nanosToUnits(double nanos) { - return nanos / containsPerRep; - } - - public void timeContains(int reps) { + @Benchmark void contains(int reps) { for (int i = 0; i < reps; i++) { for (Object query : queries) { set.contains(query); } } } - - public static void main(String[] args) { - Runner.main(ContainsBenchmark.class, args); - } } diff --git a/examples/src/main/java/examples/CopyArrayBenchmark.java b/examples/src/main/java/examples/CopyArrayBenchmark.java index fed0950..543db5b 100644 --- a/examples/src/main/java/examples/CopyArrayBenchmark.java +++ b/examples/src/main/java/examples/CopyArrayBenchmark.java @@ -16,9 +16,9 @@ package examples; +import com.google.caliper.BeforeExperiment; +import com.google.caliper.Benchmark; import com.google.caliper.Param; -import com.google.caliper.Runner; -import com.google.caliper.SimpleBenchmark; import java.util.Arrays; import java.util.Random; @@ -44,7 +44,7 @@ import java.util.Random; * memory (boolean arrays count as byte arrays!). * </ul> */ -public class CopyArrayBenchmark extends SimpleBenchmark { +public class CopyArrayBenchmark { public enum Strategy { CLONE { @Override Object[] copy(Object[] array) { @@ -252,7 +252,7 @@ public class CopyArrayBenchmark extends SimpleBenchmark { long[] longArray; short[] shortArray; - @Override protected void setUp() { + @BeforeExperiment void setUp() { objectArray = new Object[size]; booleanArray = new boolean[size]; byteArray = new byte[size]; @@ -271,86 +271,82 @@ public class CopyArrayBenchmark extends SimpleBenchmark { byteArray[i] = (byte) num; charArray[i] = (char) num; doubleArray[i] = num; - floatArray[i] = (float) num; + floatArray[i] = num; intArray[i] = num; longArray[i] = num; shortArray[i] = (short) num; } } - public int timeObjects(int reps) { + @Benchmark int objects(int reps) { int dummy = 0; for (int i = 0; i < reps; i++) { - dummy += strategy.copy(objectArray).hashCode(); + dummy += System.identityHashCode(strategy.copy(objectArray)); } return dummy; } - public int timeBooleans(int reps) { + @Benchmark int booleans(int reps) { int dummy = 0; for (int i = 0; i < reps; i++) { - dummy += strategy.copy(booleanArray).hashCode(); + dummy += System.identityHashCode(strategy.copy(booleanArray)); } return dummy; } - public int timeBytes(int reps) { + @Benchmark int bytes(int reps) { int dummy = 0; for (int i = 0; i < reps; i++) { - dummy += strategy.copy(byteArray).hashCode(); + dummy += System.identityHashCode(strategy.copy(byteArray)); } return dummy; } - public int timeChars(int reps) { + @Benchmark int chars(int reps) { int dummy = 0; for (int i = 0; i < reps; i++) { - dummy += strategy.copy(charArray).hashCode(); + dummy += System.identityHashCode(strategy.copy(charArray)); } return dummy; } - public int timeDoubles(int reps) { + @Benchmark int doubles(int reps) { int dummy = 0; for (int i = 0; i < reps; i++) { - dummy += strategy.copy(doubleArray).hashCode(); + dummy += System.identityHashCode(strategy.copy(doubleArray)); } return dummy; } - public int timeFloats(int reps) { + @Benchmark int floats(int reps) { int dummy = 0; for (int i = 0; i < reps; i++) { - dummy += strategy.copy(floatArray).hashCode(); + dummy += System.identityHashCode(strategy.copy(floatArray)); } return dummy; } - public int timeInts(int reps) { + @Benchmark int ints(int reps) { int dummy = 0; for (int i = 0; i < reps; i++) { - dummy += strategy.copy(intArray).hashCode(); + dummy += System.identityHashCode(strategy.copy(intArray)); } return dummy; } - public int timeLongs(int reps) { + @Benchmark int longs(int reps) { int dummy = 0; for (int i = 0; i < reps; i++) { - dummy += strategy.copy(longArray).hashCode(); + dummy += System.identityHashCode(strategy.copy(longArray)); } return dummy; } - public int timeShorts(int reps) { + @Benchmark int shorts(int reps) { int dummy = 0; for (int i = 0; i < reps; i++) { - dummy += strategy.copy(shortArray).hashCode(); + dummy += System.identityHashCode(strategy.copy(shortArray)); } return dummy; } - - public static void main(String[] args) { - Runner.main(CopyArrayBenchmark.class, args); - } } diff --git a/examples/src/main/java/examples/DemoBenchmark.java b/examples/src/main/java/examples/DemoBenchmark.java index 3b7e2dd..3313eee 100644 --- a/examples/src/main/java/examples/DemoBenchmark.java +++ b/examples/src/main/java/examples/DemoBenchmark.java @@ -16,23 +16,24 @@ package examples; +import com.google.caliper.AfterExperiment; +import com.google.caliper.BeforeExperiment; +import com.google.caliper.Benchmark; import com.google.caliper.Param; -import com.google.caliper.api.Benchmark; import com.google.caliper.api.SkipThisScenarioException; -import com.google.caliper.api.VmParam; -import com.google.caliper.runner.CaliperMain; +import com.google.caliper.api.VmOptions; import com.google.caliper.util.ShortDuration; import java.math.BigDecimal; -public class DemoBenchmark extends Benchmark { +@VmOptions("-server") +public class DemoBenchmark { @Param({"abc", "def", "xyz"}) String string; @Param({"1", "2"}) int number; @Param Foo foo; @Param({"0.00", "123.45"}) BigDecimal money; @Param({"1ns", "2 minutes"}) ShortDuration duration; - @VmParam({"-Xmx32m", "-Xmx1g"}) String memoryMax; enum Foo { FOO, BAR, BAZ, QUX; @@ -42,13 +43,13 @@ public class DemoBenchmark extends Benchmark { // System.out.println("I should not do this."); } - @Override protected void setUp() throws Exception { + @BeforeExperiment void setUp() throws Exception { if (string.equals("abc") && number == 1) { throw new SkipThisScenarioException(); } } - public int timeSomething(int reps) { + @Benchmark int something(int reps) { int dummy = 0; for (int i = 0; i < reps; i++) { dummy += i; @@ -56,7 +57,7 @@ public class DemoBenchmark extends Benchmark { return dummy; } - public int timeSomethingElse(int reps) { + @Benchmark int somethingElse(int reps) { int dummy = 0; for (int i = 0; i < reps; i++) { dummy -= i; @@ -64,11 +65,7 @@ public class DemoBenchmark extends Benchmark { return dummy; } - @Override protected void tearDown() throws Exception { + @AfterExperiment void tearDown() throws Exception { // System.out.println("Hey, I'm tearing up the joint."); } - - public static void main(String[] args) { - CaliperMain.main(DemoBenchmark.class, args); - } } diff --git a/examples/src/main/java/examples/DoubleToStringBenchmark.java b/examples/src/main/java/examples/DoubleToStringBenchmark.java index 291ec84..d28f9b0 100644 --- a/examples/src/main/java/examples/DoubleToStringBenchmark.java +++ b/examples/src/main/java/examples/DoubleToStringBenchmark.java @@ -16,17 +16,13 @@ package examples; +import com.google.caliper.Benchmark; import com.google.caliper.Param; -import com.google.caliper.Runner; -import com.google.caliper.SimpleBenchmark; - -import java.util.Arrays; -import java.util.List; /** * Measures the various ways the JDK converts doubles to strings. */ -public class DoubleToStringBenchmark extends SimpleBenchmark { +public class DoubleToStringBenchmark { @Param Method method; public enum Method { @@ -83,7 +79,7 @@ public class DoubleToStringBenchmark extends SimpleBenchmark { @Param Value value; - public int timePrimitive(int reps) { + @Benchmark int primitive(int reps) { double d = value.value; int dummy = 0; for (int i = 0; i < reps; i++) { @@ -92,7 +88,7 @@ public class DoubleToStringBenchmark extends SimpleBenchmark { return dummy; } - public int timeWrapper(int reps) { + @Benchmark int wrapper(int reps) { Double d = value.value; int dummy = 0; for (int i = 0; i < reps; i++) { @@ -100,8 +96,4 @@ public class DoubleToStringBenchmark extends SimpleBenchmark { } return dummy; } - - public static void main(String[] args) throws Exception { - Runner.main(DoubleToStringBenchmark.class, args); - } } diff --git a/examples/src/main/java/examples/DoubleToStringBenchmark2.java b/examples/src/main/java/examples/DoubleToStringBenchmark2.java index 8e6c69a..3a6da5b 100644 --- a/examples/src/main/java/examples/DoubleToStringBenchmark2.java +++ b/examples/src/main/java/examples/DoubleToStringBenchmark2.java @@ -16,14 +16,13 @@ package examples; +import com.google.caliper.Benchmark; import com.google.caliper.Param; -import com.google.caliper.Runner; -import com.google.caliper.SimpleBenchmark; /** * Measures the various ways the JDK converts doubles to strings. */ -public class DoubleToStringBenchmark2 extends SimpleBenchmark { +public class DoubleToStringBenchmark2 { @Param boolean useWrapper; enum Value { @@ -41,7 +40,7 @@ public class DoubleToStringBenchmark2 extends SimpleBenchmark { @Param Value value; - public int timeToString(int reps) { + @Benchmark int toString(int reps) { int dummy = 0; if (useWrapper) { Double d = value.d; @@ -57,7 +56,7 @@ public class DoubleToStringBenchmark2 extends SimpleBenchmark { return dummy; } - public int timeStringValueOf(int reps) { + @Benchmark int stringValueOf(int reps) { int dummy = 0; if (useWrapper) { Double d = value.d; @@ -73,7 +72,7 @@ public class DoubleToStringBenchmark2 extends SimpleBenchmark { return dummy; } - public int timeStringFormat(int reps) { + @Benchmark int stringFormat(int reps) { int dummy = 0; if (useWrapper) { Double d = value.d; @@ -89,7 +88,7 @@ public class DoubleToStringBenchmark2 extends SimpleBenchmark { return dummy; } - public int timeQuoteTrick(int reps) { + @Benchmark int quoteTrick(int reps) { int dummy = 0; if (useWrapper) { Double d = value.d; @@ -104,8 +103,4 @@ public class DoubleToStringBenchmark2 extends SimpleBenchmark { } return dummy; } - - public static void main(String[] args) throws Exception { - Runner.main(DoubleToStringBenchmark2.class, args); - } } diff --git a/examples/src/main/java/examples/EnumSetContainsBenchmark.java b/examples/src/main/java/examples/EnumSetContainsBenchmark.java index b232514..10660a9 100644 --- a/examples/src/main/java/examples/EnumSetContainsBenchmark.java +++ b/examples/src/main/java/examples/EnumSetContainsBenchmark.java @@ -16,16 +16,17 @@ package examples; +import com.google.caliper.BeforeExperiment; +import com.google.caliper.Benchmark; import com.google.caliper.Param; -import com.google.caliper.Runner; -import com.google.caliper.SimpleBenchmark; + import java.util.EnumSet; import java.util.Set; /** * Measures EnumSet#contains(). */ -public class EnumSetContainsBenchmark extends SimpleBenchmark { +public class EnumSetContainsBenchmark { @Param private SetMaker setMaker; @@ -75,18 +76,14 @@ public class EnumSetContainsBenchmark extends SimpleBenchmark { private Set<?> set; private Object[] testValues; - @Override protected void setUp() { + @BeforeExperiment void setUp() { this.set = setMaker.newSet(); this.testValues = setMaker.testValues(); } - public void timeContains(int reps) { + @Benchmark void contains(int reps) { for (int i = 0; i < reps; i++) { set.contains(testValues[i % testValues.length]); } } - - public static void main(String[] args) throws Exception { - Runner.main(EnumSetContainsBenchmark.class, args); - } } diff --git a/examples/src/main/java/examples/ExpensiveObjectsBenchmark.java b/examples/src/main/java/examples/ExpensiveObjectsBenchmark.java index a11b1bd..7c4bc79 100644 --- a/examples/src/main/java/examples/ExpensiveObjectsBenchmark.java +++ b/examples/src/main/java/examples/ExpensiveObjectsBenchmark.java @@ -16,9 +16,7 @@ package examples; -import com.google.caliper.SimpleBenchmark; -import com.google.caliper.runner.CaliperMain; - +import com.google.caliper.Benchmark; import java.text.DecimalFormatSymbols; import java.text.NumberFormat; import java.text.SimpleDateFormat; @@ -28,49 +26,43 @@ import java.util.Locale; * Benchmarks creation and cloning various expensive objects. */ @SuppressWarnings({"ResultOfObjectAllocationIgnored"}) // TODO: should fix! -public class ExpensiveObjectsBenchmark extends SimpleBenchmark { - public void timeNewDecimalFormatSymbols(int reps) { +public class ExpensiveObjectsBenchmark { + @Benchmark void newDecimalFormatSymbols(int reps) { for (int i = 0; i < reps; ++i) { new DecimalFormatSymbols(Locale.US); } } - public void timeClonedDecimalFormatSymbols(int reps) { + @Benchmark void clonedDecimalFormatSymbols(int reps) { DecimalFormatSymbols dfs = new DecimalFormatSymbols(Locale.US); for (int i = 0; i < reps; ++i) { dfs.clone(); } } - public void timeNewNumberFormat(int reps) { + @Benchmark void newNumberFormat(int reps) { for (int i = 0; i < reps; ++i) { NumberFormat.getInstance(Locale.US); } } - public void timeClonedNumberFormat(int reps) { + @Benchmark void clonedNumberFormat(int reps) { NumberFormat nf = NumberFormat.getInstance(Locale.US); for (int i = 0; i < reps; ++i) { nf.clone(); } } - public void timeNewSimpleDateFormat(int reps) { + @Benchmark void newSimpleDateFormat(int reps) { for (int i = 0; i < reps; ++i) { new SimpleDateFormat(); } } - public void timeClonedSimpleDateFormat(int reps) { + @Benchmark void clonedSimpleDateFormat(int reps) { SimpleDateFormat sdf = new SimpleDateFormat(); for (int i = 0; i < reps; ++i) { sdf.clone(); } } - - // TODO: remove this from all examples when IDE plugins are ready - public static void main(String[] args) throws Exception { - CaliperMain.main(ExpensiveObjectsBenchmark.class, args); -// Runner.main(ExpensiveObjectsBenchmark.class, args); - } } diff --git a/examples/src/main/java/examples/FormatterBenchmark.java b/examples/src/main/java/examples/FormatterBenchmark.java index b4a0541..93ed936 100644 --- a/examples/src/main/java/examples/FormatterBenchmark.java +++ b/examples/src/main/java/examples/FormatterBenchmark.java @@ -16,36 +16,37 @@ package examples; -import com.google.caliper.Runner; -import com.google.caliper.SimpleBenchmark; +import com.google.caliper.Benchmark; import java.util.Formatter; /** * Compares Formatter against hand-written StringBuilder code. */ -public class FormatterBenchmark extends SimpleBenchmark { - public void timeFormatter_NoFormatting(int reps) { +public class FormatterBenchmark { + @Benchmark void formatter_NoFormatting(int reps) { for (int i = 0; i < reps; i++) { Formatter f = new Formatter(); f.format("this is a reasonably short string that doesn't actually need any formatting"); + f.close(); } } - public void timeStringBuilder_NoFormatting(int reps) { + @Benchmark void stringBuilder_NoFormatting(int reps) { for (int i = 0; i < reps; i++) { StringBuilder sb = new StringBuilder(); sb.append("this is a reasonably short string that doesn't actually need any formatting"); } } - public void timeFormatter_OneInt(int reps) { + @Benchmark void formatter_OneInt(int reps) { for (int i = 0; i < reps; i++) { Formatter f = new Formatter(); f.format("this is a reasonably short string that has an int %d in it", i); + f.close(); } } - public void timeStringBuilder_OneInt(int reps) { + @Benchmark void stringBuilder_OneInt(int reps) { for (int i = 0; i < reps; i++) { StringBuilder sb = new StringBuilder(); sb.append("this is a reasonably short string that has an int "); @@ -54,14 +55,15 @@ public class FormatterBenchmark extends SimpleBenchmark { } } - public void timeFormatter_OneString(int reps) { + @Benchmark void formatter_OneString(int reps) { for (int i = 0; i < reps; i++) { Formatter f = new Formatter(); f.format("this is a reasonably short string that has a string %s in it", "hello"); + f.close(); } } - public void timeStringBuilder_OneString(int reps) { + @Benchmark void stringBuilder_OneString(int reps) { for (int i = 0; i < reps; i++) { StringBuilder sb = new StringBuilder(); sb.append("this is a reasonably short string that has a string "); @@ -69,8 +71,4 @@ public class FormatterBenchmark extends SimpleBenchmark { sb.append(" in it"); } } - - public static void main(String[] args) throws Exception { - Runner.main(FormatterBenchmark.class, args); - } } diff --git a/examples/src/main/java/examples/IntModBenchmark.java b/examples/src/main/java/examples/IntModBenchmark.java index 55a119c..bed415f 100644 --- a/examples/src/main/java/examples/IntModBenchmark.java +++ b/examples/src/main/java/examples/IntModBenchmark.java @@ -16,17 +16,16 @@ package examples; -import com.google.caliper.Runner; -import com.google.caliper.SimpleBenchmark; +import com.google.caliper.Benchmark; /** * Measures several candidate implementations for mod(). */ @SuppressWarnings("SameParameterValue") -public class IntModBenchmark extends SimpleBenchmark { +public class IntModBenchmark { private static final int M = (1 << 16) - 1; - public int timeConditional(int reps) { + @Benchmark int conditional(int reps) { int dummy = 5; for (int i = 0; i < reps; i++) { dummy += Integer.MAX_VALUE + conditionalMod(dummy, M); @@ -39,7 +38,7 @@ public class IntModBenchmark extends SimpleBenchmark { return r < 0 ? r + m : r; } - public int timeDoubleRemainder(int reps) { + @Benchmark int doubleRemainder(int reps) { int dummy = 5; for (int i = 0; i < reps; i++) { dummy += Integer.MAX_VALUE + doubleRemainderMod(dummy, M); @@ -52,7 +51,7 @@ public class IntModBenchmark extends SimpleBenchmark { return (int) ((a % m + (long) m) % m); } - public int timeRightShiftingMod(int reps) { + @Benchmark int rightShiftingMod(int reps) { int dummy = 5; for (int i = 0; i < reps; i++) { dummy += Integer.MAX_VALUE + rightShiftingMod(dummy, M); @@ -66,7 +65,7 @@ public class IntModBenchmark extends SimpleBenchmark { return (int) (r + (r >> 63 & m)); } - public int timeLeftShiftingMod(int reps) { + @Benchmark int leftShiftingMod(int reps) { int dummy = 5; for (int i = 0; i < reps; i++) { dummy += Integer.MAX_VALUE + leftShiftingMod(dummy, M); @@ -79,16 +78,11 @@ public class IntModBenchmark extends SimpleBenchmark { return (int) ((a + ((long) m << 32)) % m); } - public int timeWrongMod(int reps) { + @Benchmark int wrongMod(int reps) { int dummy = 5; for (int i = 0; i < reps; i++) { dummy += Integer.MAX_VALUE + dummy % M; } return dummy; } - - // TODO: remove this from all examples when IDE plugins are ready - public static void main(String[] args) throws Exception { - Runner.main(IntModBenchmark.class, args); - } -}
\ No newline at end of file +} diff --git a/examples/src/main/java/examples/ListIterationBenchmark.java b/examples/src/main/java/examples/ListIterationBenchmark.java index a8cfb05..07ae8eb 100644 --- a/examples/src/main/java/examples/ListIterationBenchmark.java +++ b/examples/src/main/java/examples/ListIterationBenchmark.java @@ -16,16 +16,17 @@ package examples; +import com.google.caliper.BeforeExperiment; +import com.google.caliper.Benchmark; import com.google.caliper.Param; -import com.google.caliper.Runner; -import com.google.caliper.SimpleBenchmark; + import java.util.AbstractList; import java.util.List; /** * Measures iterating through list elements. */ -public class ListIterationBenchmark extends SimpleBenchmark { +public class ListIterationBenchmark { @Param({"0", "10", "100", "1000"}) private int length; @@ -33,7 +34,7 @@ public class ListIterationBenchmark extends SimpleBenchmark { private List<Object> list; private Object[] array; - @Override protected void setUp() { + @BeforeExperiment void setUp() { array = new Object[length]; for (int i = 0; i < length; i++) { array[i] = new Object(); @@ -50,24 +51,23 @@ public class ListIterationBenchmark extends SimpleBenchmark { }; } - @SuppressWarnings({"UnusedDeclaration"}) // TODO: fix - public void timeListIteration(int reps) { + @Benchmark int listIteration(int reps) { + int dummy = 0; for (int i = 0; i < reps; i++) { for (Object value : list) { + dummy |= value.hashCode(); } } + return dummy; } - @SuppressWarnings({"UnusedDeclaration"}) // TODO: fix - public void timeArrayIteration(int reps) { + @Benchmark int arrayIteration(int reps) { + int dummy = 0; for (int i = 0; i < reps; i++) { for (Object value : array) { + dummy |= value.hashCode(); } } - } - - // TODO: remove this from all examples when IDE plugins are ready - public static void main(String[] args) throws Exception { - Runner.main(ListIterationBenchmark.class, args); + return dummy; } }
\ No newline at end of file diff --git a/examples/src/main/java/examples/ListModificationBenchmark.java b/examples/src/main/java/examples/ListModificationBenchmark.java new file mode 100644 index 0000000..81d14f9 --- /dev/null +++ b/examples/src/main/java/examples/ListModificationBenchmark.java @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2012 Google Inc. + * + * 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 examples; + +import com.google.caliper.BeforeExperiment; +import com.google.caliper.Benchmark; +import com.google.caliper.Param; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; + +/** + * Measures performance of list operations. + */ +public class ListModificationBenchmark { + + private enum Element { + INSTANCE, + } + private enum ListImpl { + Array { + @Override List<Element> create() { + return new ArrayList<Element>(); + } + }, + Linked { + @Override List<Element> create() { + return new LinkedList<Element>(); + } + }; + + abstract List<Element> create(); + } + + @Param({"10", "100", "1000", "10000"}) + private int size; + + @Param({"Array", "Linked"}) + private ListImpl implementation; + + private List<Element> list; + + @BeforeExperiment void setUp() throws Exception { + list = implementation.create(); + for (int i = 0; i < size; i++) { + list.add(Element.INSTANCE); + } + } + + @Benchmark void populate(int reps) throws Exception { + for (int rep = 0; rep < reps; rep++) { + List<Element> list = implementation.create(); + for (int i = 0; i < size; i++) { + list.add(Element.INSTANCE); + } + } + } + + @Benchmark void iteration(int reps) { + for (int rep = 0; rep < reps; rep++) { + Iterator<Element> iterator = list.iterator(); + while (iterator.hasNext()) { + iterator.next(); + } + } + } + + @Benchmark void headAddRemove(int reps) { + for (int rep = 0; rep < reps; rep++) { + list.add(0, Element.INSTANCE); + list.remove(0); + } + } + + @Benchmark void middleAddRemove(int reps) { + int index = size / 2; + for (int rep = 0; rep < reps; rep++) { + list.add(index, Element.INSTANCE); + list.remove(index); + } + } + + @Benchmark void tailAddRemove(int reps) { + int index = size - 1; + for (int rep = 0; rep < reps; rep++) { + list.add(Element.INSTANCE); + list.remove(index); + } + } +} diff --git a/examples/src/main/java/examples/LoopingBackwardsBenchmark.java b/examples/src/main/java/examples/LoopingBackwardsBenchmark.java index 1e3d1ad..d23a28d 100644 --- a/examples/src/main/java/examples/LoopingBackwardsBenchmark.java +++ b/examples/src/main/java/examples/LoopingBackwardsBenchmark.java @@ -16,17 +16,16 @@ package examples; +import com.google.caliper.Benchmark; import com.google.caliper.Param; -import com.google.caliper.Runner; -import com.google.caliper.SimpleBenchmark; /** * Testing the old canard that looping backwards is faster. */ -public class LoopingBackwardsBenchmark extends SimpleBenchmark { +public class LoopingBackwardsBenchmark { @Param({"2", "20", "2000", "20000000"}) int max; - public int timeForwards(int reps) { + @Benchmark int forwards(int reps) { int dummy = 0; for (int i = 0; i < reps; i++) { for (int j = 0; j < max; j++) { @@ -36,7 +35,7 @@ public class LoopingBackwardsBenchmark extends SimpleBenchmark { return dummy; } - public int timeBackwards(int reps) { + @Benchmark int backwards(int reps) { int dummy = 0; for (int i = 0; i < reps; i++) { for (int j = max - 1; j >= 0; j--) { @@ -45,8 +44,4 @@ public class LoopingBackwardsBenchmark extends SimpleBenchmark { } return dummy; } - - public static void main(String[] args) throws Exception { - Runner.main(LoopingBackwardsBenchmark.class, args); - } } diff --git a/examples/src/main/java/examples/MessageDigestCreationBenchmark.java b/examples/src/main/java/examples/MessageDigestCreationBenchmark.java index c9437bd..681d10e 100644 --- a/examples/src/main/java/examples/MessageDigestCreationBenchmark.java +++ b/examples/src/main/java/examples/MessageDigestCreationBenchmark.java @@ -16,29 +16,24 @@ package examples; +import com.google.caliper.Benchmark; import com.google.caliper.Param; -import com.google.caliper.Runner; -import com.google.caliper.SimpleBenchmark; import java.security.MessageDigest; /** * Times creating new MessageDigest instances. */ -public class MessageDigestCreationBenchmark extends SimpleBenchmark { +public class MessageDigestCreationBenchmark { // By default, just the "interesting ones". Also consider Adler32 and CRC32, // but these are not guaranteed to be supported in all runtime environments. @Param({"MD5", "SHA-1", "SHA-256", "SHA-512"}) String algorithm; - public void time(int reps) throws Exception { + @Benchmark void time(int reps) throws Exception { // Change this to use a dummy if the results look suspicious. for (int i = 0; i < reps; i++) { MessageDigest.getInstance(algorithm); } } - - public static void main(String[] args) throws Exception { - Runner.main(MessageDigestCreationBenchmark.class, args); - } } diff --git a/examples/src/main/java/examples/NoOpBenchmark.java b/examples/src/main/java/examples/NoOpBenchmark.java new file mode 100644 index 0000000..fa24805 --- /dev/null +++ b/examples/src/main/java/examples/NoOpBenchmark.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2013 Google Inc. + * + * 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 examples; + +import com.google.caliper.Benchmark; + +/** + * This is the absolute minimal benchmark. It does nothing but time the rep loop. + */ +public class NoOpBenchmark { + @Benchmark long increment(long reps) { + long result = 0; + for (; result < reps; result++) {} + return result; + } +} diff --git a/examples/src/main/java/examples/StringBuilderBenchmark.java b/examples/src/main/java/examples/StringBuilderBenchmark.java index 3c839d3..c39b3be 100644 --- a/examples/src/main/java/examples/StringBuilderBenchmark.java +++ b/examples/src/main/java/examples/StringBuilderBenchmark.java @@ -16,117 +16,176 @@ package examples; +import static java.lang.Character.MIN_SURROGATE; + +import com.google.caliper.Benchmark; import com.google.caliper.Param; -import com.google.caliper.Runner; -import com.google.caliper.SimpleBenchmark; /** * Tests the performance of various StringBuilder methods. */ -public class StringBuilderBenchmark extends SimpleBenchmark { +public class StringBuilderBenchmark { - @Param({"1", "10", "100"}) private int length; + @Param({"1", "10", "100"}) private int length; - public void timeAppendBoolean(int reps) { - for (int i = 0; i < reps; ++i) { - StringBuilder sb = new StringBuilder(); - for (int j = 0; j < length; ++j) { - sb.append(true); - } - } + @Benchmark void appendBoolean(int reps) { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < reps; ++i) { + sb.setLength(0); + for (int j = 0; j < length; ++j) { + sb.append(true); + sb.append(false); + } } + } - public void timeAppendChar(int reps) { - for (int i = 0; i < reps; ++i) { - StringBuilder sb = new StringBuilder(); - for (int j = 0; j < length; ++j) { - sb.append('c'); - } - } + @Benchmark void appendChar(int reps) { + for (int i = 0; i < reps; ++i) { + StringBuilder sb = new StringBuilder(); + for (int j = 0; j < length; ++j) { + sb.append('c'); + } } + } - public void timeAppendCharArray(int reps) { - char[] chars = "chars".toCharArray(); - for (int i = 0; i < reps; ++i) { - StringBuilder sb = new StringBuilder(); - for (int j = 0; j < length; ++j) { - sb.append(chars); - } - } + @Benchmark void appendCharArray(int reps) { + char[] chars = "chars".toCharArray(); + for (int i = 0; i < reps; ++i) { + StringBuilder sb = new StringBuilder(); + for (int j = 0; j < length; ++j) { + sb.append(chars); + } } + } - public void timeAppendCharSequence(int reps) { - CharSequence cs = "chars"; - for (int i = 0; i < reps; ++i) { - StringBuilder sb = new StringBuilder(); - for (int j = 0; j < length; ++j) { - sb.append(cs); - } - } + @Benchmark void appendCharSequence(int reps) { + CharSequence cs = "chars"; + for (int i = 0; i < reps; ++i) { + StringBuilder sb = new StringBuilder(); + for (int j = 0; j < length; ++j) { + sb.append(cs); + } } + } - public void timeAppendDouble(int reps) { - double d = 1.2; - for (int i = 0; i < reps; ++i) { - StringBuilder sb = new StringBuilder(); - for (int j = 0; j < length; ++j) { - sb.append(d); - } - } + @Benchmark void appendDouble(int reps) { + double d = 1.2; + for (int i = 0; i < reps; ++i) { + StringBuilder sb = new StringBuilder(); + for (int j = 0; j < length; ++j) { + sb.append(d); + } } + } - public void timeAppendFloat(int reps) { - float f = 1.2f; - for (int i = 0; i < reps; ++i) { - StringBuilder sb = new StringBuilder(); - for (int j = 0; j < length; ++j) { - sb.append(f); - } - } + @Benchmark void appendFloat(int reps) { + float f = 1.2f; + for (int i = 0; i < reps; ++i) { + StringBuilder sb = new StringBuilder(); + for (int j = 0; j < length; ++j) { + sb.append(f); + } } + } - public void timeAppendInt(int reps) { - int n = 123; - for (int i = 0; i < reps; ++i) { - StringBuilder sb = new StringBuilder(); - for (int j = 0; j < length; ++j) { - sb.append(n); - } - } + @Benchmark void appendInt(int reps) { + int n = 123; + for (int i = 0; i < reps; ++i) { + StringBuilder sb = new StringBuilder(); + for (int j = 0; j < length; ++j) { + sb.append(n); + } } + } - public void timeAppendLong(int reps) { - long l = 123; - for (int i = 0; i < reps; ++i) { - StringBuilder sb = new StringBuilder(); - for (int j = 0; j < length; ++j) { - sb.append(l); - } - } + @Benchmark void appendLong(int reps) { + long l = 123; + for (int i = 0; i < reps; ++i) { + StringBuilder sb = new StringBuilder(); + for (int j = 0; j < length; ++j) { + sb.append(l); + } } + } - public void timeAppendObject(int reps) { - Object o = new Object(); - for (int i = 0; i < reps; ++i) { - StringBuilder sb = new StringBuilder(); - for (int j = 0; j < length; ++j) { - sb.append(o); - } - } + @Benchmark void appendObject(int reps) { + Object o = new Object(); + for (int i = 0; i < reps; ++i) { + StringBuilder sb = new StringBuilder(); + for (int j = 0; j < length; ++j) { + sb.append(o); + } } + } - public void timeAppendString(int reps) { - String s = "chars"; - for (int i = 0; i < reps; ++i) { - StringBuilder sb = new StringBuilder(); - for (int j = 0; j < length; ++j) { - sb.append(s); - } + @Benchmark void appendString(int reps) { + String s = "chars"; + for (int i = 0; i < reps; ++i) { + StringBuilder sb = new StringBuilder(); + for (int j = 0; j < length; ++j) { + sb.append(s); + } + } + } + + @Benchmark void appendNull(int reps) { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < reps; ++i) { + sb.setLength(0); + for (int j = 0; j < length; ++j) { + sb.append((String)null); + sb.append((StringBuilder)null); + } + } + } + + /** Times .reverse() when no surrogates are present. */ + @Benchmark void reverseNoSurrogates(int reps) { + final int length = Math.min(this.length, MIN_SURROGATE); + StringBuilder sb = new StringBuilder(); + for (int j = 0; j < length; j++) { + sb.appendCodePoint(j); + } + for (int i = 0; i < reps; i++) { + for (int j = 0; j < 4; j++) { + sb.reverse(); + } + if (sb.codePointAt(0) > MIN_SURROGATE) + throw new Error(); + } + } + + /** Times .codePointAt(int) when no surrogates are present. */ + @Benchmark void codePointAtNoSurrogates(int reps) { + final int length = Math.min(this.length, MIN_SURROGATE); + StringBuilder sb = new StringBuilder(); + for (int j = 0; j < length; j++) { + sb.appendCodePoint(j); + } + for (int i = 0; i < reps; i++) { + for (int j = 0; j < 4; j++) { + for (int k = 0; k < length - 1; k++) { + if (sb.codePointAt(k) > MIN_SURROGATE) + throw new Error(); } + } } + } - // TODO: remove this from all examples when IDE plugins are ready - public static void main(String[] args) throws Exception { - Runner.main(StringBuilderBenchmark.class, args); + /** Times .codePointBefore(int) when no surrogates are present. */ + @Benchmark void codePointBeforeNoSurrogates(int reps) { + final int length = Math.min(this.length, MIN_SURROGATE); + StringBuilder sb = new StringBuilder(); + for (int j = 0; j < length; j++) { + sb.appendCodePoint(j); + } + for (int i = 0; i < reps; i++) { + for (int j = 0; j < 4; j++) { + for (int k = 1; k < length; k++) { + if (sb.codePointBefore(k) > MIN_SURROGATE) + throw new Error(); + } + } } + } } diff --git a/examples/src/main/java/examples/Utf8Benchmark.java b/examples/src/main/java/examples/Utf8Benchmark.java new file mode 100644 index 0000000..97b4e00 --- /dev/null +++ b/examples/src/main/java/examples/Utf8Benchmark.java @@ -0,0 +1,141 @@ +/* + * Copyright (C) 2011 Google Inc. + * + * 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 examples; + +import com.google.caliper.BeforeExperiment; +import com.google.caliper.Benchmark; +import com.google.caliper.Param; + +import java.nio.charset.Charset; +import java.util.Random; + +/** + * Benchmark for operations with the UTF-8 charset. + */ +public class Utf8Benchmark { + + static final Charset UTF_8 = Charset.forName("UTF-8"); + + /** + * The maximum code point used in generated text. Different values + * provide reasonable models of different real-world human text. + */ + static class MaxCodePoint { + final int value; + + /** + * Convert the input string to a code point. Accepts regular + * decimal numerals, hex strings, and some symbolic names + * meaningful to humans. + */ + private static int decode(String userFriendly) { + try { + return Integer.decode(userFriendly); + } catch (NumberFormatException ignored) { + if (userFriendly.matches("(?i)(?:American|English|ASCII)")) { + // 1-byte UTF-8 sequences - "American" ASCII text + return 0x80; + } else if (userFriendly.matches("(?i)(?:French|Latin|Western.*European)")) { + // Mostly 1-byte UTF-8 sequences, mixed with occasional 2-byte + // sequences - "Western European" text + return 0x90; + } else if (userFriendly.matches("(?i)(?:Branch.*Prediction.*Hostile)")) { + // Defeat branch predictor for: c < 0x80 ; branch taken 50% of the time. + return 0x100; + } else if (userFriendly.matches("(?i)(?:Greek|Cyrillic|European|ISO.?8859)")) { + // Mostly 2-byte UTF-8 sequences - "European" text + return 0x800; + } else if (userFriendly.matches("(?i)(?:Chinese|Han|Asian|BMP)")) { + // Mostly 3-byte UTF-8 sequences - "Asian" text + return Character.MIN_SUPPLEMENTARY_CODE_POINT; + } else if (userFriendly.matches("(?i)(?:Cuneiform|rare|exotic|supplementary.*)")) { + // Mostly 4-byte UTF-8 sequences - "rare exotic" text + return Character.MAX_CODE_POINT; + } else { + throw new IllegalArgumentException("Can't decode codepoint " + userFriendly); + } + } + } + + public static MaxCodePoint valueOf(String userFriendly) { + return new MaxCodePoint(userFriendly); + } + + private MaxCodePoint(String userFriendly) { + value = decode(userFriendly); + } + } + + /** + * The default values of maxCodePoint below provide pretty good + * performance models of different kinds of common human text. + * @see MaxCodePoint#decode + */ + @Param({"0x80", "0x100", "0x800", "0x10000", "0x10ffff"}) MaxCodePoint maxCodePoint; + + static final int STRING_COUNT = 1 << 7; + + @Param({"65536"}) int charCount; + private String[] strings; + + /** + * Computes arrays of valid unicode Strings. + */ + @BeforeExperiment void setUp() { + final long seed = 99; + final Random rnd = new Random(seed); + strings = new String[STRING_COUNT]; + for (int i = 0; i < STRING_COUNT; i++) { + StringBuilder sb = new StringBuilder(); + for (int j = 0; j < charCount; j++) { + int codePoint; + // discard illegal surrogate "codepoints" + do { + codePoint = rnd.nextInt(maxCodePoint.value); + } while (isSurrogate(codePoint)); + sb.appendCodePoint(codePoint); + } + strings[i] = sb.toString(); + } + // The reps will continue until the non-determinism detector is pacified! + getBytes(100); + } + + /** + * Benchmarks {@link String#getBytes} on valid strings containing + * pseudo-randomly-generated codePoints less than {@code + * maxCodePoint}. A constant seed is used, so separate runs perform + * identical computations. + */ + @Benchmark void getBytes(int reps) { + final String[] strings = this.strings; + final int mask = STRING_COUNT - 1; + for (int i = 0; i < reps; i++) { + String string = strings[i & mask]; + byte[] bytes = string.getBytes(UTF_8); + if (bytes[0] == 86 && bytes[bytes.length - 1] == 99) { + throw new Error("Unlikely! We're just defeating the optimizer!"); + } + } + } + + /** Character.isSurrogate was added in Java SE 7. */ + private boolean isSurrogate(int c) { + return (Character.MIN_HIGH_SURROGATE <= c && + c <= Character.MAX_LOW_SURROGATE); + } +} diff --git a/examples/src/main/java/examples/VarargsBenchmark.java b/examples/src/main/java/examples/VarargsBenchmark.java new file mode 100644 index 0000000..eceb72d --- /dev/null +++ b/examples/src/main/java/examples/VarargsBenchmark.java @@ -0,0 +1,236 @@ +/* + * Copyright (C) 2012 Google Inc. + * + * 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 examples; + +import com.google.caliper.BeforeExperiment; +import com.google.caliper.Benchmark; +import com.google.caliper.Param; +import com.google.caliper.api.SkipThisScenarioException; + +import java.util.Random; + +/** + * Benchmarks the overhead created by using varargs instead of parameter expansion. + * + * @author gak@google.com (Gregory Kick) + */ +public final class VarargsBenchmark { + enum Strategy { + VARARGS { + @Override long one(long a) { + return varargs(a); + } + + @Override long two(long a, long b) { + return varargs(a, b); + } + + @Override long three(long a, long b, long c) { + return varargs(a, b, c); + } + + @Override long four(long a, long b, long c, long d) { + return varargs(a, b, c, d); + } + + @Override long five(long a, long b, long c, long d, long e) { + return varargs(a, b, c, d); + } + + @Override long six(long a, long b, long c, long d, long e, long f) { + return varargs(a, b, c, d, e, f); + }}, + EXPANSION { + @Override long one(long a) { + return VarargsBenchmark.one(a); + } + + @Override long two(long a, long b) { + return VarargsBenchmark.two(a, b); + } + + @Override long three(long a, long b, long c) { + return VarargsBenchmark.three(a, b, c); + } + + @Override long four(long a, long b, long c, long d) { + return VarargsBenchmark.four(a, b, c, d); + } + + @Override long five(long a, long b, long c, long d, long e) { + return VarargsBenchmark.five(a, b, c, d, e); + } + + @Override long six(long a, long b, long c, long d, long e, long f) { + return VarargsBenchmark.six(a, b, c, d, e, f); + } + }; + + abstract long one(long a); + + abstract long two(long a, long b); + + abstract long three(long a, long b, long c); + + abstract long four(long a, long b, long c, long d); + + abstract long five(long a, long b, long c, long d, long e); + + abstract long six(long a, long b, long c, long d, long e, long f); + } + + private static long varargs(long... longs) { + long result = 0; + for (long i : longs) { + result ^= i; + } + return result; + } + + private static long one(long a) { + return a; + } + + private static long two(long a, long b) { + return a ^ b; + } + + private static long three(long a, long b, long c) { + return a ^ b ^ c; + } + + private static long four(long a, long b, long c, long d) { + return a ^ b ^ c ^ d; + } + + private static long five(long a, long b, long c, long d, long e) { + return a ^ b ^ c ^ d ^ e; + } + + private static long six(long a, long b, long c, long d, long e, long f) { + return a ^ b ^ c ^ d ^ e ^ f; + } + + @Param private Strategy strategy; + @Param({"1", "2", "3", "4", "5", "6"}) private int arguments; + + private long[] data = new long[2048]; + + @BeforeExperiment void setUp() { + Random random = new Random(); + for (int i = 0; i < data.length; i++) { + data[i] = random.nextLong(); + } + } + + @Benchmark long invocation(int reps) { + switch (arguments) { + case 1: + return oneArgument(reps); + case 2: + return twoArguments(reps); + case 3: + return threeArguments(reps); + case 4: + return fourArguments(reps); + case 5: + return fiveArguments(reps); + case 6: + return sixArguments(reps); + default: + throw new SkipThisScenarioException(); + } + } + + private long oneArgument(int reps) { + long dummy = 0; + long[] data = this.data; + int dataLength = data.length; + for (int i = 0; i < reps; i++) { + dummy += strategy.one(data[i % dataLength]); + } + return dummy; + } + + private long twoArguments(int reps) { + long dummy = 0; + long[] data = this.data; + int dataLength = data.length; + for (int i = 0; i < reps; i++) { + dummy += strategy.two(data[i % dataLength], data[(i + 1) % dataLength]); + } + return dummy; + } + + private long threeArguments(int reps) { + long dummy = 0; + long[] data = this.data; + int dataLength = data.length; + for (int i = 0; i < reps; i++) { + dummy += strategy.three( + data[i % dataLength], + data[(i + 1) % dataLength], + data[(i + 2) % dataLength]); + } + return dummy; + } + + private long fourArguments(int reps) { + long dummy = 0; + long[] data = this.data; + int dataLength = data.length; + for (int i = 0; i < reps; i++) { + dummy += strategy.four( + data[i % dataLength], + data[(i + 1) % dataLength], + data[(i + 2) % dataLength], + data[(i + 3) % dataLength]); + } + return dummy; + } + + private long fiveArguments(int reps) { + long dummy = 0; + long[] data = this.data; + int dataLength = data.length; + for (int i = 0; i < reps; i++) { + dummy += strategy.five( + data[i % dataLength], + data[(i + 1) % dataLength], + data[(i + 2) % dataLength], + data[(i + 3) % dataLength], + data[(i + 4) % dataLength]); + } + return dummy; + } + + private long sixArguments(int reps) { + long dummy = 0; + long[] data = this.data; + int dataLength = data.length; + for (int i = 0; i < reps; i++) { + dummy += strategy.six( + data[i % dataLength], + data[(i + 1) % dataLength], + data[(i + 2) % dataLength], + data[(i + 3) % dataLength], + data[(i + 4) % dataLength], + data[(i + 5) % dataLength]); + } + return dummy; + } +} diff --git a/expectations/knownfailures.txt b/expectations/knownfailures.txt new file mode 100644 index 0000000..8caadbe --- /dev/null +++ b/expectations/knownfailures.txt @@ -0,0 +1,76 @@ +[ +{ + description: "ObjectGraphMeasurer doesn't work on Android", + names: [ + "com.google.caliper.memory.ObjectGraphMeasurerTest" + ] +}, +{ + description: "Ljava/lang/management/ManagementFactory; doesn't exist on Android", + names: [ + "com.google.caliper.config.CaliperConfigTest#getDefaultVmConfig", + "com.google.caliper.runner.StreamServiceTest", + "com.google.caliper.runner.WorkerProcessTest" + ] +}, +{ + description: "@RunWith(MockitoJUnitRunner) doesn't work in Vogar", + names: [ + "com.google.caliper.config.CaliperConfigLoaderTest", + "com.google.caliper.config.LoggingConfigLoaderTest", + "com.google.caliper.runner.ExperimentingRunnerModuleTest" + ] +}, +{ + description: "Assumes it is running on a standard Java platform with java binary", + names: [ + "com.google.caliper.config.VmConfigTest#testExecutable" + ] +}, +{ + description: "AllocationInstrument doesn't work on Android", + names: [ + "com.google.caliper.runner.AllocationInstrumentTest", + "com.google.caliper.runner.BadUserCodeTest#testComplexNonDeterministicAllocation_noTrackAllocations", + "com.google.caliper.runner.BadUserCodeTest#testComplexNonDeterministicAllocation_trackAllocations", + "com.google.caliper.runner.BadUserCodeTest#testNonDeterministicAllocation_noTrackAllocations", + "com.google.caliper.runner.BadUserCodeTest#testNonDeterministicAllocation_trackAllocations", + "com.google.caliper.runner.MalformedBenchmarksTest#noBenchmarkMethods" + ] +}, +{ + description: "ArbitraryMeasurementInstrument doesn't work on Android", + names: [ + "com.google.caliper.runner.ArbitraryMeasurmentInstrumentTest" + ] +}, +{ + description: "Android only has 256M heap", + names: [ + "com.google.caliper.runner.RuntimeInstrumentTest#gcBeforeEachOptionIsHonored", + "com.google.caliper.runner.RuntimeInstrumentTest#gcBeforeEachOptionIsReallyNecessary" + ] +}, +{ + description: "Checks for a specific exception message that is an implementation detail of JVM", + names: [ + "com.google.caliper.runner.MalformedBenchmarksTest#unparsableParamDefault" + ] +}, +{ + description: "Unknown cause", + names: [ + "com.google.caliper.runner.ServerSocketServiceTest#getConnectionStoppedService", + "com.google.caliper.runner.BadUserCodeTest#testExceptionInMethod_notInDryRun",# + "com.google.caliper.runner.RuntimeInstrumentTest#maxWarmupWallTimeOptionIsHonored" + ] +}, +{ + description: "Possible race in parse code that causes dryRun to be true when it is false, goes away under debug", + names: [ + "com.google.caliper.options.ParsedOptionsTest#testDefaults_DoNotRequireBenchmarkClassName", + "com.google.caliper.options.ParsedOptionsTest#testDefaults_RequireBenchmarkClassName", + "com.google.caliper.options.ParsedOptionsTest#testKitchenSink" + ] +} +] diff --git a/lib/gson-1.7.1.jar b/lib/gson-1.7.1.jar Binary files differdeleted file mode 100644 index eb54274..0000000 --- a/lib/gson-1.7.1.jar +++ /dev/null diff --git a/lib/gson-2.2.2-sources.jar b/lib/gson-2.2.2-sources.jar Binary files differnew file mode 100644 index 0000000..26c43d2 --- /dev/null +++ b/lib/gson-2.2.2-sources.jar diff --git a/lib/gson-2.2.2.jar b/lib/gson-2.2.2.jar Binary files differnew file mode 100644 index 0000000..9adc66f --- /dev/null +++ b/lib/gson-2.2.2.jar diff --git a/lib/gson-1.7.1.jar.txt b/lib/gson-2.2.2.jar.txt index 892eaed..892eaed 100644 --- a/lib/gson-1.7.1.jar.txt +++ b/lib/gson-2.2.2.jar.txt diff --git a/lib/java-allocation-instrumenter-2.0-sources.jar b/lib/java-allocation-instrumenter-2.0-sources.jar Binary files differnew file mode 100644 index 0000000..d8cc489 --- /dev/null +++ b/lib/java-allocation-instrumenter-2.0-sources.jar diff --git a/lib/jersey-client-1.11-sources.jar b/lib/jersey-client-1.11-sources.jar Binary files differnew file mode 100644 index 0000000..58164ca --- /dev/null +++ b/lib/jersey-client-1.11-sources.jar diff --git a/lib/jersey-client-1.11.jar b/lib/jersey-client-1.11.jar Binary files differnew file mode 100644 index 0000000..42d8925 --- /dev/null +++ b/lib/jersey-client-1.11.jar diff --git a/lib/jersey-client-1.11.jar.txt b/lib/jersey-client-1.11.jar.txt new file mode 100644 index 0000000..8681d13 --- /dev/null +++ b/lib/jersey-client-1.11.jar.txt @@ -0,0 +1,712 @@ +COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.1 + +1. Definitions. + + 1.1. "Contributor" means each individual or entity that creates or + contributes to the creation of Modifications. + + 1.2. "Contributor Version" means the combination of the Original + Software, prior Modifications used by a Contributor (if any), and the + Modifications made by that particular Contributor. + + 1.3. "Covered Software" means (a) the Original Software, or (b) + Modifications, or (c) the combination of files containing Original + Software with files containing Modifications, in each case including + portions thereof. + + 1.4. "Executable" means the Covered Software in any form other than + Source Code. + + 1.5. "Initial Developer" means the individual or entity that first makes + Original Software available under this License. + + 1.6. "Larger Work" means a work which combines Covered Software or + portions thereof with code not governed by the terms of this License. + + 1.7. "License" means this document. + + 1.8. "Licensable" means having the right to grant, to the maximum extent + possible, whether at the time of the initial grant or subsequently + acquired, any and all of the rights conveyed herein. + + 1.9. "Modifications" means the Source Code and Executable form of any of + the following: + + A. Any file that results from an addition to, deletion from or + modification of the contents of a file containing Original Software or + previous Modifications; + + B. Any new file that contains any part of the Original Software or + previous Modification; or + + C. Any new file that is contributed or otherwise made available under + the terms of this License. + + 1.10. "Original Software" means the Source Code and Executable form of + computer software code that is originally released under this License. + + 1.11. "Patent Claims" means any patent claim(s), now owned or hereafter + acquired, including without limitation, method, process, and apparatus + claims, in any patent Licensable by grantor. + + 1.12. "Source Code" means (a) the common form of computer software code + in which modifications are made and (b) associated documentation + included in or with such code. + + 1.13. "You" (or "Your") means an individual or a legal entity exercising + rights under, and complying with all of the terms of, this License. For + legal entities, "You" includes any entity which controls, is controlled + by, or is under common control with You. For purposes of this + definition, "control" means (a) the power, direct or indirect, to cause + the direction or management of such entity, whether by contract or + otherwise, or (b) ownership of more than fifty percent (50%) of the + outstanding shares or beneficial ownership of such entity. + +2. License Grants. + + 2.1. The Initial Developer Grant. + + Conditioned upon Your compliance with Section 3.1 below and subject to + third party intellectual property claims, the Initial Developer hereby + grants You a world-wide, royalty-free, non-exclusive license: + + (a) under intellectual property rights (other than patent or trademark) + Licensable by Initial Developer, to use, reproduce, modify, display, + perform, sublicense and distribute the Original Software (or portions + thereof), with or without Modifications, and/or as part of a Larger + Work; and + + (b) under Patent Claims infringed by the making, using or selling of + Original Software, to make, have made, use, practice, sell, and offer + for sale, and/or otherwise dispose of the Original Software (or portions + thereof). + + (c) The licenses granted in Sections 2.1(a) and (b) are effective on the + date Initial Developer first distributes or otherwise makes the Original + Software available to a third party under the terms of this License. + + (d) Notwithstanding Section 2.1(b) above, no patent license is granted: + (1) for code that You delete from the Original Software, or (2) for + infringements caused by: (i) the modification of the Original Software, + or (ii) the combination of the Original Software with other software or + devices. + + 2.2. Contributor Grant. + + Conditioned upon Your compliance with Section 3.1 below and subject to + third party intellectual property claims, each Contributor hereby grants + You a world-wide, royalty-free, non-exclusive license: + + (a) under intellectual property rights (other than patent or trademark) + Licensable by Contributor to use, reproduce, modify, display, perform, + sublicense and distribute the Modifications created by such Contributor + (or portions thereof), either on an unmodified basis, with other + Modifications, as Covered Software and/or as part of a Larger Work; and + + (b) under Patent Claims infringed by the making, using, or selling of + Modifications made by that Contributor either alone and/or in + combination with its Contributor Version (or portions of such + combination), to make, use, sell, offer for sale, have made, and/or + otherwise dispose of: (1) Modifications made by that Contributor (or + portions thereof); and (2) the combination of Modifications made by that + Contributor with its Contributor Version (or portions of such + combination). + + (c) The licenses granted in Sections 2.2(a) and 2.2(b) are effective on + the date Contributor first distributes or otherwise makes the + Modifications available to a third party. + + (d) Notwithstanding Section 2.2(b) above, no patent license is granted: + (1) for any code that Contributor has deleted from the Contributor + Version; (2) for infringements caused by: (i) third party modifications + of Contributor Version, or (ii) the combination of Modifications made by + that Contributor with other software (except as part of the Contributor + Version) or other devices; or (3) under Patent Claims infringed by + Covered Software in the absence of Modifications made by that + Contributor. + +3. Distribution Obligations. + + 3.1. Availability of Source Code. + + Any Covered Software that You distribute or otherwise make available in + Executable form must also be made available in Source Code form and that + Source Code form must be distributed only under the terms of this + License. You must include a copy of this License with every copy of the + Source Code form of the Covered Software You distribute or otherwise + make available. You must inform recipients of any such Covered Software + in Executable form as to how they can obtain such Covered Software in + Source Code form in a reasonable manner on or through a medium + customarily used for software exchange. + + 3.2. Modifications. + + The Modifications that You create or to which You contribute are + governed by the terms of this License. You represent that You believe + Your Modifications are Your original creation(s) and/or You have + sufficient rights to grant the rights conveyed by this License. + + 3.3. Required Notices. + + You must include a notice in each of Your Modifications that identifies + You as the Contributor of the Modification. You may not remove or alter + any copyright, patent or trademark notices contained within the Covered + Software, or any notices of licensing or any descriptive text giving + attribution to any Contributor or the Initial Developer. + + 3.4. Application of Additional Terms. + + You may not offer or impose any terms on any Covered Software in Source + Code form that alters or restricts the applicable version of this + License or the recipients' rights hereunder. You may choose to offer, + and to charge a fee for, warranty, support, indemnity or liability + obligations to one or more recipients of Covered Software. However, you + may do so only on Your own behalf, and not on behalf of the Initial + Developer or any Contributor. You must make it absolutely clear that any + such warranty, support, indemnity or liability obligation is offered by + You alone, and You hereby agree to indemnify the Initial Developer and + every Contributor for any liability incurred by the Initial Developer or + such Contributor as a result of warranty, support, indemnity or + liability terms You offer. + + 3.5. Distribution of Executable Versions. + + You may distribute the Executable form of the Covered Software under the + terms of this License or under the terms of a license of Your choice, + which may contain terms different from this License, provided that You + are in compliance with the terms of this License and that the license + for the Executable form does not attempt to limit or alter the + recipient's rights in the Source Code form from the rights set forth in + this License. If You distribute the Covered Software in Executable form + under a different license, You must make it absolutely clear that any + terms which differ from this License are offered by You alone, not by + the Initial Developer or Contributor. You hereby agree to indemnify the + Initial Developer and every Contributor for any liability incurred by + the Initial Developer or such Contributor as a result of any such terms + You offer. + + 3.6. Larger Works. + + You may create a Larger Work by combining Covered Software with other + code not governed by the terms of this License and distribute the Larger + Work as a single product. In such a case, You must make sure the + requirements of this License are fulfilled for the Covered Software. + +4. Versions of the License. + + 4.1. New Versions. + + Oracle is the initial license steward and may publish revised and/or new + versions of this License from time to time. Each version will be given a + distinguishing version number. Except as provided in Section 4.3, no one + other than the license steward has the right to modify this License. + + 4.2. Effect of New Versions. + + You may always continue to use, distribute or otherwise make the Covered + Software available under the terms of the version of the License under + which You originally received the Covered Software. If the Initial + Developer includes a notice in the Original Software prohibiting it from + being distributed or otherwise made available under any subsequent + version of the License, You must distribute and make the Covered + Software available under the terms of the version of the License under + which You originally received the Covered Software. Otherwise, You may + also choose to use, distribute or otherwise make the Covered Software + available under the terms of any subsequent version of the License + published by the license steward. + + 4.3. Modified Versions. + + When You are an Initial Developer and You want to create a new license + for Your Original Software, You may create and use a modified version of + this License if You: (a) rename the license and remove any references to + the name of the license steward (except to note that the license differs + from this License); and (b) otherwise make it clear that the license + contains terms which differ from this License. + +5. DISCLAIMER OF WARRANTY. + + COVERED SOFTWARE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, + WITHOUT LIMITATION, WARRANTIES THAT THE COVERED SOFTWARE IS FREE OF + DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING. + THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED + SOFTWARE IS WITH YOU. SHOULD ANY COVERED SOFTWARE PROVE DEFECTIVE IN ANY + RESPECT, YOU (NOT THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME + THE COST OF ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS + DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO + USE OF ANY COVERED SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS + DISCLAIMER. + +6. TERMINATION. + + 6.1. This License and the rights granted hereunder will terminate + automatically if You fail to comply with terms herein and fail to cure + such breach within 30 days of becoming aware of the breach. Provisions + which, by their nature, must remain in effect beyond the termination of + this License shall survive. + + 6.2. If You assert a patent infringement claim (excluding declaratory + judgment actions) against Initial Developer or a Contributor (the + Initial Developer or Contributor against whom You assert such claim is + referred to as "Participant") alleging that the Participant Software + (meaning the Contributor Version where the Participant is a Contributor + or the Original Software where the Participant is the Initial Developer) + directly or indirectly infringes any patent, then any and all rights + granted directly or indirectly to You by such Participant, the Initial + Developer (if the Initial Developer is not the Participant) and all + Contributors under Sections 2.1 and/or 2.2 of this License shall, upon + 60 days notice from Participant terminate prospectively and + automatically at the expiration of such 60 day notice period, unless if + within such 60 day period You withdraw Your claim with respect to the + Participant Software against such Participant either unilaterally or + pursuant to a written agreement with Participant. + + 6.3. If You assert a patent infringement claim against Participant + alleging that the Participant Software directly or indirectly infringes + any patent where such claim is resolved (such as by license or + settlement) prior to the initiation of patent infringement litigation, + then the reasonable value of the licenses granted by such Participant + under Sections 2.1 or 2.2 shall be taken into account in determining the + amount or value of any payment or license. + + 6.4. In the event of termination under Sections 6.1 or 6.2 above, all + end user licenses that have been validly granted by You or any + distributor hereunder prior to termination (excluding licenses granted + to You by any distributor) shall survive termination. + +7. LIMITATION OF LIABILITY. + + UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT + (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE INITIAL + DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF COVERED + SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO ANY + PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF + GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL + OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN + INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF + LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY + RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT APPLICABLE LAW + PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION + OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THIS EXCLUSION + AND LIMITATION MAY NOT APPLY TO YOU. + +8. U.S. GOVERNMENT END USERS. + + The Covered Software is a "commercial item," as that term is defined in + 48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial computer + software" (as that term is defined at 48 C.F.R. § 252.227-7014(a)(1)) + and "commercial computer software documentation" as such terms are used + in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48 C.F.R. 12.212 and + 48 C.F.R. 227.7202-1 through 227.7202-4 (June 1995), all U.S. Government + End Users acquire Covered Software with only those rights set forth + herein. This U.S. Government Rights clause is in lieu of, and + supersedes, any other FAR, DFAR, or other clause or provision that + addresses Government rights in computer software under this License. + +9. MISCELLANEOUS. + + This License represents the complete agreement concerning subject matter + hereof. If any provision of this License is held to be unenforceable, + such provision shall be reformed only to the extent necessary to make it + enforceable. This License shall be governed by the law of the + jurisdiction specified in a notice contained within the Original + Software (except to the extent applicable law, if any, provides + otherwise), excluding such jurisdiction's conflict-of-law provisions. + Any litigation relating to this License shall be subject to the + jurisdiction of the courts located in the jurisdiction and venue + specified in a notice contained within the Original Software, with the + losing party responsible for costs, including, without limitation, court + costs and reasonable attorneys' fees and expenses. The application of + the United Nations Convention on Contracts for the International Sale of + Goods is expressly excluded. Any law or regulation which provides that + the language of a contract shall be construed against the drafter shall + not apply to this License. You agree that You alone are responsible for + compliance with the United States export administration regulations (and + the export control laws and regulation of any other countries) when You + use, distribute or otherwise make available any Covered Software. + +10. RESPONSIBILITY FOR CLAIMS. + + As between Initial Developer and the Contributors, each party is + responsible for claims and damages arising, directly or indirectly, out + of its utilization of rights under this License and You agree to work + with Initial Developer and Contributors to distribute such + responsibility on an equitable basis. Nothing herein is intended or + shall be deemed to constitute any admission of liability. + +NOTICE PURSUANT TO SECTION 9 OF THE COMMON DEVELOPMENT AND DISTRIBUTION +LICENSE (CDDL) + +The code released under the CDDL shall be governed by the laws of the +State of California (excluding conflict-of-law provisions). Any +litigation relating to this License shall be subject to the jurisdiction +of the Federal Courts of the Northern District of California and the +state courts of the State of California, with venue lying in Santa Clara +County, California. + + + + +The GNU General Public License (GPL) Version 2, June 1991 + +Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place, +Suite 330, Boston, MA 02111-1307 USA + +Everyone is permitted to copy and distribute verbatim copies of this +license document, but changing it is not allowed. + +Preamble + +The licenses for most software are designed to take away your freedom to +share and change it. By contrast, the GNU General Public License is +intended to guarantee your freedom to share and change free software--to +make sure the software is free for all its users. This General Public +License applies to most of the Free Software Foundation's software and +to any other program whose authors commit to using it. (Some other Free +Software Foundation software is covered by the GNU Library General +Public License instead.) You can apply it to your programs, too. + +When we speak of free software, we are referring to freedom, not price. +Our General Public Licenses are designed to make sure that you have the +freedom to distribute copies of free software (and charge for this +service if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs; and that you know you can do these things. + +To protect your rights, we need to make restrictions that forbid anyone +to deny you these rights or to ask you to surrender the rights. These +restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + +For example, if you distribute copies of such a program, whether gratis +or for a fee, you must give the recipients all the rights that you have. +You must make sure that they, too, receive or can get the source code. +And you must show them these terms so they know their rights. + +We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + +Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + +Finally, any free program is threatened constantly by software patents. +We wish to avoid the danger that redistributors of a free program will +individually obtain patent licenses, in effect making the program +proprietary. To prevent this, we have made it clear that any patent must +be licensed for everyone's free use or not licensed at all. + +The precise terms and conditions for copying, distribution and +modification follow. + +TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + +0. This License applies to any program or other work which contains a +notice placed by the copyright holder saying it may be distributed under +the terms of this General Public License. The "Program", below, refers +to any such program or work, and a "work based on the Program" means +either the Program or any derivative work under copyright law: that is +to say, a work containing the Program or a portion of it, either +verbatim or with modifications and/or translated into another language. +(Hereinafter, translation is included without limitation in the term +"modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of running +the Program is not restricted, and the output from the Program is +covered only if its contents constitute a work based on the Program +(independent of having been made by running the Program). Whether that +is true depends on what the Program does. + +1. You may copy and distribute verbatim copies of the Program's source +code as you receive it, in any medium, provided that you conspicuously +and appropriately publish on each copy an appropriate copyright notice +and disclaimer of warranty; keep intact all the notices that refer to +this License and to the absence of any warranty; and give any other +recipients of the Program a copy of this License along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + +2. You may modify your copy or copies of the Program or any portion of +it, thus forming a work based on the Program, and copy and distribute +such modifications or work under the terms of Section 1 above, provided +that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices stating + that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in whole + or in part contains or is derived from the Program or any part thereof, + to be licensed as a whole at no charge to all third parties under the + terms of this License. + + c) If the modified program normally reads commands interactively when + run, you must cause it, when started running for such interactive use in + the most ordinary way, to print or display an announcement including an + appropriate copyright notice and a notice that there is no warranty (or + else, saying that you provide a warranty) and that users may + redistribute the program under these conditions, and telling the user + how to view a copy of this License. (Exception: if the Program itself is + interactive but does not normally print such an announcement, your work + based on the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, and +can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based on +the Program, the distribution of the whole must be on the terms of this +License, whose permissions for other licensees extend to the entire +whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of a +storage or distribution medium does not bring the other work under the +scope of this License. + +3. You may copy and distribute the Program (or a work based on it, under +Section 2) in object code or executable form under the terms of Sections +1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable source + code, which must be distributed under the terms of Sections 1 and 2 + above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three years, to + give any third party, for a charge no more than your cost of physically + performing source distribution, a complete machine-readable copy of the + corresponding source code, to be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer to + distribute corresponding source code. (This alternative is allowed only + for noncommercial distribution and only if you received the program in + object code or executable form with such an offer, in accord with + Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source code +means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to control +compilation and installation of the executable. However, as a special +exception, the source code distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies the +executable. + +If distribution of executable or object code is made by offering access +to copy from a designated place, then offering equivalent access to copy +the source code from the same place counts as distribution of the source +code, even though third parties are not compelled to copy the source +along with the object code. + +4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt otherwise +to copy, modify, sublicense or distribute the Program is void, and will +automatically terminate your rights under this License. However, parties +who have received copies, or rights, from you under this License will +not have their licenses terminated so long as such parties remain in +full compliance. + +5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and all +its terms and conditions for copying, distributing or modifying the +Program or works based on it. + +6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further restrictions +on the recipients' exercise of the rights granted herein. You are not +responsible for enforcing compliance by third parties to this License. + +7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot distribute +so as to satisfy simultaneously your obligations under this License and +any other pertinent obligations, then as a consequence you may not +distribute the Program at all. For example, if a patent license would +not permit royalty-free redistribution of the Program by all those who +receive copies directly or indirectly through you, then the only way you +could satisfy both it and this License would be to refrain entirely from +distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is implemented +by public license practices. Many people have made generous +contributions to the wide range of software distributed through that +system in reliance on consistent application of that system; it is up to +the author/donor to decide if he or she is willing to distribute +software through any other system and a licensee cannot impose that +choice. + +This section is intended to make thoroughly clear what is believed to be +a consequence of the rest of this License. + +8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License may +add an explicit geographical distribution limitation excluding those +countries, so that distribution is permitted only in or among countries +not thus excluded. In such case, this License incorporates the +limitation as if written in the body of this License. + +9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Program does not specify a version +number of this License, you may choose any version ever published by the +Free Software Foundation. + +10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the +author to ask for permission. For software which is copyrighted by the +Free Software Foundation, write to the Free Software Foundation; we +sometimes make exceptions for this. Our decision will be guided by the +two goals of preserving the free status of all derivatives of our free +software and of promoting the sharing and reuse of software generally. + +NO WARRANTY + +11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER +EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE +ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH +YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL +NECESSARY SERVICING, REPAIR OR CORRECTION. + +12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR +DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL +DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM +(INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED +INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF +THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR +OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +END OF TERMS AND CONDITIONS + +How to Apply These Terms to Your New Programs + +If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these +terms. + +To do so, attach the following notices to the program. It is safest to +attach them to the start of each source file to most effectively convey +the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + One line to give the program's name and a brief idea of what it does. + Copyright (C) <year> <name of author> + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General + Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author Gnomovision + comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is + free software, and you are welcome to redistribute it under certain + conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the +appropriate parts of the General Public License. Of course, the commands +you use may be called something other than `show w' and `show c'; they +could even be mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + signature of Ty Coon, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications +with the library. If this is what you want to do, use the GNU Library +General Public License instead of this License. + +# + +"CLASSPATH" EXCEPTION TO THE GPL VERSION 2 + +Certain source files distributed by Oracle are subject to the following +clarification and special exception to the GPL Version 2, but only where +Oracle has expressly included in the particular source file's header the +words "Oracle designates this particular file as subject to the +"Classpath" exception as provided by Oracle in the License file that +accompanied this code." + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License Version 2 cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under terms +of your choice, provided that you also meet, for each linked independent +module, the terms and conditions of the license of that module. An +independent module is a module which is not derived from or based on +this library. If you modify this library, you may extend this exception +to your version of the library, but you are not obligated to do so. If +you do not wish to do so, delete this exception statement from your +version. diff --git a/lib/jersey-core-1.11-sources.jar b/lib/jersey-core-1.11-sources.jar Binary files differnew file mode 100644 index 0000000..b685f2c --- /dev/null +++ b/lib/jersey-core-1.11-sources.jar diff --git a/lib/jersey-core-1.11.jar b/lib/jersey-core-1.11.jar Binary files differnew file mode 100644 index 0000000..d19f7ae --- /dev/null +++ b/lib/jersey-core-1.11.jar diff --git a/lib/jersey-core-1.11.jar.txt b/lib/jersey-core-1.11.jar.txt new file mode 100644 index 0000000..8681d13 --- /dev/null +++ b/lib/jersey-core-1.11.jar.txt @@ -0,0 +1,712 @@ +COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.1 + +1. Definitions. + + 1.1. "Contributor" means each individual or entity that creates or + contributes to the creation of Modifications. + + 1.2. "Contributor Version" means the combination of the Original + Software, prior Modifications used by a Contributor (if any), and the + Modifications made by that particular Contributor. + + 1.3. "Covered Software" means (a) the Original Software, or (b) + Modifications, or (c) the combination of files containing Original + Software with files containing Modifications, in each case including + portions thereof. + + 1.4. "Executable" means the Covered Software in any form other than + Source Code. + + 1.5. "Initial Developer" means the individual or entity that first makes + Original Software available under this License. + + 1.6. "Larger Work" means a work which combines Covered Software or + portions thereof with code not governed by the terms of this License. + + 1.7. "License" means this document. + + 1.8. "Licensable" means having the right to grant, to the maximum extent + possible, whether at the time of the initial grant or subsequently + acquired, any and all of the rights conveyed herein. + + 1.9. "Modifications" means the Source Code and Executable form of any of + the following: + + A. Any file that results from an addition to, deletion from or + modification of the contents of a file containing Original Software or + previous Modifications; + + B. Any new file that contains any part of the Original Software or + previous Modification; or + + C. Any new file that is contributed or otherwise made available under + the terms of this License. + + 1.10. "Original Software" means the Source Code and Executable form of + computer software code that is originally released under this License. + + 1.11. "Patent Claims" means any patent claim(s), now owned or hereafter + acquired, including without limitation, method, process, and apparatus + claims, in any patent Licensable by grantor. + + 1.12. "Source Code" means (a) the common form of computer software code + in which modifications are made and (b) associated documentation + included in or with such code. + + 1.13. "You" (or "Your") means an individual or a legal entity exercising + rights under, and complying with all of the terms of, this License. For + legal entities, "You" includes any entity which controls, is controlled + by, or is under common control with You. For purposes of this + definition, "control" means (a) the power, direct or indirect, to cause + the direction or management of such entity, whether by contract or + otherwise, or (b) ownership of more than fifty percent (50%) of the + outstanding shares or beneficial ownership of such entity. + +2. License Grants. + + 2.1. The Initial Developer Grant. + + Conditioned upon Your compliance with Section 3.1 below and subject to + third party intellectual property claims, the Initial Developer hereby + grants You a world-wide, royalty-free, non-exclusive license: + + (a) under intellectual property rights (other than patent or trademark) + Licensable by Initial Developer, to use, reproduce, modify, display, + perform, sublicense and distribute the Original Software (or portions + thereof), with or without Modifications, and/or as part of a Larger + Work; and + + (b) under Patent Claims infringed by the making, using or selling of + Original Software, to make, have made, use, practice, sell, and offer + for sale, and/or otherwise dispose of the Original Software (or portions + thereof). + + (c) The licenses granted in Sections 2.1(a) and (b) are effective on the + date Initial Developer first distributes or otherwise makes the Original + Software available to a third party under the terms of this License. + + (d) Notwithstanding Section 2.1(b) above, no patent license is granted: + (1) for code that You delete from the Original Software, or (2) for + infringements caused by: (i) the modification of the Original Software, + or (ii) the combination of the Original Software with other software or + devices. + + 2.2. Contributor Grant. + + Conditioned upon Your compliance with Section 3.1 below and subject to + third party intellectual property claims, each Contributor hereby grants + You a world-wide, royalty-free, non-exclusive license: + + (a) under intellectual property rights (other than patent or trademark) + Licensable by Contributor to use, reproduce, modify, display, perform, + sublicense and distribute the Modifications created by such Contributor + (or portions thereof), either on an unmodified basis, with other + Modifications, as Covered Software and/or as part of a Larger Work; and + + (b) under Patent Claims infringed by the making, using, or selling of + Modifications made by that Contributor either alone and/or in + combination with its Contributor Version (or portions of such + combination), to make, use, sell, offer for sale, have made, and/or + otherwise dispose of: (1) Modifications made by that Contributor (or + portions thereof); and (2) the combination of Modifications made by that + Contributor with its Contributor Version (or portions of such + combination). + + (c) The licenses granted in Sections 2.2(a) and 2.2(b) are effective on + the date Contributor first distributes or otherwise makes the + Modifications available to a third party. + + (d) Notwithstanding Section 2.2(b) above, no patent license is granted: + (1) for any code that Contributor has deleted from the Contributor + Version; (2) for infringements caused by: (i) third party modifications + of Contributor Version, or (ii) the combination of Modifications made by + that Contributor with other software (except as part of the Contributor + Version) or other devices; or (3) under Patent Claims infringed by + Covered Software in the absence of Modifications made by that + Contributor. + +3. Distribution Obligations. + + 3.1. Availability of Source Code. + + Any Covered Software that You distribute or otherwise make available in + Executable form must also be made available in Source Code form and that + Source Code form must be distributed only under the terms of this + License. You must include a copy of this License with every copy of the + Source Code form of the Covered Software You distribute or otherwise + make available. You must inform recipients of any such Covered Software + in Executable form as to how they can obtain such Covered Software in + Source Code form in a reasonable manner on or through a medium + customarily used for software exchange. + + 3.2. Modifications. + + The Modifications that You create or to which You contribute are + governed by the terms of this License. You represent that You believe + Your Modifications are Your original creation(s) and/or You have + sufficient rights to grant the rights conveyed by this License. + + 3.3. Required Notices. + + You must include a notice in each of Your Modifications that identifies + You as the Contributor of the Modification. You may not remove or alter + any copyright, patent or trademark notices contained within the Covered + Software, or any notices of licensing or any descriptive text giving + attribution to any Contributor or the Initial Developer. + + 3.4. Application of Additional Terms. + + You may not offer or impose any terms on any Covered Software in Source + Code form that alters or restricts the applicable version of this + License or the recipients' rights hereunder. You may choose to offer, + and to charge a fee for, warranty, support, indemnity or liability + obligations to one or more recipients of Covered Software. However, you + may do so only on Your own behalf, and not on behalf of the Initial + Developer or any Contributor. You must make it absolutely clear that any + such warranty, support, indemnity or liability obligation is offered by + You alone, and You hereby agree to indemnify the Initial Developer and + every Contributor for any liability incurred by the Initial Developer or + such Contributor as a result of warranty, support, indemnity or + liability terms You offer. + + 3.5. Distribution of Executable Versions. + + You may distribute the Executable form of the Covered Software under the + terms of this License or under the terms of a license of Your choice, + which may contain terms different from this License, provided that You + are in compliance with the terms of this License and that the license + for the Executable form does not attempt to limit or alter the + recipient's rights in the Source Code form from the rights set forth in + this License. If You distribute the Covered Software in Executable form + under a different license, You must make it absolutely clear that any + terms which differ from this License are offered by You alone, not by + the Initial Developer or Contributor. You hereby agree to indemnify the + Initial Developer and every Contributor for any liability incurred by + the Initial Developer or such Contributor as a result of any such terms + You offer. + + 3.6. Larger Works. + + You may create a Larger Work by combining Covered Software with other + code not governed by the terms of this License and distribute the Larger + Work as a single product. In such a case, You must make sure the + requirements of this License are fulfilled for the Covered Software. + +4. Versions of the License. + + 4.1. New Versions. + + Oracle is the initial license steward and may publish revised and/or new + versions of this License from time to time. Each version will be given a + distinguishing version number. Except as provided in Section 4.3, no one + other than the license steward has the right to modify this License. + + 4.2. Effect of New Versions. + + You may always continue to use, distribute or otherwise make the Covered + Software available under the terms of the version of the License under + which You originally received the Covered Software. If the Initial + Developer includes a notice in the Original Software prohibiting it from + being distributed or otherwise made available under any subsequent + version of the License, You must distribute and make the Covered + Software available under the terms of the version of the License under + which You originally received the Covered Software. Otherwise, You may + also choose to use, distribute or otherwise make the Covered Software + available under the terms of any subsequent version of the License + published by the license steward. + + 4.3. Modified Versions. + + When You are an Initial Developer and You want to create a new license + for Your Original Software, You may create and use a modified version of + this License if You: (a) rename the license and remove any references to + the name of the license steward (except to note that the license differs + from this License); and (b) otherwise make it clear that the license + contains terms which differ from this License. + +5. DISCLAIMER OF WARRANTY. + + COVERED SOFTWARE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, + WITHOUT LIMITATION, WARRANTIES THAT THE COVERED SOFTWARE IS FREE OF + DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING. + THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED + SOFTWARE IS WITH YOU. SHOULD ANY COVERED SOFTWARE PROVE DEFECTIVE IN ANY + RESPECT, YOU (NOT THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME + THE COST OF ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS + DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO + USE OF ANY COVERED SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS + DISCLAIMER. + +6. TERMINATION. + + 6.1. This License and the rights granted hereunder will terminate + automatically if You fail to comply with terms herein and fail to cure + such breach within 30 days of becoming aware of the breach. Provisions + which, by their nature, must remain in effect beyond the termination of + this License shall survive. + + 6.2. If You assert a patent infringement claim (excluding declaratory + judgment actions) against Initial Developer or a Contributor (the + Initial Developer or Contributor against whom You assert such claim is + referred to as "Participant") alleging that the Participant Software + (meaning the Contributor Version where the Participant is a Contributor + or the Original Software where the Participant is the Initial Developer) + directly or indirectly infringes any patent, then any and all rights + granted directly or indirectly to You by such Participant, the Initial + Developer (if the Initial Developer is not the Participant) and all + Contributors under Sections 2.1 and/or 2.2 of this License shall, upon + 60 days notice from Participant terminate prospectively and + automatically at the expiration of such 60 day notice period, unless if + within such 60 day period You withdraw Your claim with respect to the + Participant Software against such Participant either unilaterally or + pursuant to a written agreement with Participant. + + 6.3. If You assert a patent infringement claim against Participant + alleging that the Participant Software directly or indirectly infringes + any patent where such claim is resolved (such as by license or + settlement) prior to the initiation of patent infringement litigation, + then the reasonable value of the licenses granted by such Participant + under Sections 2.1 or 2.2 shall be taken into account in determining the + amount or value of any payment or license. + + 6.4. In the event of termination under Sections 6.1 or 6.2 above, all + end user licenses that have been validly granted by You or any + distributor hereunder prior to termination (excluding licenses granted + to You by any distributor) shall survive termination. + +7. LIMITATION OF LIABILITY. + + UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT + (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE INITIAL + DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF COVERED + SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO ANY + PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF + GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL + OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN + INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF + LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY + RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT APPLICABLE LAW + PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION + OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THIS EXCLUSION + AND LIMITATION MAY NOT APPLY TO YOU. + +8. U.S. GOVERNMENT END USERS. + + The Covered Software is a "commercial item," as that term is defined in + 48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial computer + software" (as that term is defined at 48 C.F.R. § 252.227-7014(a)(1)) + and "commercial computer software documentation" as such terms are used + in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48 C.F.R. 12.212 and + 48 C.F.R. 227.7202-1 through 227.7202-4 (June 1995), all U.S. Government + End Users acquire Covered Software with only those rights set forth + herein. This U.S. Government Rights clause is in lieu of, and + supersedes, any other FAR, DFAR, or other clause or provision that + addresses Government rights in computer software under this License. + +9. MISCELLANEOUS. + + This License represents the complete agreement concerning subject matter + hereof. If any provision of this License is held to be unenforceable, + such provision shall be reformed only to the extent necessary to make it + enforceable. This License shall be governed by the law of the + jurisdiction specified in a notice contained within the Original + Software (except to the extent applicable law, if any, provides + otherwise), excluding such jurisdiction's conflict-of-law provisions. + Any litigation relating to this License shall be subject to the + jurisdiction of the courts located in the jurisdiction and venue + specified in a notice contained within the Original Software, with the + losing party responsible for costs, including, without limitation, court + costs and reasonable attorneys' fees and expenses. The application of + the United Nations Convention on Contracts for the International Sale of + Goods is expressly excluded. Any law or regulation which provides that + the language of a contract shall be construed against the drafter shall + not apply to this License. You agree that You alone are responsible for + compliance with the United States export administration regulations (and + the export control laws and regulation of any other countries) when You + use, distribute or otherwise make available any Covered Software. + +10. RESPONSIBILITY FOR CLAIMS. + + As between Initial Developer and the Contributors, each party is + responsible for claims and damages arising, directly or indirectly, out + of its utilization of rights under this License and You agree to work + with Initial Developer and Contributors to distribute such + responsibility on an equitable basis. Nothing herein is intended or + shall be deemed to constitute any admission of liability. + +NOTICE PURSUANT TO SECTION 9 OF THE COMMON DEVELOPMENT AND DISTRIBUTION +LICENSE (CDDL) + +The code released under the CDDL shall be governed by the laws of the +State of California (excluding conflict-of-law provisions). Any +litigation relating to this License shall be subject to the jurisdiction +of the Federal Courts of the Northern District of California and the +state courts of the State of California, with venue lying in Santa Clara +County, California. + + + + +The GNU General Public License (GPL) Version 2, June 1991 + +Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place, +Suite 330, Boston, MA 02111-1307 USA + +Everyone is permitted to copy and distribute verbatim copies of this +license document, but changing it is not allowed. + +Preamble + +The licenses for most software are designed to take away your freedom to +share and change it. By contrast, the GNU General Public License is +intended to guarantee your freedom to share and change free software--to +make sure the software is free for all its users. This General Public +License applies to most of the Free Software Foundation's software and +to any other program whose authors commit to using it. (Some other Free +Software Foundation software is covered by the GNU Library General +Public License instead.) You can apply it to your programs, too. + +When we speak of free software, we are referring to freedom, not price. +Our General Public Licenses are designed to make sure that you have the +freedom to distribute copies of free software (and charge for this +service if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs; and that you know you can do these things. + +To protect your rights, we need to make restrictions that forbid anyone +to deny you these rights or to ask you to surrender the rights. These +restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + +For example, if you distribute copies of such a program, whether gratis +or for a fee, you must give the recipients all the rights that you have. +You must make sure that they, too, receive or can get the source code. +And you must show them these terms so they know their rights. + +We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + +Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + +Finally, any free program is threatened constantly by software patents. +We wish to avoid the danger that redistributors of a free program will +individually obtain patent licenses, in effect making the program +proprietary. To prevent this, we have made it clear that any patent must +be licensed for everyone's free use or not licensed at all. + +The precise terms and conditions for copying, distribution and +modification follow. + +TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + +0. This License applies to any program or other work which contains a +notice placed by the copyright holder saying it may be distributed under +the terms of this General Public License. The "Program", below, refers +to any such program or work, and a "work based on the Program" means +either the Program or any derivative work under copyright law: that is +to say, a work containing the Program or a portion of it, either +verbatim or with modifications and/or translated into another language. +(Hereinafter, translation is included without limitation in the term +"modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of running +the Program is not restricted, and the output from the Program is +covered only if its contents constitute a work based on the Program +(independent of having been made by running the Program). Whether that +is true depends on what the Program does. + +1. You may copy and distribute verbatim copies of the Program's source +code as you receive it, in any medium, provided that you conspicuously +and appropriately publish on each copy an appropriate copyright notice +and disclaimer of warranty; keep intact all the notices that refer to +this License and to the absence of any warranty; and give any other +recipients of the Program a copy of this License along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + +2. You may modify your copy or copies of the Program or any portion of +it, thus forming a work based on the Program, and copy and distribute +such modifications or work under the terms of Section 1 above, provided +that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices stating + that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in whole + or in part contains or is derived from the Program or any part thereof, + to be licensed as a whole at no charge to all third parties under the + terms of this License. + + c) If the modified program normally reads commands interactively when + run, you must cause it, when started running for such interactive use in + the most ordinary way, to print or display an announcement including an + appropriate copyright notice and a notice that there is no warranty (or + else, saying that you provide a warranty) and that users may + redistribute the program under these conditions, and telling the user + how to view a copy of this License. (Exception: if the Program itself is + interactive but does not normally print such an announcement, your work + based on the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, and +can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based on +the Program, the distribution of the whole must be on the terms of this +License, whose permissions for other licensees extend to the entire +whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of a +storage or distribution medium does not bring the other work under the +scope of this License. + +3. You may copy and distribute the Program (or a work based on it, under +Section 2) in object code or executable form under the terms of Sections +1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable source + code, which must be distributed under the terms of Sections 1 and 2 + above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three years, to + give any third party, for a charge no more than your cost of physically + performing source distribution, a complete machine-readable copy of the + corresponding source code, to be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer to + distribute corresponding source code. (This alternative is allowed only + for noncommercial distribution and only if you received the program in + object code or executable form with such an offer, in accord with + Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source code +means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to control +compilation and installation of the executable. However, as a special +exception, the source code distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies the +executable. + +If distribution of executable or object code is made by offering access +to copy from a designated place, then offering equivalent access to copy +the source code from the same place counts as distribution of the source +code, even though third parties are not compelled to copy the source +along with the object code. + +4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt otherwise +to copy, modify, sublicense or distribute the Program is void, and will +automatically terminate your rights under this License. However, parties +who have received copies, or rights, from you under this License will +not have their licenses terminated so long as such parties remain in +full compliance. + +5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and all +its terms and conditions for copying, distributing or modifying the +Program or works based on it. + +6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further restrictions +on the recipients' exercise of the rights granted herein. You are not +responsible for enforcing compliance by third parties to this License. + +7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot distribute +so as to satisfy simultaneously your obligations under this License and +any other pertinent obligations, then as a consequence you may not +distribute the Program at all. For example, if a patent license would +not permit royalty-free redistribution of the Program by all those who +receive copies directly or indirectly through you, then the only way you +could satisfy both it and this License would be to refrain entirely from +distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is implemented +by public license practices. Many people have made generous +contributions to the wide range of software distributed through that +system in reliance on consistent application of that system; it is up to +the author/donor to decide if he or she is willing to distribute +software through any other system and a licensee cannot impose that +choice. + +This section is intended to make thoroughly clear what is believed to be +a consequence of the rest of this License. + +8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License may +add an explicit geographical distribution limitation excluding those +countries, so that distribution is permitted only in or among countries +not thus excluded. In such case, this License incorporates the +limitation as if written in the body of this License. + +9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Program does not specify a version +number of this License, you may choose any version ever published by the +Free Software Foundation. + +10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the +author to ask for permission. For software which is copyrighted by the +Free Software Foundation, write to the Free Software Foundation; we +sometimes make exceptions for this. Our decision will be guided by the +two goals of preserving the free status of all derivatives of our free +software and of promoting the sharing and reuse of software generally. + +NO WARRANTY + +11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER +EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE +ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH +YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL +NECESSARY SERVICING, REPAIR OR CORRECTION. + +12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR +DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL +DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM +(INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED +INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF +THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR +OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +END OF TERMS AND CONDITIONS + +How to Apply These Terms to Your New Programs + +If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these +terms. + +To do so, attach the following notices to the program. It is safest to +attach them to the start of each source file to most effectively convey +the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + One line to give the program's name and a brief idea of what it does. + Copyright (C) <year> <name of author> + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General + Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author Gnomovision + comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is + free software, and you are welcome to redistribute it under certain + conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the +appropriate parts of the General Public License. Of course, the commands +you use may be called something other than `show w' and `show c'; they +could even be mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + signature of Ty Coon, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications +with the library. If this is what you want to do, use the GNU Library +General Public License instead of this License. + +# + +"CLASSPATH" EXCEPTION TO THE GPL VERSION 2 + +Certain source files distributed by Oracle are subject to the following +clarification and special exception to the GPL Version 2, but only where +Oracle has expressly included in the particular source file's header the +words "Oracle designates this particular file as subject to the +"Classpath" exception as provided by Oracle in the License file that +accompanied this code." + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License Version 2 cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under terms +of your choice, provided that you also meet, for each linked independent +module, the terms and conditions of the license of that module. An +independent module is a module which is not derived from or based on +this library. If you modify this library, you may extend this exception +to your version of the library, but you are not obligated to do so. If +you do not wish to do so, delete this exception statement from your +version. diff --git a/lib/joda-time-2.1-sources.jar b/lib/joda-time-2.1-sources.jar Binary files differnew file mode 100644 index 0000000..44e4ed8 --- /dev/null +++ b/lib/joda-time-2.1-sources.jar diff --git a/lib/joda-time-2.1.jar b/lib/joda-time-2.1.jar Binary files differnew file mode 100644 index 0000000..b2aca95 --- /dev/null +++ b/lib/joda-time-2.1.jar diff --git a/lib/joda-time-2.1.jar.txt b/lib/joda-time-2.1.jar.txt new file mode 100644 index 0000000..230f1d4 --- /dev/null +++ b/lib/joda-time-2.1.jar.txt @@ -0,0 +1,202 @@ +
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright 2008-2011 Google Inc.
+
+ 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.
diff --git a/lib/jsr311-api-1.1.1-sources.jar b/lib/jsr311-api-1.1.1-sources.jar Binary files differnew file mode 100644 index 0000000..2cd5eb8 --- /dev/null +++ b/lib/jsr311-api-1.1.1-sources.jar diff --git a/lib/jsr311-api-1.1.1.jar b/lib/jsr311-api-1.1.1.jar Binary files differnew file mode 100644 index 0000000..ec8bc81 --- /dev/null +++ b/lib/jsr311-api-1.1.1.jar diff --git a/lib/jsr311-api-1.1.1.jar.txt b/lib/jsr311-api-1.1.1.jar.txt new file mode 100644 index 0000000..8681d13 --- /dev/null +++ b/lib/jsr311-api-1.1.1.jar.txt @@ -0,0 +1,712 @@ +COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.1 + +1. Definitions. + + 1.1. "Contributor" means each individual or entity that creates or + contributes to the creation of Modifications. + + 1.2. "Contributor Version" means the combination of the Original + Software, prior Modifications used by a Contributor (if any), and the + Modifications made by that particular Contributor. + + 1.3. "Covered Software" means (a) the Original Software, or (b) + Modifications, or (c) the combination of files containing Original + Software with files containing Modifications, in each case including + portions thereof. + + 1.4. "Executable" means the Covered Software in any form other than + Source Code. + + 1.5. "Initial Developer" means the individual or entity that first makes + Original Software available under this License. + + 1.6. "Larger Work" means a work which combines Covered Software or + portions thereof with code not governed by the terms of this License. + + 1.7. "License" means this document. + + 1.8. "Licensable" means having the right to grant, to the maximum extent + possible, whether at the time of the initial grant or subsequently + acquired, any and all of the rights conveyed herein. + + 1.9. "Modifications" means the Source Code and Executable form of any of + the following: + + A. Any file that results from an addition to, deletion from or + modification of the contents of a file containing Original Software or + previous Modifications; + + B. Any new file that contains any part of the Original Software or + previous Modification; or + + C. Any new file that is contributed or otherwise made available under + the terms of this License. + + 1.10. "Original Software" means the Source Code and Executable form of + computer software code that is originally released under this License. + + 1.11. "Patent Claims" means any patent claim(s), now owned or hereafter + acquired, including without limitation, method, process, and apparatus + claims, in any patent Licensable by grantor. + + 1.12. "Source Code" means (a) the common form of computer software code + in which modifications are made and (b) associated documentation + included in or with such code. + + 1.13. "You" (or "Your") means an individual or a legal entity exercising + rights under, and complying with all of the terms of, this License. For + legal entities, "You" includes any entity which controls, is controlled + by, or is under common control with You. For purposes of this + definition, "control" means (a) the power, direct or indirect, to cause + the direction or management of such entity, whether by contract or + otherwise, or (b) ownership of more than fifty percent (50%) of the + outstanding shares or beneficial ownership of such entity. + +2. License Grants. + + 2.1. The Initial Developer Grant. + + Conditioned upon Your compliance with Section 3.1 below and subject to + third party intellectual property claims, the Initial Developer hereby + grants You a world-wide, royalty-free, non-exclusive license: + + (a) under intellectual property rights (other than patent or trademark) + Licensable by Initial Developer, to use, reproduce, modify, display, + perform, sublicense and distribute the Original Software (or portions + thereof), with or without Modifications, and/or as part of a Larger + Work; and + + (b) under Patent Claims infringed by the making, using or selling of + Original Software, to make, have made, use, practice, sell, and offer + for sale, and/or otherwise dispose of the Original Software (or portions + thereof). + + (c) The licenses granted in Sections 2.1(a) and (b) are effective on the + date Initial Developer first distributes or otherwise makes the Original + Software available to a third party under the terms of this License. + + (d) Notwithstanding Section 2.1(b) above, no patent license is granted: + (1) for code that You delete from the Original Software, or (2) for + infringements caused by: (i) the modification of the Original Software, + or (ii) the combination of the Original Software with other software or + devices. + + 2.2. Contributor Grant. + + Conditioned upon Your compliance with Section 3.1 below and subject to + third party intellectual property claims, each Contributor hereby grants + You a world-wide, royalty-free, non-exclusive license: + + (a) under intellectual property rights (other than patent or trademark) + Licensable by Contributor to use, reproduce, modify, display, perform, + sublicense and distribute the Modifications created by such Contributor + (or portions thereof), either on an unmodified basis, with other + Modifications, as Covered Software and/or as part of a Larger Work; and + + (b) under Patent Claims infringed by the making, using, or selling of + Modifications made by that Contributor either alone and/or in + combination with its Contributor Version (or portions of such + combination), to make, use, sell, offer for sale, have made, and/or + otherwise dispose of: (1) Modifications made by that Contributor (or + portions thereof); and (2) the combination of Modifications made by that + Contributor with its Contributor Version (or portions of such + combination). + + (c) The licenses granted in Sections 2.2(a) and 2.2(b) are effective on + the date Contributor first distributes or otherwise makes the + Modifications available to a third party. + + (d) Notwithstanding Section 2.2(b) above, no patent license is granted: + (1) for any code that Contributor has deleted from the Contributor + Version; (2) for infringements caused by: (i) third party modifications + of Contributor Version, or (ii) the combination of Modifications made by + that Contributor with other software (except as part of the Contributor + Version) or other devices; or (3) under Patent Claims infringed by + Covered Software in the absence of Modifications made by that + Contributor. + +3. Distribution Obligations. + + 3.1. Availability of Source Code. + + Any Covered Software that You distribute or otherwise make available in + Executable form must also be made available in Source Code form and that + Source Code form must be distributed only under the terms of this + License. You must include a copy of this License with every copy of the + Source Code form of the Covered Software You distribute or otherwise + make available. You must inform recipients of any such Covered Software + in Executable form as to how they can obtain such Covered Software in + Source Code form in a reasonable manner on or through a medium + customarily used for software exchange. + + 3.2. Modifications. + + The Modifications that You create or to which You contribute are + governed by the terms of this License. You represent that You believe + Your Modifications are Your original creation(s) and/or You have + sufficient rights to grant the rights conveyed by this License. + + 3.3. Required Notices. + + You must include a notice in each of Your Modifications that identifies + You as the Contributor of the Modification. You may not remove or alter + any copyright, patent or trademark notices contained within the Covered + Software, or any notices of licensing or any descriptive text giving + attribution to any Contributor or the Initial Developer. + + 3.4. Application of Additional Terms. + + You may not offer or impose any terms on any Covered Software in Source + Code form that alters or restricts the applicable version of this + License or the recipients' rights hereunder. You may choose to offer, + and to charge a fee for, warranty, support, indemnity or liability + obligations to one or more recipients of Covered Software. However, you + may do so only on Your own behalf, and not on behalf of the Initial + Developer or any Contributor. You must make it absolutely clear that any + such warranty, support, indemnity or liability obligation is offered by + You alone, and You hereby agree to indemnify the Initial Developer and + every Contributor for any liability incurred by the Initial Developer or + such Contributor as a result of warranty, support, indemnity or + liability terms You offer. + + 3.5. Distribution of Executable Versions. + + You may distribute the Executable form of the Covered Software under the + terms of this License or under the terms of a license of Your choice, + which may contain terms different from this License, provided that You + are in compliance with the terms of this License and that the license + for the Executable form does not attempt to limit or alter the + recipient's rights in the Source Code form from the rights set forth in + this License. If You distribute the Covered Software in Executable form + under a different license, You must make it absolutely clear that any + terms which differ from this License are offered by You alone, not by + the Initial Developer or Contributor. You hereby agree to indemnify the + Initial Developer and every Contributor for any liability incurred by + the Initial Developer or such Contributor as a result of any such terms + You offer. + + 3.6. Larger Works. + + You may create a Larger Work by combining Covered Software with other + code not governed by the terms of this License and distribute the Larger + Work as a single product. In such a case, You must make sure the + requirements of this License are fulfilled for the Covered Software. + +4. Versions of the License. + + 4.1. New Versions. + + Oracle is the initial license steward and may publish revised and/or new + versions of this License from time to time. Each version will be given a + distinguishing version number. Except as provided in Section 4.3, no one + other than the license steward has the right to modify this License. + + 4.2. Effect of New Versions. + + You may always continue to use, distribute or otherwise make the Covered + Software available under the terms of the version of the License under + which You originally received the Covered Software. If the Initial + Developer includes a notice in the Original Software prohibiting it from + being distributed or otherwise made available under any subsequent + version of the License, You must distribute and make the Covered + Software available under the terms of the version of the License under + which You originally received the Covered Software. Otherwise, You may + also choose to use, distribute or otherwise make the Covered Software + available under the terms of any subsequent version of the License + published by the license steward. + + 4.3. Modified Versions. + + When You are an Initial Developer and You want to create a new license + for Your Original Software, You may create and use a modified version of + this License if You: (a) rename the license and remove any references to + the name of the license steward (except to note that the license differs + from this License); and (b) otherwise make it clear that the license + contains terms which differ from this License. + +5. DISCLAIMER OF WARRANTY. + + COVERED SOFTWARE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, + WITHOUT LIMITATION, WARRANTIES THAT THE COVERED SOFTWARE IS FREE OF + DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING. + THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED + SOFTWARE IS WITH YOU. SHOULD ANY COVERED SOFTWARE PROVE DEFECTIVE IN ANY + RESPECT, YOU (NOT THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME + THE COST OF ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS + DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO + USE OF ANY COVERED SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS + DISCLAIMER. + +6. TERMINATION. + + 6.1. This License and the rights granted hereunder will terminate + automatically if You fail to comply with terms herein and fail to cure + such breach within 30 days of becoming aware of the breach. Provisions + which, by their nature, must remain in effect beyond the termination of + this License shall survive. + + 6.2. If You assert a patent infringement claim (excluding declaratory + judgment actions) against Initial Developer or a Contributor (the + Initial Developer or Contributor against whom You assert such claim is + referred to as "Participant") alleging that the Participant Software + (meaning the Contributor Version where the Participant is a Contributor + or the Original Software where the Participant is the Initial Developer) + directly or indirectly infringes any patent, then any and all rights + granted directly or indirectly to You by such Participant, the Initial + Developer (if the Initial Developer is not the Participant) and all + Contributors under Sections 2.1 and/or 2.2 of this License shall, upon + 60 days notice from Participant terminate prospectively and + automatically at the expiration of such 60 day notice period, unless if + within such 60 day period You withdraw Your claim with respect to the + Participant Software against such Participant either unilaterally or + pursuant to a written agreement with Participant. + + 6.3. If You assert a patent infringement claim against Participant + alleging that the Participant Software directly or indirectly infringes + any patent where such claim is resolved (such as by license or + settlement) prior to the initiation of patent infringement litigation, + then the reasonable value of the licenses granted by such Participant + under Sections 2.1 or 2.2 shall be taken into account in determining the + amount or value of any payment or license. + + 6.4. In the event of termination under Sections 6.1 or 6.2 above, all + end user licenses that have been validly granted by You or any + distributor hereunder prior to termination (excluding licenses granted + to You by any distributor) shall survive termination. + +7. LIMITATION OF LIABILITY. + + UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT + (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE INITIAL + DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF COVERED + SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO ANY + PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF + GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL + OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN + INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF + LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY + RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT APPLICABLE LAW + PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION + OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THIS EXCLUSION + AND LIMITATION MAY NOT APPLY TO YOU. + +8. U.S. GOVERNMENT END USERS. + + The Covered Software is a "commercial item," as that term is defined in + 48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial computer + software" (as that term is defined at 48 C.F.R. § 252.227-7014(a)(1)) + and "commercial computer software documentation" as such terms are used + in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48 C.F.R. 12.212 and + 48 C.F.R. 227.7202-1 through 227.7202-4 (June 1995), all U.S. Government + End Users acquire Covered Software with only those rights set forth + herein. This U.S. Government Rights clause is in lieu of, and + supersedes, any other FAR, DFAR, or other clause or provision that + addresses Government rights in computer software under this License. + +9. MISCELLANEOUS. + + This License represents the complete agreement concerning subject matter + hereof. If any provision of this License is held to be unenforceable, + such provision shall be reformed only to the extent necessary to make it + enforceable. This License shall be governed by the law of the + jurisdiction specified in a notice contained within the Original + Software (except to the extent applicable law, if any, provides + otherwise), excluding such jurisdiction's conflict-of-law provisions. + Any litigation relating to this License shall be subject to the + jurisdiction of the courts located in the jurisdiction and venue + specified in a notice contained within the Original Software, with the + losing party responsible for costs, including, without limitation, court + costs and reasonable attorneys' fees and expenses. The application of + the United Nations Convention on Contracts for the International Sale of + Goods is expressly excluded. Any law or regulation which provides that + the language of a contract shall be construed against the drafter shall + not apply to this License. You agree that You alone are responsible for + compliance with the United States export administration regulations (and + the export control laws and regulation of any other countries) when You + use, distribute or otherwise make available any Covered Software. + +10. RESPONSIBILITY FOR CLAIMS. + + As between Initial Developer and the Contributors, each party is + responsible for claims and damages arising, directly or indirectly, out + of its utilization of rights under this License and You agree to work + with Initial Developer and Contributors to distribute such + responsibility on an equitable basis. Nothing herein is intended or + shall be deemed to constitute any admission of liability. + +NOTICE PURSUANT TO SECTION 9 OF THE COMMON DEVELOPMENT AND DISTRIBUTION +LICENSE (CDDL) + +The code released under the CDDL shall be governed by the laws of the +State of California (excluding conflict-of-law provisions). Any +litigation relating to this License shall be subject to the jurisdiction +of the Federal Courts of the Northern District of California and the +state courts of the State of California, with venue lying in Santa Clara +County, California. + + + + +The GNU General Public License (GPL) Version 2, June 1991 + +Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place, +Suite 330, Boston, MA 02111-1307 USA + +Everyone is permitted to copy and distribute verbatim copies of this +license document, but changing it is not allowed. + +Preamble + +The licenses for most software are designed to take away your freedom to +share and change it. By contrast, the GNU General Public License is +intended to guarantee your freedom to share and change free software--to +make sure the software is free for all its users. This General Public +License applies to most of the Free Software Foundation's software and +to any other program whose authors commit to using it. (Some other Free +Software Foundation software is covered by the GNU Library General +Public License instead.) You can apply it to your programs, too. + +When we speak of free software, we are referring to freedom, not price. +Our General Public Licenses are designed to make sure that you have the +freedom to distribute copies of free software (and charge for this +service if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs; and that you know you can do these things. + +To protect your rights, we need to make restrictions that forbid anyone +to deny you these rights or to ask you to surrender the rights. These +restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + +For example, if you distribute copies of such a program, whether gratis +or for a fee, you must give the recipients all the rights that you have. +You must make sure that they, too, receive or can get the source code. +And you must show them these terms so they know their rights. + +We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + +Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + +Finally, any free program is threatened constantly by software patents. +We wish to avoid the danger that redistributors of a free program will +individually obtain patent licenses, in effect making the program +proprietary. To prevent this, we have made it clear that any patent must +be licensed for everyone's free use or not licensed at all. + +The precise terms and conditions for copying, distribution and +modification follow. + +TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + +0. This License applies to any program or other work which contains a +notice placed by the copyright holder saying it may be distributed under +the terms of this General Public License. The "Program", below, refers +to any such program or work, and a "work based on the Program" means +either the Program or any derivative work under copyright law: that is +to say, a work containing the Program or a portion of it, either +verbatim or with modifications and/or translated into another language. +(Hereinafter, translation is included without limitation in the term +"modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of running +the Program is not restricted, and the output from the Program is +covered only if its contents constitute a work based on the Program +(independent of having been made by running the Program). Whether that +is true depends on what the Program does. + +1. You may copy and distribute verbatim copies of the Program's source +code as you receive it, in any medium, provided that you conspicuously +and appropriately publish on each copy an appropriate copyright notice +and disclaimer of warranty; keep intact all the notices that refer to +this License and to the absence of any warranty; and give any other +recipients of the Program a copy of this License along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + +2. You may modify your copy or copies of the Program or any portion of +it, thus forming a work based on the Program, and copy and distribute +such modifications or work under the terms of Section 1 above, provided +that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices stating + that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in whole + or in part contains or is derived from the Program or any part thereof, + to be licensed as a whole at no charge to all third parties under the + terms of this License. + + c) If the modified program normally reads commands interactively when + run, you must cause it, when started running for such interactive use in + the most ordinary way, to print or display an announcement including an + appropriate copyright notice and a notice that there is no warranty (or + else, saying that you provide a warranty) and that users may + redistribute the program under these conditions, and telling the user + how to view a copy of this License. (Exception: if the Program itself is + interactive but does not normally print such an announcement, your work + based on the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, and +can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based on +the Program, the distribution of the whole must be on the terms of this +License, whose permissions for other licensees extend to the entire +whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of a +storage or distribution medium does not bring the other work under the +scope of this License. + +3. You may copy and distribute the Program (or a work based on it, under +Section 2) in object code or executable form under the terms of Sections +1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable source + code, which must be distributed under the terms of Sections 1 and 2 + above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three years, to + give any third party, for a charge no more than your cost of physically + performing source distribution, a complete machine-readable copy of the + corresponding source code, to be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer to + distribute corresponding source code. (This alternative is allowed only + for noncommercial distribution and only if you received the program in + object code or executable form with such an offer, in accord with + Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source code +means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to control +compilation and installation of the executable. However, as a special +exception, the source code distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies the +executable. + +If distribution of executable or object code is made by offering access +to copy from a designated place, then offering equivalent access to copy +the source code from the same place counts as distribution of the source +code, even though third parties are not compelled to copy the source +along with the object code. + +4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt otherwise +to copy, modify, sublicense or distribute the Program is void, and will +automatically terminate your rights under this License. However, parties +who have received copies, or rights, from you under this License will +not have their licenses terminated so long as such parties remain in +full compliance. + +5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and all +its terms and conditions for copying, distributing or modifying the +Program or works based on it. + +6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further restrictions +on the recipients' exercise of the rights granted herein. You are not +responsible for enforcing compliance by third parties to this License. + +7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot distribute +so as to satisfy simultaneously your obligations under this License and +any other pertinent obligations, then as a consequence you may not +distribute the Program at all. For example, if a patent license would +not permit royalty-free redistribution of the Program by all those who +receive copies directly or indirectly through you, then the only way you +could satisfy both it and this License would be to refrain entirely from +distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is implemented +by public license practices. Many people have made generous +contributions to the wide range of software distributed through that +system in reliance on consistent application of that system; it is up to +the author/donor to decide if he or she is willing to distribute +software through any other system and a licensee cannot impose that +choice. + +This section is intended to make thoroughly clear what is believed to be +a consequence of the rest of this License. + +8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License may +add an explicit geographical distribution limitation excluding those +countries, so that distribution is permitted only in or among countries +not thus excluded. In such case, this License incorporates the +limitation as if written in the body of this License. + +9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Program does not specify a version +number of this License, you may choose any version ever published by the +Free Software Foundation. + +10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the +author to ask for permission. For software which is copyrighted by the +Free Software Foundation, write to the Free Software Foundation; we +sometimes make exceptions for this. Our decision will be guided by the +two goals of preserving the free status of all derivatives of our free +software and of promoting the sharing and reuse of software generally. + +NO WARRANTY + +11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER +EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE +ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH +YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL +NECESSARY SERVICING, REPAIR OR CORRECTION. + +12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR +DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL +DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM +(INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED +INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF +THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR +OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +END OF TERMS AND CONDITIONS + +How to Apply These Terms to Your New Programs + +If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these +terms. + +To do so, attach the following notices to the program. It is safest to +attach them to the start of each source file to most effectively convey +the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + One line to give the program's name and a brief idea of what it does. + Copyright (C) <year> <name of author> + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General + Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author Gnomovision + comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is + free software, and you are welcome to redistribute it under certain + conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the +appropriate parts of the General Public License. Of course, the commands +you use may be called something other than `show w' and `show c'; they +could even be mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + signature of Ty Coon, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications +with the library. If this is what you want to do, use the GNU Library +General Public License instead of this License. + +# + +"CLASSPATH" EXCEPTION TO THE GPL VERSION 2 + +Certain source files distributed by Oracle are subject to the following +clarification and special exception to the GPL Version 2, but only where +Oracle has expressly included in the particular source file's header the +words "Oracle designates this particular file as subject to the +"Classpath" exception as provided by Oracle in the License file that +accompanied this code." + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License Version 2 cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under terms +of your choice, provided that you also meet, for each linked independent +module, the terms and conditions of the license of that module. An +independent module is a module which is not derived from or based on +this library. If you modify this library, you may extend this exception +to your version of the library, but you are not obligated to do so. If +you do not wish to do so, delete this exception statement from your +version. diff --git a/tutorial/Tutorial.java b/tutorial/Tutorial.java index 625d387..025a4d5 100644 --- a/tutorial/Tutorial.java +++ b/tutorial/Tutorial.java @@ -16,8 +16,9 @@ package tutorial; +import com.google.caliper.BeforeExperiment; +import com.google.caliper.Benchmark; import com.google.caliper.Param; -import com.google.caliper.SimpleBenchmark; /** * Caliper tutorial. To run the example benchmarks in this file: @@ -31,7 +32,7 @@ public class Tutorial { * * Notice: * - * - We write a class that extends com.google.caliper.SimpleBenchmark. + * - We write a class that extends com.google.caliper.Benchmark. * - It contains a public instance method whose name begins with 'time' and * which accepts a single 'int reps' parameter. * - The body of the method simply executes the code we wish to measure, @@ -48,8 +49,8 @@ public class Tutorial { * --------- --- * NanoTime 233 */ - public static class Benchmark1 extends SimpleBenchmark { - public void timeNanoTime(int reps) { + public static class Benchmark1 { + @Benchmark void timeNanoTime(int reps) { for (int i = 0; i < reps; i++) { System.nanoTime(); } @@ -69,13 +70,13 @@ public class Tutorial { * NanoTime 248 * CurrentTimeMillis 118 */ - public static class Benchmark2 extends SimpleBenchmark { - public void timeNanoTime(int reps) { + public static class Benchmark2 { + @Benchmark void timeNanoTime(int reps) { for (int i = 0; i < reps; i++) { System.nanoTime(); } } - public void timeCurrentTimeMillis(int reps) { + @Benchmark void timeCurrentTimeMillis(int reps) { for (int i = 0; i < reps; i++) { System.currentTimeMillis(); } @@ -86,11 +87,11 @@ public class Tutorial { * Let's try iterating over a large array. This seems simple enough, but * there is a problem! */ - public static class Benchmark3 extends SimpleBenchmark { + public static class Benchmark3 { private final int[] array = new int[1000000]; @SuppressWarnings("UnusedDeclaration") // IDEA tries to warn us! - public void timeArrayIteration_BAD(int reps) { + @Benchmark void timeArrayIteration_BAD(int reps) { for (int i = 0; i < reps; i++) { for (int ignoreMe : array) {} } @@ -123,10 +124,10 @@ public class Tutorial { * With this change, Caliper should report a much more realistic value, more * on the order of an entire millisecond. */ - public static class Benchmark4 extends SimpleBenchmark { + public static class Benchmark4 { private final int[] array = new int[1000000]; - public int timeArrayIteration_fixed(int reps) { + @Benchmark int timeArrayIteration_fixed(int reps) { int dummy = 0; for (int i = 0; i < reps; i++) { for (int doNotIgnoreMe : array) { @@ -164,17 +165,17 @@ public class Tutorial { * ArrayIteration 1000 477 |||||||||||||||||||||||||||||| * */ - public static class Benchmark5 extends SimpleBenchmark { + public static class Benchmark5 { @Param int size; // set automatically by framework private int[] array; // set by us, in setUp() - @Override protected void setUp() { + @BeforeExperiment void setUp() { // @Param values are guaranteed to have been injected by now array = new int[size]; } - public int timeArrayIteration(int reps) { + @Benchmark int timeArrayIteration(int reps) { int dummy = 0; for (int i = 0; i < reps; i++) { for (int doNotIgnoreMe : array) { |