changeset 400:b45d214529fc

Support @Param as the configurable benchmark option.
author shade
date Sun, 16 Feb 2014 18:33:50 +0400
parents 542cef961e16
children 0280e5a5d571
files jmh-core-it/src/test/java/org/openjdk/jmh/it/compile/params/ParamTest.java jmh-core-it/src/test/java/org/openjdk/jmh/it/params/BenchmarkBenchParamSequenceTest.java jmh-core-it/src/test/java/org/openjdk/jmh/it/params/BenchmarkStateParamSequenceTest.java jmh-core-it/src/test/java/org/openjdk/jmh/it/params/GroupBenchParamSequenceTest.java jmh-core-it/src/test/java/org/openjdk/jmh/it/params/GroupStateParamSequenceTest.java jmh-core-it/src/test/java/org/openjdk/jmh/it/params/Shared.java jmh-core-it/src/test/java/org/openjdk/jmh/it/params/ThreadBenchParamSequenceTest.java jmh-core-it/src/test/java/org/openjdk/jmh/it/params/ThreadStateParamSequenceTest.java jmh-core/src/main/java/org/openjdk/jmh/annotations/Param.java jmh-core/src/main/java/org/openjdk/jmh/logic/InfraControl.java jmh-core/src/main/java/org/openjdk/jmh/output/format/TextReportFormat.java jmh-core/src/main/java/org/openjdk/jmh/processor/internal/GenerateMicroBenchmarkProcessor.java jmh-core/src/main/java/org/openjdk/jmh/processor/internal/MethodGroup.java jmh-core/src/main/java/org/openjdk/jmh/processor/internal/ParamValidationProcessor.java jmh-core/src/main/java/org/openjdk/jmh/processor/internal/StateObject.java jmh-core/src/main/java/org/openjdk/jmh/processor/internal/StateObjectHandler.java jmh-core/src/main/java/org/openjdk/jmh/runner/ActualParams.java jmh-core/src/main/java/org/openjdk/jmh/runner/BenchmarkRecord.java jmh-core/src/main/java/org/openjdk/jmh/runner/LoopMicroBenchmarkHandler.java jmh-core/src/main/java/org/openjdk/jmh/runner/Runner.java jmh-core/src/main/java/org/openjdk/jmh/runner/options/ChainedOptionsBuilder.java jmh-core/src/main/java/org/openjdk/jmh/runner/options/CommandLineOptions.java jmh-core/src/main/java/org/openjdk/jmh/runner/options/Options.java jmh-core/src/main/java/org/openjdk/jmh/runner/options/OptionsBuilder.java jmh-core/src/main/java/org/openjdk/jmh/util/internal/Optional.java jmh-core/src/test/java/org/openjdk/jmh/runner/options/TestOptions.java jmh-core/src/test/java/org/openjdk/jmh/runner/options/TestParentOptions.java jmh-samples/src/main/java/org/openjdk/jmh/samples/JMHSample_27_Params.java
diffstat 28 files changed, 1577 insertions(+), 42 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jmh-core-it/src/test/java/org/openjdk/jmh/it/compile/params/ParamTest.java	Sun Feb 16 18:33:50 2014 +0400
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code 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
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package org.openjdk.jmh.it.compile.params;
+
+import org.openjdk.jmh.annotations.GenerateMicroBenchmark;
+import org.openjdk.jmh.annotations.Param;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.State;
+
+@State(Scope.Benchmark)
+public class ParamTest {
+
+    @Param("true")
+    public boolean param_boolean;
+
+    @Param("0")
+    public byte param_byte;
+
+    @Param("0")
+    public short param_short;
+
+    @Param("0")
+    public char param_char;
+
+    @Param("0")
+    public int param_int;
+
+    @Param("0")
+    public float param_float;
+
+    @Param("0")
+    public long param_long;
+
+    @Param("0")
+    public double param_double;
+
+    @Param("true")
+    public Boolean param_Boolean;
+
+    @Param("0")
+    public Byte param_Byte;
+
+    @Param("0")
+    public Short param_Short;
+
+    @Param("0")
+    public Character param_Char;
+
+    @Param("0")
+    public Integer param_Int;
+
+    @Param("0")
+    public Float param_Float;
+
+    @Param("0")
+    public Long param_Long;
+
+    @Param("0")
+    public Double param_Double;
+
+    @Param("null")
+    public String param_String;
+
+    @GenerateMicroBenchmark
+    public void test() {
+
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jmh-core-it/src/test/java/org/openjdk/jmh/it/params/BenchmarkBenchParamSequenceTest.java	Sun Feb 16 18:33:50 2014 +0400
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code 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
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package org.openjdk.jmh.it.params;
+
+import org.junit.Test;
+import org.openjdk.jmh.annotations.Fork;
+import org.openjdk.jmh.annotations.GenerateMicroBenchmark;
+import org.openjdk.jmh.annotations.Measurement;
+import org.openjdk.jmh.annotations.Param;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.State;
+import org.openjdk.jmh.annotations.Warmup;
+import org.openjdk.jmh.it.Fixtures;
+import org.openjdk.jmh.logic.results.RunResult;
+import org.openjdk.jmh.runner.BenchmarkRecord;
+import org.openjdk.jmh.runner.Runner;
+import org.openjdk.jmh.runner.RunnerException;
+import org.openjdk.jmh.runner.options.Options;
+import org.openjdk.jmh.runner.options.OptionsBuilder;
+
+import java.util.SortedMap;
+import java.util.concurrent.TimeUnit;
+
+@Measurement(iterations = 1, time = 100, timeUnit = TimeUnit.MICROSECONDS)
+@Warmup(iterations = 1, time = 100, timeUnit = TimeUnit.MICROSECONDS)
+@Fork(1)
+@State(Scope.Benchmark)
+public class BenchmarkBenchParamSequenceTest {
+
+    @Param({"1", "2", "3"})
+    public int x;
+
+    @Param({"a", "b", "c"})
+    public String y;
+
+    @GenerateMicroBenchmark
+    public void test() {
+        Fixtures.work();
+    }
+
+    @Test
+    public void full() throws RunnerException {
+        Options opts = new OptionsBuilder()
+                .include(Fixtures.getTestMask(this.getClass()))
+                .shouldFailOnError(true)
+                .build();
+
+        SortedMap<BenchmarkRecord,RunResult> params = new Runner(opts).run();
+        Shared.compare(params, new int[] {1, 2, 3}, new String[] { "a", "b", "c"});
+    }
+
+    @Test
+    public void constrainedX() throws RunnerException {
+        Options opts = new OptionsBuilder()
+                .include(Fixtures.getTestMask(this.getClass()))
+                .shouldFailOnError(true)
+                .param("x", "2", "3")
+                .build();
+
+        SortedMap<BenchmarkRecord,RunResult> params = new Runner(opts).run();
+        Shared.compare(params, new int[] {2, 3}, new String[] { "a", "b", "c"});
+    }
+
+    @Test
+    public void constrainedY() throws RunnerException {
+        Options opts = new OptionsBuilder()
+                .include(Fixtures.getTestMask(this.getClass()))
+                .shouldFailOnError(true)
+                .param("y", "b", "c")
+                .build();
+
+        SortedMap<BenchmarkRecord,RunResult> params = new Runner(opts).run();
+        Shared.compare(params, new int[] {1, 2, 3}, new String[] { "b", "c"});
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jmh-core-it/src/test/java/org/openjdk/jmh/it/params/BenchmarkStateParamSequenceTest.java	Sun Feb 16 18:33:50 2014 +0400
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code 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
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package org.openjdk.jmh.it.params;
+
+import org.junit.Test;
+import org.openjdk.jmh.annotations.Fork;
+import org.openjdk.jmh.annotations.GenerateMicroBenchmark;
+import org.openjdk.jmh.annotations.Measurement;
+import org.openjdk.jmh.annotations.Param;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.State;
+import org.openjdk.jmh.annotations.Warmup;
+import org.openjdk.jmh.it.Fixtures;
+import org.openjdk.jmh.logic.results.RunResult;
+import org.openjdk.jmh.runner.BenchmarkRecord;
+import org.openjdk.jmh.runner.Runner;
+import org.openjdk.jmh.runner.RunnerException;
+import org.openjdk.jmh.runner.options.Options;
+import org.openjdk.jmh.runner.options.OptionsBuilder;
+
+import java.util.SortedMap;
+import java.util.concurrent.TimeUnit;
+
+@Measurement(iterations = 1, time = 100, timeUnit = TimeUnit.MICROSECONDS)
+@Warmup(iterations = 1, time = 100, timeUnit = TimeUnit.MICROSECONDS)
+@Fork(1)
+public class BenchmarkStateParamSequenceTest {
+
+    @State(Scope.Benchmark)
+    public static class MyState {
+        @Param({"1", "2", "3"})
+        public int x;
+
+        @Param({"a", "b", "c"})
+        public String y;
+    }
+
+    @GenerateMicroBenchmark
+    public void test(MyState s) {
+        Fixtures.work();
+    }
+
+    @Test
+    public void full() throws RunnerException {
+        Options opts = new OptionsBuilder()
+                .include(Fixtures.getTestMask(this.getClass()))
+                .shouldFailOnError(true)
+                .build();
+
+        SortedMap<BenchmarkRecord,RunResult> params = new Runner(opts).run();
+        Shared.compare(params, new int[] {1, 2, 3}, new String[] { "a", "b", "c"});
+    }
+
+    @Test
+    public void constrainedX() throws RunnerException {
+        Options opts = new OptionsBuilder()
+                .include(Fixtures.getTestMask(this.getClass()))
+                .shouldFailOnError(true)
+                .param("x", "2", "3")
+                .build();
+
+        SortedMap<BenchmarkRecord,RunResult> params = new Runner(opts).run();
+        Shared.compare(params, new int[] {2, 3}, new String[] { "a", "b", "c"});
+    }
+
+    @Test
+    public void constrainedY() throws RunnerException {
+        Options opts = new OptionsBuilder()
+                .include(Fixtures.getTestMask(this.getClass()))
+                .shouldFailOnError(true)
+                .param("y", "b", "c")
+                .build();
+
+        SortedMap<BenchmarkRecord,RunResult> params = new Runner(opts).run();
+        Shared.compare(params, new int[] {1, 2, 3}, new String[] { "b", "c"});
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jmh-core-it/src/test/java/org/openjdk/jmh/it/params/GroupBenchParamSequenceTest.java	Sun Feb 16 18:33:50 2014 +0400
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code 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
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package org.openjdk.jmh.it.params;
+
+import org.junit.Test;
+import org.openjdk.jmh.annotations.Fork;
+import org.openjdk.jmh.annotations.GenerateMicroBenchmark;
+import org.openjdk.jmh.annotations.Group;
+import org.openjdk.jmh.annotations.Measurement;
+import org.openjdk.jmh.annotations.Param;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.State;
+import org.openjdk.jmh.annotations.Warmup;
+import org.openjdk.jmh.it.Fixtures;
+import org.openjdk.jmh.logic.results.RunResult;
+import org.openjdk.jmh.runner.BenchmarkRecord;
+import org.openjdk.jmh.runner.Runner;
+import org.openjdk.jmh.runner.RunnerException;
+import org.openjdk.jmh.runner.options.Options;
+import org.openjdk.jmh.runner.options.OptionsBuilder;
+
+import java.util.SortedMap;
+import java.util.concurrent.TimeUnit;
+
+@Measurement(iterations = 1, time = 100, timeUnit = TimeUnit.MICROSECONDS)
+@Warmup(iterations = 1, time = 100, timeUnit = TimeUnit.MICROSECONDS)
+@Fork(1)
+@State(Scope.Group)
+public class GroupBenchParamSequenceTest {
+
+    @Param({"1", "2", "3"})
+    public int x;
+
+    @Param({"a", "b", "c"})
+    public String y;
+
+    @GenerateMicroBenchmark
+    @Group("s")
+    public void test1() {
+        Fixtures.work();
+    }
+
+    @GenerateMicroBenchmark
+    @Group("s")
+    public void test2() {
+        Fixtures.work();
+    }
+
+    @Test
+    public void full() throws RunnerException {
+        Options opts = new OptionsBuilder()
+                .include(Fixtures.getTestMask(this.getClass()))
+                .shouldFailOnError(true)
+                .build();
+
+        SortedMap<BenchmarkRecord,RunResult> params = new Runner(opts).run();
+        Shared.compare(params, new int[] {1, 2, 3}, new String[] { "a", "b", "c"});
+    }
+
+    @Test
+    public void constrainedX() throws RunnerException {
+        Options opts = new OptionsBuilder()
+                .include(Fixtures.getTestMask(this.getClass()))
+                .shouldFailOnError(true)
+                .param("x", "2", "3")
+                .build();
+
+        SortedMap<BenchmarkRecord,RunResult> params = new Runner(opts).run();
+        Shared.compare(params, new int[] {2, 3}, new String[] { "a", "b", "c"});
+    }
+
+    @Test
+    public void constrainedY() throws RunnerException {
+        Options opts = new OptionsBuilder()
+                .include(Fixtures.getTestMask(this.getClass()))
+                .shouldFailOnError(true)
+                .param("y", "b", "c")
+                .build();
+
+        SortedMap<BenchmarkRecord,RunResult> params = new Runner(opts).run();
+        Shared.compare(params, new int[] {1, 2, 3}, new String[] { "b", "c" });
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jmh-core-it/src/test/java/org/openjdk/jmh/it/params/GroupStateParamSequenceTest.java	Sun Feb 16 18:33:50 2014 +0400
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code 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
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package org.openjdk.jmh.it.params;
+
+import org.junit.Test;
+import org.openjdk.jmh.annotations.Fork;
+import org.openjdk.jmh.annotations.GenerateMicroBenchmark;
+import org.openjdk.jmh.annotations.Group;
+import org.openjdk.jmh.annotations.Measurement;
+import org.openjdk.jmh.annotations.Param;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.State;
+import org.openjdk.jmh.annotations.Warmup;
+import org.openjdk.jmh.it.Fixtures;
+import org.openjdk.jmh.logic.results.RunResult;
+import org.openjdk.jmh.runner.BenchmarkRecord;
+import org.openjdk.jmh.runner.Runner;
+import org.openjdk.jmh.runner.RunnerException;
+import org.openjdk.jmh.runner.options.Options;
+import org.openjdk.jmh.runner.options.OptionsBuilder;
+
+import java.util.SortedMap;
+import java.util.concurrent.TimeUnit;
+
+@Measurement(iterations = 1, time = 100, timeUnit = TimeUnit.MICROSECONDS)
+@Warmup(iterations = 1, time = 100, timeUnit = TimeUnit.MICROSECONDS)
+@Fork(1)
+public class GroupStateParamSequenceTest {
+
+    @State(Scope.Group)
+    public static class MyState {
+        @Param({"1", "2", "3"})
+        public int x;
+
+        @Param({"a", "b", "c"})
+        public String y;
+    }
+
+    @GenerateMicroBenchmark
+    @Group("s")
+    public void test1(MyState s) {
+        Fixtures.work();
+    }
+
+    @GenerateMicroBenchmark
+    @Group("s")
+    public void test2(MyState s) {
+        Fixtures.work();
+    }
+
+    @Test
+    public void full() throws RunnerException {
+        Options opts = new OptionsBuilder()
+                .include(Fixtures.getTestMask(this.getClass()))
+                .shouldFailOnError(true)
+                .build();
+
+        SortedMap<BenchmarkRecord,RunResult> params = new Runner(opts).run();
+        Shared.compare(params, new int[] {1, 2, 3}, new String[] { "a", "b", "c"});
+    }
+
+    @Test
+    public void constrainedX() throws RunnerException {
+        Options opts = new OptionsBuilder()
+                .include(Fixtures.getTestMask(this.getClass()))
+                .shouldFailOnError(true)
+                .param("x", "2", "3")
+                .build();
+
+        SortedMap<BenchmarkRecord,RunResult> params = new Runner(opts).run();
+        Shared.compare(params, new int[] {2, 3}, new String[] { "a", "b", "c"});
+    }
+
+    @Test
+    public void constrainedY() throws RunnerException {
+        Options opts = new OptionsBuilder()
+                .include(Fixtures.getTestMask(this.getClass()))
+                .shouldFailOnError(true)
+                .param("y", "b", "c")
+                .build();
+
+        SortedMap<BenchmarkRecord,RunResult> params = new Runner(opts).run();
+        Shared.compare(params, new int[] {1, 2, 3}, new String[] { "b", "c"});
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jmh-core-it/src/test/java/org/openjdk/jmh/it/params/Shared.java	Sun Feb 16 18:33:50 2014 +0400
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code 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
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package org.openjdk.jmh.it.params;
+
+import junit.framework.Assert;
+import org.openjdk.jmh.logic.results.RunResult;
+import org.openjdk.jmh.runner.BenchmarkRecord;
+
+import java.util.HashSet;
+import java.util.Set;
+import java.util.SortedMap;
+
+public class Shared {
+
+    public static void compare(SortedMap<BenchmarkRecord,RunResult> params, int[] xs, String[] ys) {
+        Set<String> actualPairs = new HashSet<String>();
+        for (BenchmarkRecord br : params.keySet()) {
+            actualPairs.add(br.getActualParams().get("x") + br.getActualParams().get("y"));
+        }
+
+        for (int x : xs) {
+            for (String y : ys) {
+                Assert.assertTrue(x + y + " is not found", actualPairs.contains(x + y));
+            }
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jmh-core-it/src/test/java/org/openjdk/jmh/it/params/ThreadBenchParamSequenceTest.java	Sun Feb 16 18:33:50 2014 +0400
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code 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
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package org.openjdk.jmh.it.params;
+
+import org.junit.Test;
+import org.openjdk.jmh.annotations.Fork;
+import org.openjdk.jmh.annotations.GenerateMicroBenchmark;
+import org.openjdk.jmh.annotations.Measurement;
+import org.openjdk.jmh.annotations.Param;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.State;
+import org.openjdk.jmh.annotations.Warmup;
+import org.openjdk.jmh.it.Fixtures;
+import org.openjdk.jmh.logic.results.RunResult;
+import org.openjdk.jmh.runner.BenchmarkRecord;
+import org.openjdk.jmh.runner.Runner;
+import org.openjdk.jmh.runner.RunnerException;
+import org.openjdk.jmh.runner.options.Options;
+import org.openjdk.jmh.runner.options.OptionsBuilder;
+
+import java.util.SortedMap;
+import java.util.concurrent.TimeUnit;
+
+@Measurement(iterations = 1, time = 100, timeUnit = TimeUnit.MICROSECONDS)
+@Warmup(iterations = 1, time = 100, timeUnit = TimeUnit.MICROSECONDS)
+@Fork(1)
+@State(Scope.Thread)
+public class ThreadBenchParamSequenceTest {
+
+    @Param({"1", "2", "3"})
+    public int x;
+
+    @Param({"a", "b", "c"})
+    public String y;
+
+    @GenerateMicroBenchmark
+    public void test() {
+        Fixtures.work();
+    }
+
+    @Test
+    public void full() throws RunnerException {
+        Options opts = new OptionsBuilder()
+                .include(Fixtures.getTestMask(this.getClass()))
+                .shouldFailOnError(true)
+                .build();
+
+        SortedMap<BenchmarkRecord,RunResult> params = new Runner(opts).run();
+        Shared.compare(params, new int[] {1, 2, 3}, new String[] { "a", "b", "c"});
+    }
+
+    @Test
+    public void constrainedX() throws RunnerException {
+        Options opts = new OptionsBuilder()
+                .include(Fixtures.getTestMask(this.getClass()))
+                .shouldFailOnError(true)
+                .param("x", "2", "3")
+                .build();
+
+        SortedMap<BenchmarkRecord,RunResult> params = new Runner(opts).run();
+        Shared.compare(params, new int[] {2, 3}, new String[] { "a", "b", "c"});
+    }
+
+    @Test
+    public void constrainedY() throws RunnerException {
+        Options opts = new OptionsBuilder()
+                .include(Fixtures.getTestMask(this.getClass()))
+                .shouldFailOnError(true)
+                .param("y", "b", "c")
+                .build();
+
+        SortedMap<BenchmarkRecord,RunResult> params = new Runner(opts).run();
+        Shared.compare(params, new int[] {1, 2, 3}, new String[] { "b", "c"});
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jmh-core-it/src/test/java/org/openjdk/jmh/it/params/ThreadStateParamSequenceTest.java	Sun Feb 16 18:33:50 2014 +0400
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code 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
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package org.openjdk.jmh.it.params;
+
+import org.junit.Test;
+import org.openjdk.jmh.annotations.Fork;
+import org.openjdk.jmh.annotations.GenerateMicroBenchmark;
+import org.openjdk.jmh.annotations.Measurement;
+import org.openjdk.jmh.annotations.Param;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.State;
+import org.openjdk.jmh.annotations.Warmup;
+import org.openjdk.jmh.it.Fixtures;
+import org.openjdk.jmh.logic.results.RunResult;
+import org.openjdk.jmh.runner.BenchmarkRecord;
+import org.openjdk.jmh.runner.Runner;
+import org.openjdk.jmh.runner.RunnerException;
+import org.openjdk.jmh.runner.options.Options;
+import org.openjdk.jmh.runner.options.OptionsBuilder;
+
+import java.util.SortedMap;
+import java.util.concurrent.TimeUnit;
+
+@Measurement(iterations = 1, time = 100, timeUnit = TimeUnit.MICROSECONDS)
+@Warmup(iterations = 1, time = 100, timeUnit = TimeUnit.MICROSECONDS)
+@Fork(1)
+public class ThreadStateParamSequenceTest {
+
+    @State(Scope.Thread)
+    public static class MyState {
+        @Param({"1", "2", "3"})
+        public int x;
+
+        @Param({"a", "b", "c"})
+        public String y;
+    }
+
+    @GenerateMicroBenchmark
+    public void test(MyState s) {
+        Fixtures.work();
+    }
+
+    @Test
+    public void full() throws RunnerException {
+        Options opts = new OptionsBuilder()
+                .include(Fixtures.getTestMask(this.getClass()))
+                .shouldFailOnError(true)
+                .build();
+
+        SortedMap<BenchmarkRecord,RunResult> params = new Runner(opts).run();
+        Shared.compare(params, new int[] {1, 2, 3}, new String[] { "a", "b", "c"});
+    }
+
+    @Test
+    public void constrainedX() throws RunnerException {
+        Options opts = new OptionsBuilder()
+                .include(Fixtures.getTestMask(this.getClass()))
+                .shouldFailOnError(true)
+                .param("x", "2", "3")
+                .build();
+
+        SortedMap<BenchmarkRecord,RunResult> params = new Runner(opts).run();
+        Shared.compare(params, new int[] {2, 3}, new String[] { "a", "b", "c"});
+    }
+
+    @Test
+    public void constrainedY() throws RunnerException {
+        Options opts = new OptionsBuilder()
+                .include(Fixtures.getTestMask(this.getClass()))
+                .shouldFailOnError(true)
+                .param("y", "b", "c")
+                .build();
+
+        SortedMap<BenchmarkRecord,RunResult> params = new Runner(opts).run();
+        Shared.compare(params, new int[] {1, 2, 3}, new String[] { "b", "c"});
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jmh-core/src/main/java/org/openjdk/jmh/annotations/Param.java	Sun Feb 16 18:33:50 2014 +0400
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code 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
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package org.openjdk.jmh.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Marks the configurable parameter in the benchmark.
+ * <p/>
+ * {@link Param} fields should be public non-final instance fields,
+ * and should only reside in in {@link State} classes. JMH will inject
+ * the value into the annotated field before any {@link Setup} method
+ * is called. It is *not* guaranteed the field value would be accessible
+ * in any instance initializer of {@link State}.
+ * <p/>
+ * Parameters are acceptable on any primitive type, primitive wrapper type,
+ * or a String. The annotation value is given in String, and will be
+ * valueOf-ed as required to match the field type.
+ * <p/>
+ * When multiple {@link Param}-s are needed for the benchmark run,
+ * JMH will compute the outer product of all the parameters in the run.
+ */
+@Target({ElementType.FIELD})
+@Retention(RetentionPolicy.SOURCE)
+@Inherited
+public @interface Param {
+
+    /**
+     * Default values for the parameter.
+     * @return values sequence to follow.
+     */
+    String[] value();
+
+}
--- a/jmh-core/src/main/java/org/openjdk/jmh/logic/InfraControl.java	Fri Feb 14 17:13:31 2014 +0400
+++ b/jmh-core/src/main/java/org/openjdk/jmh/logic/InfraControl.java	Sun Feb 16 18:33:50 2014 +0400
@@ -24,6 +24,7 @@
  */
 package org.openjdk.jmh.logic;
 
+import org.openjdk.jmh.runner.ActualParams;
 import org.openjdk.jmh.runner.parameters.TimeValue;
 import sun.misc.Unsafe;
 
@@ -80,8 +81,8 @@
         }
     }
 
-    public InfraControl(int threads, boolean syncIterations, TimeValue loopTime, CountDownLatch preSetup, CountDownLatch preTearDown, boolean lastIteration, TimeUnit timeUnit, int batchSize) {
-        super(threads, syncIterations, loopTime, preSetup, preTearDown, lastIteration, timeUnit, batchSize);
+    public InfraControl(int threads, boolean syncIterations, TimeValue loopTime, CountDownLatch preSetup, CountDownLatch preTearDown, boolean lastIteration, TimeUnit timeUnit, int batchSize, ActualParams params) {
+        super(threads, syncIterations, loopTime, preSetup, preTearDown, lastIteration, timeUnit, batchSize, params);
     }
 
     /**
@@ -180,8 +181,9 @@
     public volatile boolean warmupShouldWait, warmdownShouldWait;
 
     public final int batchSize;
+    private final ActualParams params;
 
-    public InfraControlL2(int threads, boolean syncIterations, TimeValue loopTime, CountDownLatch preSetup, CountDownLatch preTearDown, boolean lastIteration, TimeUnit timeUnit, int batchSize) {
+    public InfraControlL2(int threads, boolean syncIterations, TimeValue loopTime, CountDownLatch preSetup, CountDownLatch preTearDown, boolean lastIteration, TimeUnit timeUnit, int batchSize, ActualParams params) {
         this.threads = threads;
         this.syncIterations = syncIterations;
         this.warmupVisited = new AtomicInteger();
@@ -194,6 +196,7 @@
         this.duration = loopTime.convertTo(TimeUnit.NANOSECONDS);
         this.lastIteration = lastIteration;
         this.timeUnit = timeUnit;
+        this.params = params;
         this.batchSize = batchSize;
     }
 
@@ -221,6 +224,13 @@
         }
     }
 
+    public String getParam(String name) {
+        if (!params.containsKey(name)) {
+            throw new IllegalStateException("Querying the non-existing parameter: " + name);
+        }
+        return params.get(name);
+    }
+
 }
 
 abstract class InfraControlL3 extends InfraControlL2 {
@@ -241,16 +251,16 @@
     private boolean q161, q162, q163, q164, q165, q166, q167, q168;
     private boolean q171, q172, q173, q174, q175, q176, q177, q178;
 
-    public InfraControlL3(int threads, boolean syncIterations, TimeValue loopTime, CountDownLatch preSetup, CountDownLatch preTearDown, boolean lastIteration, TimeUnit timeUnit, int batchSize) {
-        super(threads, syncIterations, loopTime, preSetup, preTearDown, lastIteration, timeUnit, batchSize);
+    public InfraControlL3(int threads, boolean syncIterations, TimeValue loopTime, CountDownLatch preSetup, CountDownLatch preTearDown, boolean lastIteration, TimeUnit timeUnit, int batchSize, ActualParams params) {
+        super(threads, syncIterations, loopTime, preSetup, preTearDown, lastIteration, timeUnit, batchSize, params);
     }
 }
 
 abstract class InfraControlL4 extends InfraControlL3 {
     public int markerEnd;
 
-    public InfraControlL4(int threads, boolean syncIterations, TimeValue loopTime, CountDownLatch preSetup, CountDownLatch preTearDown, boolean lastIteration, TimeUnit timeUnit, int batchSize) {
-        super(threads, syncIterations, loopTime, preSetup, preTearDown, lastIteration, timeUnit, batchSize);
+    public InfraControlL4(int threads, boolean syncIterations, TimeValue loopTime, CountDownLatch preSetup, CountDownLatch preTearDown, boolean lastIteration, TimeUnit timeUnit, int batchSize, ActualParams params) {
+        super(threads, syncIterations, loopTime, preSetup, preTearDown, lastIteration, timeUnit, batchSize, params);
     }
 }
 
--- a/jmh-core/src/main/java/org/openjdk/jmh/output/format/TextReportFormat.java	Fri Feb 14 17:13:31 2014 +0400
+++ b/jmh-core/src/main/java/org/openjdk/jmh/output/format/TextReportFormat.java	Sun Feb 16 18:33:50 2014 +0400
@@ -64,6 +64,9 @@
         out.println("# Threads: " + mbParams.getThreads() + " " + getThreadsString(mbParams.getThreads()) + (mbParams.shouldSynchIterations() ? ", will synchronize iterations" : ""));
         out.println("# Benchmark mode: " + name.getMode().longLabel());
         out.println("# Benchmark: " + name.getUsername());
+        if (name.getActualParams() != null) {
+            out.println("# Parameters: " + name.getActualParams());
+        }
     }
 
     @Override
@@ -158,9 +161,9 @@
         Collection<String> benchNames = new ArrayList<String>();
         for (BenchmarkRecord key : runResults.keySet()) {
             RunResult runResult = runResults.get(key);
-            benchNames.add(key.getUsername());
+            benchNames.add(key.getUsername() + mixActualParams(key));
             for (String label : runResult.getSecondaryResults().keySet()) {
-                benchNames.add(key.getUsername() + ":" + label);
+                benchNames.add(key.getUsername() + ":" + label + mixActualParams(key));
             }
         }
 
@@ -180,7 +183,7 @@
             {
                 Statistics stats = res.getPrimaryResult().getStatistics();
                 out.printf("%-" + nameLen + "s %6s %9d %12.3f %12.3f %8s%n",
-                        benchPrefixes.get(key.getUsername()),
+                        benchPrefixes.get(key.getUsername() + mixActualParams(key)),
                         key.getMode().shortLabel(),
                         stats.getN(),
                         stats.getMean(), stats.getMeanErrorAt(0.999),
@@ -191,7 +194,7 @@
                 Statistics stats = res.getSecondaryResults().get(label).getStatistics();
 
                 out.printf("%-" + nameLen + "s %6s %9d %12.3f %12.3f %8s%n",
-                        benchPrefixes.get(key.getUsername() + ":" + label),
+                        benchPrefixes.get(key.getUsername() + ":" + label + mixActualParams(key)),
                         key.getMode().shortLabel(),
                         stats.getN(),
                         stats.getMean(), stats.getMeanErrorAt(0.999),
@@ -200,5 +203,8 @@
         }
     }
 
+    private static String mixActualParams(BenchmarkRecord key) {
+        return (key.getActualParams() == null ? "" : " " + key.getActualParams().toString());
+    }
 
 }
--- a/jmh-core/src/main/java/org/openjdk/jmh/processor/internal/GenerateMicroBenchmarkProcessor.java	Fri Feb 14 17:13:31 2014 +0400
+++ b/jmh-core/src/main/java/org/openjdk/jmh/processor/internal/GenerateMicroBenchmarkProcessor.java	Sun Feb 16 18:33:50 2014 +0400
@@ -33,6 +33,7 @@
 import org.openjdk.jmh.annotations.Measurement;
 import org.openjdk.jmh.annotations.Mode;
 import org.openjdk.jmh.annotations.OperationsPerInvocation;
+import org.openjdk.jmh.annotations.Param;
 import org.openjdk.jmh.annotations.Scope;
 import org.openjdk.jmh.annotations.State;
 import org.openjdk.jmh.annotations.Threads;
@@ -111,6 +112,7 @@
 
         subProcessors.add(new HelperMethodValidationProcessor());
         subProcessors.add(new GroupValidationProcessor());
+        subProcessors.add(new ParamValidationProcessor());
         subProcessors.add(compilerControl);
     }
 
@@ -167,7 +169,8 @@
                                         group.getWarmupForks(),
                                         group.getJVMArgs(),
                                         group.getJVMArgsPrepend(),
-                                        group.getJVMArgsAppend()
+                                        group.getJVMArgsAppend(),
+                                        group.getParams()
                                 );
                                 writer.println(br.toLine());
                             }
@@ -447,6 +450,31 @@
             group.addStrictFP(classStrictFP);
             group.addStrictFP(methodStrictFP);
             group.addMethod(method, (method.getAnnotation(GroupThreads.class) != null) ? method.getAnnotation(GroupThreads.class).value() : 1);
+
+            // Discovering @Params, part 1:
+            //   For each parameter, walk the type hierarchy up to discover inherited @Param fields in @State objects.
+            ExecutableElement execMethod = (ExecutableElement) method;
+            for (VariableElement element : execMethod.getParameters()) {
+                TypeElement walk = (TypeElement) processingEnv.getTypeUtils().asElement(element.asType());
+                do {
+                    for (VariableElement ve : ElementFilter.fieldsIn(walk.getEnclosedElements())) {
+                        if (ve.getAnnotation(Param.class) != null) {
+                            group.addParam(ve.getSimpleName().toString(), ve.getAnnotation(Param.class).value());
+                        }
+                    }
+                } while ((walk = (TypeElement) processingEnv.getTypeUtils().asElement(walk.getSuperclass())) != null);
+            }
+
+            // Discovering @Params, part 2:
+            //  Walk the type hierarchy up to discover inherited @Param fields for class.
+            TypeElement walk = clazz;
+            do {
+                for (VariableElement ve : ElementFilter.fieldsIn(walk.getEnclosedElements())) {
+                    if (ve.getAnnotation(Param.class) != null) {
+                        group.addParam(ve.getSimpleName().toString(), ve.getAnnotation(Param.class).value());
+                    }
+                }
+            } while ((walk = (TypeElement) processingEnv.getTypeUtils().asElement(walk.getSuperclass())) != null);
         }
 
         // enforce the default value
--- a/jmh-core/src/main/java/org/openjdk/jmh/processor/internal/MethodGroup.java	Fri Feb 14 17:13:31 2014 +0400
+++ b/jmh-core/src/main/java/org/openjdk/jmh/processor/internal/MethodGroup.java	Sun Feb 16 18:33:50 2014 +0400
@@ -41,7 +41,9 @@
 import java.util.Collection;
 import java.util.Collections;
 import java.util.EnumSet;
+import java.util.Map;
 import java.util.Set;
+import java.util.TreeMap;
 import java.util.TreeSet;
 import java.util.concurrent.TimeUnit;
 
@@ -49,12 +51,14 @@
     private final String name;
     private final Set<MethodInvocation> methods;
     private final EnumSet<Mode> modes;
+    private final Map<String, String[]> params;
     private boolean strictFP;
 
     MethodGroup(String name) {
         this.name = name;
         this.methods = new TreeSet<MethodInvocation>();
         this.modes = EnumSet.noneOf(Mode.class);
+        this.params = new TreeMap<String, String[]>();
     }
 
     @Override
@@ -103,6 +107,10 @@
         return name;
     }
 
+    public void addParam(String name, String[] value) {
+        params.put(name, value);
+    }
+
     public void addStrictFP(boolean sfp) {
         strictFP |= sfp;
     }
@@ -244,4 +252,12 @@
         return finalAnn;
     }
 
+    public Optional<Map<String, String[]>> getParams() {
+        if (params.isEmpty()) {
+            return Optional.none();
+        } else {
+            return Optional.of(params);
+        }
+    }
+
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jmh-core/src/main/java/org/openjdk/jmh/processor/internal/ParamValidationProcessor.java	Sun Feb 16 18:33:50 2014 +0400
@@ -0,0 +1,154 @@
+/*
+ * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code 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
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package org.openjdk.jmh.processor.internal;
+
+import org.openjdk.jmh.annotations.Param;
+import org.openjdk.jmh.annotations.State;
+
+import javax.annotation.processing.ProcessingEnvironment;
+import javax.annotation.processing.RoundEnvironment;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.Modifier;
+import javax.lang.model.element.VariableElement;
+import javax.tools.Diagnostic.Kind;
+
+public class ParamValidationProcessor implements SubProcessor {
+
+    @Override
+    public void process(RoundEnvironment roundEnv, ProcessingEnvironment processingEnv) {
+        try {
+            for (Element element : roundEnv.getElementsAnnotatedWith(Param.class)) {
+                if (element.getKind() != ElementKind.FIELD) {
+                    processingEnv.getMessager().printMessage(Kind.ERROR,
+                            "@" + Param.class.getSimpleName() + " annotation is acceptable on fields only.",
+                            element
+                    );
+                }
+
+                if (!element.getModifiers().contains(Modifier.PUBLIC)) {
+                    processingEnv.getMessager().printMessage(Kind.ERROR,
+                            "@" + Param.class.getSimpleName() + " annotation is not acceptable on non-public field.",
+                            element
+                    );
+                }
+
+                if (element.getModifiers().contains(Modifier.STATIC)) {
+                    processingEnv.getMessager().printMessage(Kind.ERROR,
+                            "@" + Param.class.getSimpleName() + " annotation is not acceptable on static fields.",
+                            element
+                    );
+                }
+
+                if (AnnUtils.getAnnotationRecursive(element, State.class) == null) {
+                    processingEnv.getMessager().printMessage(Kind.ERROR,
+                            "@" + Param.class.getSimpleName() + " annotation should be placed in @" + State.class.getSimpleName() +
+                                    "-annotated class.",
+                            element
+                    );
+                }
+
+                VariableElement ve = (VariableElement) element;
+                String[] values = element.getAnnotation(Param.class).value();
+
+                String type = ve.asType().toString();
+                for (String val : values) {
+                    if (!isConforming(val, type)) {
+                        processingEnv.getMessager().printMessage(Kind.ERROR,
+                                "Some @" + Param.class.getSimpleName() + " values can not be converted to target type: " +
+                                "\"" + val + "\" can not be converted to " + type,
+                                element
+                        );
+                    }
+                }
+
+            }
+        } catch (Throwable t) {
+            processingEnv.getMessager().printMessage(Kind.ERROR, "Annotation processor had throw exception: " + t);
+            t.printStackTrace(System.err);
+        }
+    }
+
+    private boolean isConforming(String val, String type) {
+        if (type.equalsIgnoreCase("java.lang.String")) {
+            return true;
+        }
+        if (type.equalsIgnoreCase("boolean") || type.equalsIgnoreCase("java.lang.Boolean")) {
+            return (val.equalsIgnoreCase("true") || val.equalsIgnoreCase("false"));
+        }
+        if (type.equalsIgnoreCase("byte") || type.equalsIgnoreCase("java.lang.Byte")) {
+            try {
+                Byte.valueOf(val);
+                return true;
+            } catch (NumberFormatException nfe){
+            }
+        }
+        if (type.equalsIgnoreCase("char") || type.equalsIgnoreCase("java.lang.Character")) {
+            return (val.length() != 0);
+        }
+        if (type.equalsIgnoreCase("short") || type.equalsIgnoreCase("java.lang.Short")) {
+            try {
+                Short.valueOf(val);
+                return true;
+            } catch (NumberFormatException nfe){
+            }
+        }
+        if (type.equalsIgnoreCase("int") || type.equalsIgnoreCase("java.lang.Integer")) {
+            try {
+                Integer.valueOf(val);
+                return true;
+            } catch (NumberFormatException nfe){
+            }
+        }
+        if (type.equalsIgnoreCase("float") || type.equalsIgnoreCase("java.lang.Float")) {
+            try {
+                Float.valueOf(val);
+                return true;
+            } catch (NumberFormatException nfe){
+            }
+        }
+        if (type.equalsIgnoreCase("long") || type.equalsIgnoreCase("java.lang.Long")) {
+            try {
+                Long.valueOf(val);
+                return true;
+            } catch (NumberFormatException nfe){
+            }
+        }
+        if (type.equalsIgnoreCase("double") || type.equalsIgnoreCase("java.lang.Double")) {
+            try {
+                Double.valueOf(val);
+                return true;
+            } catch (NumberFormatException nfe){
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public void finish(RoundEnvironment roundEnv, ProcessingEnvironment processingEnv) {
+        // do nothing
+    }
+
+}
--- a/jmh-core/src/main/java/org/openjdk/jmh/processor/internal/StateObject.java	Fri Feb 14 17:13:31 2014 +0400
+++ b/jmh-core/src/main/java/org/openjdk/jmh/processor/internal/StateObject.java	Sun Feb 16 18:33:50 2014 +0400
@@ -26,7 +26,11 @@
 
 import org.openjdk.jmh.annotations.Scope;
 
+import javax.lang.model.element.VariableElement;
+import java.util.Collection;
 import java.util.Comparator;
+import java.util.Map;
+import java.util.TreeMap;
 
 public class StateObject {
 
@@ -42,6 +46,7 @@
     public final Scope scope;
     public final String localIdentifier;
     public final String fieldIdentifier;
+    public final Map<String, String> params;
 
     public StateObject(String userType, String jmhType, Scope scope, String fieldIdentifier, String localIdentifier) {
         this.userType = userType;
@@ -49,6 +54,7 @@
         this.scope = scope;
         this.localIdentifier = localIdentifier;
         this.fieldIdentifier = fieldIdentifier;
+        this.params = new TreeMap<String, String>();
     }
 
     @Override
@@ -82,4 +88,53 @@
         return localIdentifier;
     }
 
+    public String getParamAccessor(String name) {
+        return params.get(name);
+    }
+
+    public void addParam(VariableElement ve) {
+        String type = ve.asType().toString();
+        String name = ve.getSimpleName().toString();
+        if (type.equalsIgnoreCase("java.lang.String")) {
+            params.put(name, "control.getParam(\"" + name + "\")");
+            return;
+        }
+        if (type.equalsIgnoreCase("boolean") || type.equalsIgnoreCase("java.lang.Boolean")) {
+            params.put(name, "Boolean.valueOf(control.getParam(\"" + name + "\"))");
+            return;
+        }
+        if (type.equalsIgnoreCase("byte") || type.equalsIgnoreCase("java.lang.Byte")) {
+            params.put(name, "Byte.valueOf(control.getParam(\"" + name + "\"))");
+            return;
+        }
+        if (type.equalsIgnoreCase("char") || type.equalsIgnoreCase("java.lang.Character")) {
+            params.put(name, "(control.getParam(\"" + name + "\")).charAt(0)");
+            return;
+        }
+        if (type.equalsIgnoreCase("short") || type.equalsIgnoreCase("java.lang.Short")) {
+            params.put(name, "Short.valueOf(control.getParam(\"" + name + "\"))");
+            return;
+        }
+        if (type.equalsIgnoreCase("int") || type.equalsIgnoreCase("java.lang.Integer")) {
+            params.put(name, "Integer.valueOf(control.getParam(\"" + name + "\"))");
+            return;
+        }
+        if (type.equalsIgnoreCase("float") || type.equalsIgnoreCase("java.lang.Float")) {
+            params.put(name, "Float.valueOf(control.getParam(\"" + name + "\"))");
+            return;
+        }
+        if (type.equalsIgnoreCase("long") || type.equalsIgnoreCase("java.lang.Long")) {
+            params.put(name, "Long.valueOf(control.getParam(\"" + name + "\"))");
+            return;
+        }
+        if (type.equalsIgnoreCase("double") || type.equalsIgnoreCase("java.lang.Double")) {
+            params.put(name, "Double.valueOf(control.getParam(\"" + name + "\"))");
+            return;
+        }
+        throw new IllegalStateException("Unknown type: " + type);
+    }
+
+    public Collection<String> getParamsLabels() {
+        return params.keySet();
+    }
 }
--- a/jmh-core/src/main/java/org/openjdk/jmh/processor/internal/StateObjectHandler.java	Fri Feb 14 17:13:31 2014 +0400
+++ b/jmh-core/src/main/java/org/openjdk/jmh/processor/internal/StateObjectHandler.java	Sun Feb 16 18:33:50 2014 +0400
@@ -26,6 +26,7 @@
 
 import org.openjdk.jmh.annotations.AuxCounters;
 import org.openjdk.jmh.annotations.Level;
+import org.openjdk.jmh.annotations.Param;
 import org.openjdk.jmh.annotations.Scope;
 import org.openjdk.jmh.annotations.Setup;
 import org.openjdk.jmh.annotations.State;
@@ -41,6 +42,7 @@
 import javax.lang.model.element.ExecutableElement;
 import javax.lang.model.element.Modifier;
 import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.VariableElement;
 import javax.lang.model.util.ElementFilter;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -200,10 +202,20 @@
 
         stateObjects.add(so);
 
+        // walk the type hierarchy up to discover inherited @Params
+        TypeElement walk1 = element;
+        do {
+            for (VariableElement ve : ElementFilter.fieldsIn(walk1.getEnclosedElements())) {
+                if (ve.getAnnotation(Param.class) != null) {
+                    so.addParam(ve);
+                }
+            }
+        } while ((walk1 = (TypeElement) processingEnv.getTypeUtils().asElement(walk1.getSuperclass())) != null);
+
         // walk the type hierarchy up to discover inherited helper methods
-        TypeElement walk = element;
+        TypeElement walk2 = element;
         do {
-            for (ExecutableElement m : ElementFilter.methodsIn(walk.getEnclosedElements())) {
+            for (ExecutableElement m : ElementFilter.methodsIn(walk2.getEnclosedElements())) {
                 Setup setupAnn = m.getAnnotation(Setup.class);
                 if (setupAnn != null) {
                     helpersByState.put(so, new HelperMethodInvocation(m.getSimpleName().toString(), so, setupAnn.value(), HelperType.SETUP));
@@ -214,7 +226,7 @@
                     helpersByState.put(so, new HelperMethodInvocation(m.getSimpleName().toString(), so, tearDownAnn.value(), HelperType.TEARDOWN));
                 }
             }
-        } while ((walk = (TypeElement) processingEnv.getTypeUtils().asElement(walk.getSuperclass())) != null);
+        } while ((walk2 = (TypeElement) processingEnv.getTypeUtils().asElement(walk2.getSuperclass())) != null);
     }
 
     public String getArgList(Element method) {
@@ -373,12 +385,15 @@
             result.add("");
             result.add("static volatile " + so.type + " " + so.fieldIdentifier + ";");
             result.add("");
-            result.add(so.type + " tryInit_" + so.fieldIdentifier + "(" + so.type + " val) throws Throwable {");
+            result.add(so.type + " tryInit_" + so.fieldIdentifier + "(InfraControl control, " + so.type + " val) throws Throwable {");
             result.add("    synchronized(this.getClass()) {");
             result.add("        if (" + so.fieldIdentifier + " == null) {");
             result.add("            " + so.fieldIdentifier + " = val;");
             result.add("        }");
             result.add("        if (!" + so.fieldIdentifier + ".ready" + Level.Trial + ") {");
+            for (String paramName : so.getParamsLabels()) {
+                result.add("            " + so.fieldIdentifier + "." + paramName + " = " + so.getParamAccessor(paramName) + ";");
+            }
             for (HelperMethodInvocation hmi : helpersByState.get(so)) {
                 if (hmi.helperLevel != Level.Trial) continue;
                 if (hmi.type != HelperType.SETUP) continue;
@@ -397,8 +412,11 @@
             result.add("");
             result.add(so.type + " " + so.fieldIdentifier + ";");
             result.add("");
-            result.add(so.type + " tryInit_" + so.fieldIdentifier + "(" + so.type + " val) throws Throwable {");
+            result.add(so.type + " tryInit_" + so.fieldIdentifier + "(InfraControl control, " + so.type + " val) throws Throwable {");
             result.add("    if (" + so.fieldIdentifier + " == null) {");
+            for (String paramName : so.getParamsLabels()) {
+                result.add("                val." + paramName + " = " + so.getParamAccessor(paramName) + ";");
+            }
             for (HelperMethodInvocation hmi : helpersByState.get(so)) {
                 if (hmi.helperLevel != Level.Trial) continue;
                 if (hmi.type != HelperType.SETUP) continue;
@@ -417,7 +435,7 @@
             result.add("");
             result.add("static java.util.Map<Integer, " + so.type + "> " + so.fieldIdentifier + "_map = java.util.Collections.synchronizedMap(new java.util.HashMap<Integer, " + so.type + ">());");
             result.add("");
-            result.add(so.type + " tryInit_" + so.fieldIdentifier + "(int groupId, " + so.type + " val) throws Throwable {");
+            result.add(so.type + " tryInit_" + so.fieldIdentifier + "(InfraControl control, int groupId, " + so.type + " val) throws Throwable {");
             result.add("    synchronized(this.getClass()) {");
             result.add("        " + so.type + " local = " + so.fieldIdentifier + "_map.get(groupId);");
             result.add("        if (local == null) {");
@@ -425,6 +443,9 @@
             result.add("            local = val;");
             result.add("        }");
             result.add("        if (!local.ready" + Level.Trial + ") {");
+            for (String paramName : so.getParamsLabels()) {
+                result.add("            local." + paramName + " = " + so.getParamAccessor(paramName) + ";");
+            }
             for (HelperMethodInvocation hmi : helpersByState.get(so)) {
                 if (hmi.helperLevel != Level.Trial) continue;
                 if (hmi.type != HelperType.SETUP) continue;
@@ -446,10 +467,10 @@
             switch (so.scope) {
                 case Benchmark:
                 case Thread:
-                    result.add(so.type + " " + so.localIdentifier + " = tryInit_" + so.fieldIdentifier + "(new " + so.type + "());");
+                    result.add(so.type + " " + so.localIdentifier + " = tryInit_" + so.fieldIdentifier + "(control, new " + so.type + "());");
                     break;
                 case Group:
-                    result.add(so.type + " " + so.localIdentifier + " = tryInit_" + so.fieldIdentifier + "(threadControl.group, new " + so.type + "());");
+                    result.add(so.type + " " + so.localIdentifier + " = tryInit_" + so.fieldIdentifier + "(control, threadControl.group, new " + so.type + "());");
                     break;
                 default:
                     throw new IllegalStateException("Unhandled scope: " + so.scope);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jmh-core/src/main/java/org/openjdk/jmh/runner/ActualParams.java	Sun Feb 16 18:33:50 2014 +0400
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code 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
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package org.openjdk.jmh.runner;
+
+import java.io.Serializable;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+public class ActualParams implements Comparable<ActualParams>, Serializable {
+
+    private final SortedMap<String, String> params;
+
+    public ActualParams() {
+        params = new TreeMap<String, String>();
+    }
+
+    private ActualParams(SortedMap<String, String> params) {
+        this();
+        this.params.putAll(params);
+    }
+
+    @Override
+    public int compareTo(ActualParams o) {
+        return params.toString().compareTo(o.params.toString());
+    }
+
+    public void put(String k, String v) {
+        params.put(k, v);
+    }
+
+    public boolean containsKey(String name) {
+        return params.containsKey(name);
+    }
+
+    public String get(String name) {
+        return params.get(name);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+
+        ActualParams that = (ActualParams) o;
+
+        if (params != null ? !params.equals(that.params) : that.params != null) return false;
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        return params != null ? params.hashCode() : 0;
+    }
+
+    @Override
+    public String toString() {
+        if (params.isEmpty()) {
+            return "<none>";
+        }
+
+        StringBuilder sb = new StringBuilder();
+        sb.append("(");
+        boolean isFirst = true;
+        for (String k : params.keySet()) {
+            if (isFirst) {
+                isFirst = false;
+            } else {
+                sb.append(", ");
+            }
+            sb.append(k).append(" = ").append(params.get(k));
+        }
+        sb.append(")");
+        return sb.toString();
+    }
+
+    public ActualParams copy() {
+        return new ActualParams(params);
+    }
+}
--- a/jmh-core/src/main/java/org/openjdk/jmh/runner/BenchmarkRecord.java	Fri Feb 14 17:13:31 2014 +0400
+++ b/jmh-core/src/main/java/org/openjdk/jmh/runner/BenchmarkRecord.java	Sun Feb 16 18:33:50 2014 +0400
@@ -32,6 +32,7 @@
 
 import java.io.Serializable;
 import java.util.Collection;
+import java.util.Map;
 import java.util.concurrent.TimeUnit;
 
 public class BenchmarkRecord implements Comparable<BenchmarkRecord>, Serializable {
@@ -54,11 +55,15 @@
     private final Optional<Collection<String>> jvmArgs;
     private final Optional<Collection<String>> jvmArgsPrepend;
     private final Optional<Collection<String>> jvmArgsAppend;
+    private final Optional<Map<String, String[]>> params;
+
+    private ActualParams actualParams;
 
     public BenchmarkRecord(String userName, String generatedName, Mode mode, int[] threadGroups, Optional<Integer> threads,
                            Optional<Integer> warmupIterations, Optional<TimeValue> warmupTime, Optional<Integer> warmupBatchSize,
                            Optional<Integer> measurementIterations, Optional<TimeValue> measurementTime, Optional<Integer> measurementBatchSize,
-                           Optional<Integer> forks, Optional<Integer> warmupForks, Optional<Collection<String>> jvmArgs, Optional<Collection<String>> jvmArgsPrepend, Optional<Collection<String>> jvmArgsAppend) {
+                           Optional<Integer> forks, Optional<Integer> warmupForks, Optional<Collection<String>> jvmArgs, Optional<Collection<String>> jvmArgsPrepend, Optional<Collection<String>> jvmArgsAppend,
+                           Optional<Map<String, String[]>> params) {
         this.userName = userName;
         this.generatedName = generatedName;
         this.mode = mode;
@@ -75,12 +80,13 @@
         this.jvmArgs = jvmArgs;
         this.jvmArgsPrepend = jvmArgsPrepend;
         this.jvmArgsAppend = jvmArgsAppend;
+        this.params = params;
     }
 
     public BenchmarkRecord(String line) {
         String[] args = line.split(BR_SEPARATOR);
 
-        if (args.length != 16) {
+        if (args.length != 17) {
             throw new IllegalStateException("Mismatched format for the line: " + line);
         }
 
@@ -100,12 +106,14 @@
         this.jvmArgs = Optional.of(args[13], Optional.STRING_COLLECTION_UNMARSHALLER);
         this.jvmArgsPrepend = Optional.of(args[14], Optional.STRING_COLLECTION_UNMARSHALLER);
         this.jvmArgsAppend = Optional.of(args[15], Optional.STRING_COLLECTION_UNMARSHALLER);
+        this.params = Optional.of(args[16], Optional.PARAM_COLLECTION_UNMARSHALLER);
     }
 
     public BenchmarkRecord(String userName, String generatedName, Mode mode) {
         this(userName, generatedName, mode, new int[]{}, Optional.<Integer>none(),
                 Optional.<Integer>none(), Optional.<TimeValue>none(), Optional.<Integer>none(), Optional.<Integer>none(), Optional.<TimeValue>none(), Optional.<Integer>none(),
-                Optional.<Integer>none(), Optional.<Integer>none(), Optional.<Collection<String>>none(), Optional.<Collection<String>>none(), Optional.<Collection<String>>none());
+                Optional.<Integer>none(), Optional.<Integer>none(), Optional.<Collection<String>>none(), Optional.<Collection<String>>none(), Optional.<Collection<String>>none(),
+                Optional.<Map<String, String[]>>none());
     }
 
     public String toLine() {
@@ -115,14 +123,28 @@
                 forks + BR_SEPARATOR + warmupForks + BR_SEPARATOR +
                 jvmArgs.toString(Optional.STRING_COLLECTION_MARSHALLER) + BR_SEPARATOR +
                 jvmArgsPrepend.toString(Optional.STRING_COLLECTION_MARSHALLER) + BR_SEPARATOR +
-                jvmArgsAppend.toString(Optional.STRING_COLLECTION_MARSHALLER);
+                jvmArgsAppend.toString(Optional.STRING_COLLECTION_MARSHALLER) + BR_SEPARATOR +
+                params.toString(Optional.PARAM_COLLECTION_MARSHALLER);
     }
 
     public BenchmarkRecord cloneWith(Mode mode) {
         return new BenchmarkRecord(userName, generatedName, mode, threadGroups, threads,
                 warmupIterations, warmupTime, warmupBatchSize,
                 measurementIterations, measurementTime, measurementBatchSize,
-                forks, warmupForks, jvmArgs, jvmArgsPrepend, jvmArgsAppend);
+                forks, warmupForks, jvmArgs, jvmArgsPrepend, jvmArgsAppend, params);
+    }
+
+    public BenchmarkRecord cloneWith(ActualParams p) {
+        BenchmarkRecord br = new BenchmarkRecord(userName, generatedName, mode, threadGroups, threads,
+                warmupIterations, warmupTime, warmupBatchSize,
+                measurementIterations, measurementTime, measurementBatchSize,
+                forks, warmupForks, jvmArgs, jvmArgsPrepend, jvmArgsAppend, params);
+        br.actualParams = p;
+        return br;
+    }
+
+    public ActualParams getActualParams() {
+        return actualParams;
     }
 
     private int[] convert(String[] ss) {
@@ -151,7 +173,16 @@
             return v;
         }
 
-        return userName.compareTo(o.userName);
+        int v1 = userName.compareTo(o.userName);
+        if (v1 != 0) {
+            return v1;
+        }
+
+        if (actualParams == null || o.actualParams == null) {
+            return 0;
+        }
+
+        return actualParams.compareTo(o.actualParams);
     }
 
     @Override
@@ -159,18 +190,20 @@
         if (this == o) return true;
         if (o == null || getClass() != o.getClass()) return false;
 
-        BenchmarkRecord that = (BenchmarkRecord) o;
+        BenchmarkRecord record = (BenchmarkRecord) o;
 
-        if (mode != that.mode) return false;
-        if (!userName.equals(that.userName)) return false;
+        if (mode != record.mode) return false;
+        if (actualParams != null ? !actualParams.equals(record.actualParams) : record.actualParams != null) return false;
+        if (userName != null ? !userName.equals(record.userName) : record.userName != null) return false;
 
         return true;
     }
 
     @Override
     public int hashCode() {
-        int result = userName.hashCode();
-        result = 31 * result + mode.hashCode();
+        int result = userName != null ? userName.hashCode() : 0;
+        result = 31 * result + (mode != null ? mode.hashCode() : 0);
+        result = 31 * result + (actualParams != null ? actualParams.hashCode() : 0);
         return result;
     }
 
@@ -210,6 +243,7 @@
                 "userName='" + userName + '\'' +
                 ", generatedName='" + generatedName + '\'' +
                 ", mode=" + mode +
+                ", actualParams=" + actualParams +
                 '}';
     }
 
@@ -293,4 +327,8 @@
         return (Math.max(1, forks) + warmupForks) * estimatedTimeSingleFork(opts);
     }
 
+    public Optional<Map<String, String[]>> getParams() {
+        return params;
+    }
+
 }
--- a/jmh-core/src/main/java/org/openjdk/jmh/runner/LoopMicroBenchmarkHandler.java	Fri Feb 14 17:13:31 2014 +0400
+++ b/jmh-core/src/main/java/org/openjdk/jmh/runner/LoopMicroBenchmarkHandler.java	Sun Feb 16 18:33:50 2014 +0400
@@ -85,7 +85,7 @@
         // result object to accumulate the results in
         IterationResult iterationResults = new IterationResult(microbenchmark, params);
 
-        InfraControl control = new InfraControl(numThreads, shouldSynchIterations, runtime, preSetupBarrier, preTearDownBarrier, last, timeUnit, params.getBatchSize());
+        InfraControl control = new InfraControl(numThreads, shouldSynchIterations, runtime, preSetupBarrier, preTearDownBarrier, last, timeUnit, params.getBatchSize(), microbenchmark.getActualParams());
 
         // preparing the worker runnables
         BenchmarkTask[] runners = new BenchmarkTask[numThreads];
--- a/jmh-core/src/main/java/org/openjdk/jmh/runner/Runner.java	Fri Feb 14 17:13:31 2014 +0400
+++ b/jmh-core/src/main/java/org/openjdk/jmh/runner/Runner.java	Sun Feb 16 18:33:50 2014 +0400
@@ -183,20 +183,38 @@
         }
 
         // clone with all the modes
-        List<BenchmarkRecord> newBenchmarks = new ArrayList<BenchmarkRecord>();
-        for (BenchmarkRecord br : benchmarks) {
-            if (br.getMode() == Mode.All) {
-                for (Mode mode : Mode.values()) {
-                    if (mode == Mode.All) continue;
-                    newBenchmarks.add(br.cloneWith(mode));
+        {
+            List<BenchmarkRecord> newBenchmarks = new ArrayList<BenchmarkRecord>();
+            for (BenchmarkRecord br : benchmarks) {
+                if (br.getMode() == Mode.All) {
+                    for (Mode mode : Mode.values()) {
+                        if (mode == Mode.All) continue;
+                        newBenchmarks.add(br.cloneWith(mode));
+                    }
+                } else {
+                    newBenchmarks.add(br);
                 }
-            } else {
-                newBenchmarks.add(br);
             }
+
+            benchmarks.clear();
+            benchmarks.addAll(newBenchmarks);
         }
 
-        benchmarks.clear();
-        benchmarks.addAll(newBenchmarks);
+        // clone with all parameters
+        {
+            List<BenchmarkRecord> newBenchmarks = new ArrayList<BenchmarkRecord>();
+            for (BenchmarkRecord br : benchmarks) {
+                if (br.getParams().hasValue()) {
+                    for (ActualParams p : explodeAllParams(br)) {
+                        newBenchmarks.add(br.cloneWith(p));
+                    }
+                } else {
+                    newBenchmarks.add(br);
+                }
+            }
+            benchmarks.clear();
+            benchmarks.addAll(newBenchmarks);
+        }
 
         SortedMap<BenchmarkRecord, RunResult> results = runBenchmarks(benchmarks);
 
@@ -257,6 +275,35 @@
         return result;
     }
 
+    private List<ActualParams> explodeAllParams(BenchmarkRecord br) {
+        Map<String, String[]> benchParams = br.getParams().orElse(Collections.<String, String[]>emptyMap());
+        List<ActualParams> ps = new ArrayList<ActualParams>();
+        List<ActualParams> newPs = new ArrayList<ActualParams>();
+        for (String k : benchParams.keySet()) {
+            Collection<String> values = options.getParameter(k).orElse(Arrays.asList(benchParams.get(k)));
+            if (ps.isEmpty()) {
+                for (String v : values) {
+                    ActualParams al = new ActualParams();
+                    al.put(k, v);
+                    ps.add(al);
+                }
+            } else {
+                for (ActualParams p : ps) {
+                    for (String v : values) {
+                        ActualParams al = p.copy();
+                        al.put(k, v);
+                        newPs.add(al);
+                    }
+                }
+                ps.clear();
+                ps.addAll(newPs);
+            }
+        }
+        return ps;
+    }
+
+
+
     private SortedMap<BenchmarkRecord, RunResult> runBenchmarks(SortedSet<BenchmarkRecord> benchmarks) throws RunnerException {
         out.startRun();
 
--- a/jmh-core/src/main/java/org/openjdk/jmh/runner/options/ChainedOptionsBuilder.java	Fri Feb 14 17:13:31 2014 +0400
+++ b/jmh-core/src/main/java/org/openjdk/jmh/runner/options/ChainedOptionsBuilder.java	Sun Feb 16 18:33:50 2014 +0400
@@ -245,4 +245,12 @@
      */
     ChainedOptionsBuilder detectJvmArgs();
 
+    /**
+     * Set benchmark parameter.
+     * @param name parameter
+     * @param values list of values to set
+     * @return builder
+     */
+    ChainedOptionsBuilder param(String name, String... values);
+
 }
--- a/jmh-core/src/main/java/org/openjdk/jmh/runner/options/CommandLineOptions.java	Fri Feb 14 17:13:31 2014 +0400
+++ b/jmh-core/src/main/java/org/openjdk/jmh/runner/options/CommandLineOptions.java	Sun Feb 16 18:33:50 2014 +0400
@@ -34,6 +34,8 @@
 import org.openjdk.jmh.profile.ProfilerFactory;
 import org.openjdk.jmh.profile.ProfilerType;
 import org.openjdk.jmh.runner.parameters.TimeValue;
+import org.openjdk.jmh.util.internal.HashMultimap;
+import org.openjdk.jmh.util.internal.Multimap;
 import org.openjdk.jmh.util.internal.Optional;
 
 import java.io.IOException;
@@ -82,6 +84,7 @@
     private final List<String> excludes = new ArrayList<String>();
     private final Optional<WarmupMode> warmupMode;
     private final List<String> warmupMicros = new ArrayList<String>();
+    private final Multimap<String, String> params = new HashMultimap<String, String>();
     private final boolean list;
     private final boolean listResultFormats;
     private final boolean help;
@@ -176,6 +179,11 @@
         OptionSpec<String> optExcludes = parser.accepts("e", "Benchmarks to exclude from the run.")
                 .withRequiredArg().withValuesSeparatedBy(',').ofType(String.class).describedAs("regexp+");
 
+        OptionSpec<String> optParams = parser.accepts("p", "Benchmark parameters. This option is expected to be used once" +
+                " per parameter. Parameter name and parameter values should be separated with equals sign." +
+                " Parameter values should be separated with commas.")
+                .withRequiredArg().ofType(String.class).describedAs("param={v,}*");
+
         OptionSpec<String> optWarmupBenchmarks = parser.accepts("wmb", "Warmup benchmarks to include in the run " +
                 "in addition to already selected. JMH will not measure these benchmarks, but only use them" +
                 " for the warmup.")
@@ -385,6 +393,17 @@
             } else {
                 jvmArgs = Optional.none();
             }
+
+            if (set.hasArgument(optParams)) {
+                for (String p : optParams.values(set)) {
+                    String[] keys = p.split("=");
+                    if (keys.length != 2) {
+                        throw new CommandLineOptionException("Unable to parse parameter string \"" + p + "\"");
+                    }
+                    params.putAll(keys[0], Arrays.asList(keys[1].split(",")));
+                }
+            }
+
         } catch (OptionException e) {
             throw new CommandLineOptionException(e.getMessage(), e);
         }
@@ -503,6 +522,16 @@
         return jvmArgs;
     }
 
+    @Override
+    public Optional<Collection<String>> getParameter(String name) {
+        Collection<String> list = params.get(name);
+        if (list == null || list.isEmpty()){
+            return Optional.none();
+        } else {
+            return Optional.of(list);
+        }
+    }
+
     /**
      * Getter
      *
--- a/jmh-core/src/main/java/org/openjdk/jmh/runner/options/Options.java	Fri Feb 14 17:13:31 2014 +0400
+++ b/jmh-core/src/main/java/org/openjdk/jmh/runner/options/Options.java	Sun Feb 16 18:33:50 2014 +0400
@@ -193,4 +193,11 @@
      */
     Optional<Collection<String>> getJvmArgs();
 
+    /**
+     * The overridden value of the parameter.
+     * @param name parameter name
+     * @return parameter
+     */
+    Optional<Collection<String>> getParameter(String name);
+
 }
--- a/jmh-core/src/main/java/org/openjdk/jmh/runner/options/OptionsBuilder.java	Fri Feb 14 17:13:31 2014 +0400
+++ b/jmh-core/src/main/java/org/openjdk/jmh/runner/options/OptionsBuilder.java	Sun Feb 16 18:33:50 2014 +0400
@@ -28,6 +28,8 @@
 import org.openjdk.jmh.output.results.ResultFormatType;
 import org.openjdk.jmh.profile.ProfilerType;
 import org.openjdk.jmh.runner.parameters.TimeValue;
+import org.openjdk.jmh.util.internal.HashMultimap;
+import org.openjdk.jmh.util.internal.Multimap;
 import org.openjdk.jmh.util.internal.Optional;
 
 import java.lang.management.ManagementFactory;
@@ -579,4 +581,28 @@
         List<String> inputArguments = ManagementFactory.getRuntimeMXBean().getInputArguments();
         return jvmArgs(inputArguments.toArray(new String[inputArguments.size()]));
     }
+
+    // ---------------------------------------------------------------------------
+
+    private Multimap<String, String> params = new HashMultimap<String, String>();
+
+    @Override
+    public Optional<Collection<String>> getParameter(String name) {
+        Collection<String> list = params.get(name);
+        if (list == null || list.isEmpty()){
+            if (otherOptions != null) {
+                return otherOptions.getParameter(name);
+            } else {
+                return Optional.none();
+            }
+        } else {
+            return Optional.of(list);
+        }
+    }
+
+    @Override
+    public ChainedOptionsBuilder param(String name, String... values) {
+        params.putAll(name, Arrays.asList(values));
+        return this;
+    }
 }
--- a/jmh-core/src/main/java/org/openjdk/jmh/util/internal/Optional.java	Fri Feb 14 17:13:31 2014 +0400
+++ b/jmh-core/src/main/java/org/openjdk/jmh/util/internal/Optional.java	Sun Feb 16 18:33:50 2014 +0400
@@ -29,6 +29,8 @@
 import java.io.Serializable;
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.Map;
+import java.util.TreeMap;
 
 /**
  * Option class
@@ -183,4 +185,34 @@
         }
     };
 
+    public static final Unmarshaller<Map<String, String[]>> PARAM_COLLECTION_UNMARSHALLER = new Unmarshaller<Map<String, String[]>>() {
+        @Override
+        public Map<String, String[]> valueOf(String s) {
+            Map<String, String[]> map = new TreeMap<String, String[]>();
+            String[] pairs = s.split("===PAIR-SEP===");
+            for (String pair : pairs) {
+                String[] kv = pair.split("===SEP-K===");
+                map.put(kv[0], kv[1].split("===SEP-V==="));
+            }
+            return map;
+        }
+    };
+
+    public static final Marshaller<Map<String, String[]>> PARAM_COLLECTION_MARSHALLER = new Optional.Marshaller<Map<String, String[]>>() {
+        @Override
+        public String valueOf(Map<String, String[]> src) {
+            StringBuilder sb = new StringBuilder();
+            for (String s : src.keySet()) {
+                sb.append(s);
+                sb.append("===SEP-K===");
+                for (String v : src.get(s)) {
+                    sb.append(v);
+                    sb.append("===SEP-V===");
+                }
+                sb.append("===PAIR-SEP===");
+            }
+            return sb.toString();
+        }
+    };
+
 }
--- a/jmh-core/src/test/java/org/openjdk/jmh/runner/options/TestOptions.java	Fri Feb 14 17:13:31 2014 +0400
+++ b/jmh-core/src/test/java/org/openjdk/jmh/runner/options/TestOptions.java	Sun Feb 16 18:33:50 2014 +0400
@@ -36,6 +36,7 @@
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.ObjectOutputStream;
+import java.util.Collection;
 import java.util.concurrent.TimeUnit;
 
 /**
@@ -479,4 +480,25 @@
         Assert.assertEquals(EMPTY_BUILDER.getWarmupBatchSize(), EMPTY_CMDLINE.getWarmupBatchSize());
     }
 
+    @Test
+    public void testParam_Default() {
+        Assert.assertEquals(EMPTY_BUILDER.getParameter("sample"), EMPTY_CMDLINE.getParameter("sample"));
+    }
+
+    @Test
+    public void testParam() throws Exception {
+        CommandLineOptions cmdLine = new CommandLineOptions("-p", "x=1,2,3");
+        Options builder = new OptionsBuilder().param("x", "1", "2", "3").build();
+
+        Collection<String> bp = builder.getParameter("x").get();
+        Collection<String> cp = cmdLine.getParameter("x").get();
+
+        for (String b : bp) {
+            Assert.assertTrue("CP does not contain: " + b, cp.contains(b));
+        }
+        for (String c : cp) {
+            Assert.assertTrue("BP does not contain: " + c, bp.contains(c));
+        }
+    }
+
 }
--- a/jmh-core/src/test/java/org/openjdk/jmh/runner/options/TestParentOptions.java	Fri Feb 14 17:13:31 2014 +0400
+++ b/jmh-core/src/test/java/org/openjdk/jmh/runner/options/TestParentOptions.java	Sun Feb 16 18:33:50 2014 +0400
@@ -32,6 +32,7 @@
 import org.openjdk.jmh.runner.parameters.TimeValue;
 
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.EnumSet;
 import java.util.concurrent.TimeUnit;
 
@@ -520,4 +521,33 @@
         Assert.assertEquals(Arrays.asList(".*test.*", ".*"), builder.getWarmupIncludes());
     }
 
+    @Test
+    public void testParameters_Empty() {
+        Options parent = new OptionsBuilder().build();
+        Options builder = new OptionsBuilder().parent(parent).build();
+        Assert.assertFalse(builder.getParameter("x").hasValue());
+    }
+
+    @Test
+    public void testParameters_Parent() {
+        Options parent = new OptionsBuilder().param("x", "1", "2", "3").build();
+        Options builder = new OptionsBuilder().parent(parent).build();
+        Collection<String> bp = builder.getParameter("x").get();
+        Assert.assertEquals(3, bp.size());
+        for (String b : new String[] {"1", "2", "3"}) {
+            Assert.assertTrue("BP does not contain: " + b, bp.contains(b));
+        }
+    }
+
+    @Test
+    public void testParameters_Merged() {
+        Options parent = new OptionsBuilder().param("x", "1", "2", "3").build();
+        Options builder = new OptionsBuilder().parent(parent).param("x", "4", "5", "6").build();
+        Collection<String> bp = builder.getParameter("x").get();
+        Assert.assertEquals(3, bp.size());
+        for (String b : new String[] {"4", "5", "6"}) {
+            Assert.assertTrue("BP does not contain: " + b, bp.contains(b));
+        }
+    }
+
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jmh-samples/src/main/java/org/openjdk/jmh/samples/JMHSample_27_Params.java	Sun Feb 16 18:33:50 2014 +0400
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code 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
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package org.openjdk.jmh.samples;
+
+import org.openjdk.jmh.annotations.BenchmarkMode;
+import org.openjdk.jmh.annotations.Fork;
+import org.openjdk.jmh.annotations.GenerateMicroBenchmark;
+import org.openjdk.jmh.annotations.Measurement;
+import org.openjdk.jmh.annotations.Mode;
+import org.openjdk.jmh.annotations.OutputTimeUnit;
+import org.openjdk.jmh.annotations.Param;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.State;
+import org.openjdk.jmh.annotations.Warmup;
+import org.openjdk.jmh.runner.Runner;
+import org.openjdk.jmh.runner.RunnerException;
+import org.openjdk.jmh.runner.options.Options;
+import org.openjdk.jmh.runner.options.OptionsBuilder;
+
+import java.math.BigInteger;
+import java.util.concurrent.TimeUnit;
+
+@BenchmarkMode(Mode.AverageTime)
+@OutputTimeUnit(TimeUnit.NANOSECONDS)
+@Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
+@Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
+@Fork(1)
+@State(Scope.Benchmark)
+public class JMHSample_27_Params {
+
+    /**
+     * In many cases, the experiments require walking the configuration space
+     * for a benchmark. This is needed for additional control, or investigating
+     * how the workload performance changes with different settings.
+     */
+
+    @Param({"001", "031", "065", "101", "103"})
+    public int arg;
+
+    @Param({"00", "01", "02", "04", "08", "16", "32"})
+    public int certainty;
+
+    @GenerateMicroBenchmark
+    public boolean bench() {
+        return BigInteger.valueOf(arg).isProbablePrime(certainty);
+    }
+
+    /*
+     * ============================== HOW TO RUN THIS TEST: ====================================
+     *
+     * Note the performance is different with different parameters.
+     *
+     * You can run this test:
+     *
+     * a) Via the command line:
+     *    $ mvn clean install
+     *    $ java -jar target/microbenchmarks.jar ".*JMHSample_27.*"
+     *
+     * b) Via the Java API:
+     */
+
+    public static void main(String[] args) throws RunnerException {
+        Options opt = new OptionsBuilder()
+                .include(".*" + JMHSample_27_Params.class.getSimpleName() + ".*")
+//                .param("arg", "41", "42") // Use this to selectively constrain/override parameters
+                .build();
+
+        new Runner(opt).run();
+    }
+
+}