@Param support for enum
Aleksey Shipilev
aleksey.shipilev at oracle.com
Sat May 24 07:50:05 UTC 2014
Hi Joe,
On 05/23/2014 08:19 PM, Joe Kearney wrote:
> Would you be interested in having @Param support enum fields? By
> comparison, Caliper interprets @Param with no args to mean all enum values,
> and (I think) loads specific named enum values @Param("FOO", "BAR").
Yeah, @Param-s for enum are accidentally missing. We can get it back.
> I've found this an easy way of describing the benchmark state space for
> comparative tests of different implementations.
Describing -- yes. But actually implementing them in the enum is an
abuse of the language feature, IMO.
> A workaround in jmh 0.7.3 is for the @Param("FOO", "BAR") field to be a
> String, and set another field using Enum#valueOf() in a @Setup method. You
> can make it use the whole set of enum values with OptionBuilder.param() and
> some munging of the Enum#values() array, but there's no trivial way to do
> it.
I don't think OptionBuilder should specialize for enum values. I
understand the inconvenience of converting Enum#values() into String[]
to put into param(), but it seems wrong to pollute the OptionBuilder
interface for enum values to fit the minor corner case.
> Most importantly, is there a reason that doing something like this would
> contaminate the benchmark results? Is there a worry about the cost of the
> extra method indirection? Perhaps that could be inlined if there's only one
> enum value used per benchmark? I guess a lot of this could be benchmarked
> as well.
Indirection cost is probably not an issue, it will be devirtualized and
inlined. But the problem is, where to get the source data, e.g. to avoid
predictability [1]?
All options I could think of are bad:
a) put the instance fields into enum; but, you will be unable to put
@State over it so that JMH would instantiate different instances for
you, and that will also forbid you to put @Param over its fields to
further specialize the implementation;
b) accept all source data through the method arguments; that will
probably break the encapsulation you want to achieve with enums, and it
would be cleaner to do this without the enum in the first place, i.e.
with the proper interfaces and instantiating the implementors in @Setup.
@Param(enum) may be used to select implementors, e.g.:
@State(Scope.Thread) // notice "impl" is NOT SHARED
public class C {
@Param
private ImplType type;
@Param
private int x;
private Implementation impl;
@Setup
void setup() {
impl = type.create();
x = 23;
}
@GenerateMicroBenchmark
public int bench() {
return impl.doStuff(x);
}
interface Implementation {
void doStuff(int x);
}
class Foo implements Implementation {
int doStuff(int x) {
return x*42;
}
}
class Bar implements Implementation {
int doStuff(int x) {
return x*84;
}
}
enum ImplType {
FOO {
@Override Implementation create() {
return new Foo();
}
},
BAR {
@Override Implementation create() {
return new Bar();
}
},
;
abstract Implementation create();
}
}
...but really, I don't think that is cleaner than:
@State(Scope.Thread)
public class C {
@Param
private String type;
@Param
private int x;
private Implementation impl;
@Setup
void setup() {
switch(type) { // or, do the switch over enums here
case "FOO": impl = new Foo(); break;
case "BAR": impl = new Bar(); break;
default: throw new IllegalArgumentException(type);
}
x = 23;
}
@GenerateMicroBenchmark
public int bench() {
return impl.doStuff(x);
}
interface Implementation {
void doStuff(int x);
}
class Foo implements Implementation {
int doStuff(int x) {
return x*42;
}
}
class Bar implements Implementation {
int doStuff(int x) {
return x*84;
}
}
}
Thanks,
-Aleksey.
[1]
http://hg.openjdk.java.net/code-tools/jmh/file/75f8b23444f6/jmh-samples/src/main/java/org/openjdk/jmh/samples/JMHSample_10_ConstantFold.java
More information about the jmh-dev
mailing list