RFR: updated draft API for JEP 269 Convenience Collection Factories
Michael Hixson
michael.hixson at gmail.com
Sun Nov 8 22:55:46 UTC 2015
Hi Peter,
You're right. I see the same thing running that locally. Nice work!
As a sanity check, I changed the code to mirror what I think it would
look like in practice, and to make sure that structure didn't change
any JIT optimizations. The code is attached.
1. use Collections.unmodifiableList(Arrays.asList(...)) like Stuart's
original implementation
2. move the implementations into a separate package-private utility class
3. add a "static List<E> of(E... es)" method to an interface, which
delegates to the utility class
The relative results were the same as yours, in terms of execution
time and GC behavior, including the performance cliff after 7
elements.
-Michael
On Sun, Nov 8, 2015 at 11:30 AM, Peter Levart <peter.levart at gmail.com> wrote:
> Hi Michael,
>
> You see, switch2 is on par with explicit for 0 and 1 args.
>
> switch10 probably suffers from to-many-bytecodes-per-method syndrome.
>
> I tried to shorten your switch2 and switch10 methods by delegating to
> explicit/varargs methods:
>
> @SafeVarargs
> static <E> List<E> varargs_switch2(E... ea) {
> switch (ea.length) {
> case 0: return explicit();
> case 1: return explicit(ea[0]);
> default: return varargs(ea);
> }
> }
>
> @SafeVarargs
> static <E> List<E> varargs_switch10(E... ea) {
> switch (ea.length) {
> case 0: return explicit();
> case 1: return explicit(ea[0]);
> case 2: return explicit(ea[0], ea[1]);
> case 3: return explicit(ea[0], ea[1], ea[2]);
> case 4: return explicit(ea[0], ea[1], ea[2], ea[3]);
> case 5: return explicit(ea[0], ea[1], ea[2], ea[3], ea[4]);
> case 6: return explicit(ea[0], ea[1], ea[2], ea[3], ea[4], ea[5]);
> case 7: return explicit(ea[0], ea[1], ea[2], ea[3], ea[4], ea[5],
> ea[6]);
> case 8: return explicit(ea[0], ea[1], ea[2], ea[3], ea[4], ea[5],
> ea[6], ea[7]);
> case 9: return explicit(ea[0], ea[1], ea[2], ea[3], ea[4], ea[5],
> ea[6], ea[7], ea[8]);
> case 10: return explicit(ea[0], ea[1], ea[2], ea[3], ea[4], ea[5],
> ea[6], ea[7], ea[8], ea[9]);
> default: return varargs(ea);
> }
> }
>
>
> ... and the results are as follows (JDK8u60, i7-2600K, Linux):
>
>
> ListOf.explicit_00 avgt 10
> 2.600 ± 0.022 ns/op
> ListOf.explicit_01 avgt 10
> 4.032 ± 0.237 ns/op
> ListOf.explicit_02 avgt 10
> 7.255 ± 0.457 ns/op
> ListOf.explicit_03 avgt 10
> 7.684 ± 0.485 ns/op
> ListOf.explicit_04 avgt 10
> 8.401 ± 0.803 ns/op
> ListOf.explicit_05 avgt 10
> 9.001 ± 0.771 ns/op
> ListOf.explicit_06 avgt 10
> 9.467 ± 1.008 ns/op
> ListOf.explicit_07 avgt 10
> 10.506 ± 0.774 ns/op
> ListOf.explicit_08 avgt 10
> 11.218 ± 0.946 ns/op
> ListOf.explicit_09 avgt 10
> 12.466 ± 0.735 ns/op
> ListOf.explicit_10 avgt 10
> 13.160 ± 1.680 ns/op
>
> ListOf.varargs_00 avgt 10
> 6.701 ± 0.986 ns/op
> ListOf.varargs_01 avgt 10
> 7.244 ± 0.775 ns/op
> ListOf.varargs_02 avgt 10
> 9.751 ± 0.931 ns/op
> ListOf.varargs_03 avgt 10
> 10.730 ± 1.055 ns/op
> ListOf.varargs_04 avgt 10
> 11.615 ± 0.995 ns/op
> ListOf.varargs_05 avgt 10
> 12.923 ± 1.057 ns/op
> ListOf.varargs_06 avgt 10
> 12.688 ± 0.963 ns/op
> ListOf.varargs_07 avgt 10
> 14.509 ± 0.964 ns/op
> ListOf.varargs_08 avgt 10
> 15.214 ± 1.613 ns/op
> ListOf.varargs_09 avgt 10
> 15.796 ± 0.099 ns/op
> ListOf.varargs_10 avgt 10
> 17.381 ± 2.089 ns/op
>
> ListOf.varargs_switch10_00 avgt 10
> 2.598 ± 0.031 ns/op
> ListOf.varargs_switch10_01 avgt 10
> 4.200 ± 0.377 ns/op
> ListOf.varargs_switch10_02 avgt 10
> 6.829 ± 0.445 ns/op
> ListOf.varargs_switch10_03 avgt 10
> 8.074 ± 1.230 ns/op
> ListOf.varargs_switch10_04 avgt 10
> 8.254 ± 0.644 ns/op
> ListOf.varargs_switch10_05 avgt 10
> 9.955 ± 1.643 ns/op
> ListOf.varargs_switch10_06 avgt 10
> 9.856 ± 1.172 ns/op
> ListOf.varargs_switch10_07 avgt 10
> 11.230 ± 1.182 ns/op
> ListOf.varargs_switch10_08 avgt 10
> 17.431 ± 2.253 ns/op
> ListOf.varargs_switch10_09 avgt 10
> 18.439 ± 2.199 ns/op
> ListOf.varargs_switch10_10 avgt 10
> 20.477 ± 2.698 ns/op
>
> ListOf.varargs_switch2_00 avgt 10
> 2.598 ± 0.031 ns/op
> ListOf.varargs_switch2_01 avgt 10
> 4.306 ± 0.461 ns/op
> ListOf.varargs_switch2_02 avgt 10
> 9.561 ± 0.978 ns/op
> ListOf.varargs_switch2_03 avgt 10
> 10.859 ± 0.596 ns/op
> ListOf.varargs_switch2_04 avgt 10
> 11.810 ± 0.812 ns/op
> ListOf.varargs_switch2_05 avgt 10
> 13.451 ± 1.819 ns/op
> ListOf.varargs_switch2_06 avgt 10
> 12.310 ± 0.106 ns/op
> ListOf.varargs_switch2_07 avgt 10
> 15.589 ± 2.335 ns/op
> ListOf.varargs_switch2_08 avgt 10
> 15.177 ± 0.963 ns/op
> ListOf.varargs_switch2_09 avgt 10
> 18.139 ± 1.968 ns/op
> ListOf.varargs_switch2_10 avgt 10
> 16.802 ± 1.193 ns/op
>
>
> Now switch10 performs on par with explicit up to 7 arguments after which
> there's a cliff.
>
> switch2 has a cliff where expected, when it starts to delegate to varargs.
>
> More interesting, when profiling with "-prof gc", we can see that:
>
>
> ListOf.explicit_00:·gc.alloc.rate.norm avgt 10 ≈
> 10⁻⁶ B/op
> ListOf.explicit_01:·gc.alloc.rate.norm avgt 10
> 24.000 ± 0.001 B/op
> ListOf.explicit_02:·gc.alloc.rate.norm avgt 10
> 48.000 ± 0.001 B/op
> ListOf.explicit_03:·gc.alloc.rate.norm avgt 10
> 56.000 ± 0.001 B/op
> ListOf.explicit_04:·gc.alloc.rate.norm avgt 10
> 56.000 ± 0.001 B/op
> ListOf.explicit_05:·gc.alloc.rate.norm avgt 10
> 64.000 ± 0.001 B/op
> ListOf.explicit_06:·gc.alloc.rate.norm avgt 10
> 64.000 ± 0.001 B/op
> ListOf.explicit_07:·gc.alloc.rate.norm avgt 10
> 72.000 ± 0.001 B/op
> ListOf.explicit_08:·gc.alloc.rate.norm avgt 10
> 72.000 ± 0.001 B/op
> ListOf.explicit_09:·gc.alloc.rate.norm avgt 10
> 80.000 ± 0.001 B/op
> ListOf.explicit_10:·gc.alloc.rate.norm avgt 10
> 80.000 ± 0.001 B/op
>
> ListOf.varargs_00:·gc.alloc.rate.norm avgt 10
> 40.000 ± 0.001 B/op
> ListOf.varargs_01:·gc.alloc.rate.norm avgt 10
> 48.000 ± 0.001 B/op
> ListOf.varargs_02:·gc.alloc.rate.norm avgt 10
> 72.000 ± 0.001 B/op
> ListOf.varargs_03:·gc.alloc.rate.norm avgt 10
> 88.000 ± 0.001 B/op
> ListOf.varargs_04:·gc.alloc.rate.norm avgt 10
> 88.000 ± 0.001 B/op
> ListOf.varargs_05:·gc.alloc.rate.norm avgt 10
> 104.000 ± 0.001 B/op
> ListOf.varargs_06:·gc.alloc.rate.norm avgt 10
> 104.000 ± 0.001 B/op
> ListOf.varargs_07:·gc.alloc.rate.norm avgt 10
> 120.000 ± 0.001 B/op
> ListOf.varargs_08:·gc.alloc.rate.norm avgt 10
> 120.000 ± 0.001 B/op
> ListOf.varargs_09:·gc.alloc.rate.norm avgt 10
> 136.000 ± 0.001 B/op
> ListOf.varargs_10:·gc.alloc.rate.norm avgt 10
> 136.000 ± 0.001 B/op
>
> ListOf.varargs_switch10_00:·gc.alloc.rate.norm avgt 10 ≈
> 10⁻⁶ B/op
> ListOf.varargs_switch10_01:·gc.alloc.rate.norm avgt 10
> 24.000 ± 0.001 B/op
> ListOf.varargs_switch10_02:·gc.alloc.rate.norm avgt 10
> 48.000 ± 0.001 B/op
> ListOf.varargs_switch10_03:·gc.alloc.rate.norm avgt 10
> 56.000 ± 0.001 B/op
> ListOf.varargs_switch10_04:·gc.alloc.rate.norm avgt 10
> 56.000 ± 0.001 B/op
> ListOf.varargs_switch10_05:·gc.alloc.rate.norm avgt 10
> 64.000 ± 0.001 B/op
> ListOf.varargs_switch10_06:·gc.alloc.rate.norm avgt 10
> 64.000 ± 0.001 B/op
> ListOf.varargs_switch10_07:·gc.alloc.rate.norm avgt 10
> 72.000 ± 0.001 B/op
> ListOf.varargs_switch10_08:·gc.alloc.rate.norm avgt 10
> 120.000 ± 0.001 B/op
> ListOf.varargs_switch10_09:·gc.alloc.rate.norm avgt 10
> 136.000 ± 0.001 B/op
> ListOf.varargs_switch10_10:·gc.alloc.rate.norm avgt 10
> 136.000 ± 0.001 B/op
>
> ListOf.varargs_switch2_00:·gc.alloc.rate.norm avgt 10 ≈
> 10⁻⁶ B/op
> ListOf.varargs_switch2_01:·gc.alloc.rate.norm avgt 10
> 24.000 ± 0.001 B/op
> ListOf.varargs_switch2_02:·gc.alloc.rate.norm avgt 10
> 72.000 ± 0.001 B/op
> ListOf.varargs_switch2_03:·gc.alloc.rate.norm avgt 10
> 88.000 ± 0.001 B/op
> ListOf.varargs_switch2_04:·gc.alloc.rate.norm avgt 10
> 88.000 ± 0.001 B/op
> ListOf.varargs_switch2_05:·gc.alloc.rate.norm avgt 10
> 104.000 ± 0.001 B/op
> ListOf.varargs_switch2_06:·gc.alloc.rate.norm avgt 10
> 104.000 ± 0.001 B/op
> ListOf.varargs_switch2_07:·gc.alloc.rate.norm avgt 10
> 120.000 ± 0.001 B/op
> ListOf.varargs_switch2_08:·gc.alloc.rate.norm avgt 10
> 120.000 ± 0.001 B/op
> ListOf.varargs_switch2_09:·gc.alloc.rate.norm avgt 10
> 136.000 ± 0.001 B/op
> ListOf.varargs_switch2_10:·gc.alloc.rate.norm avgt 10
> 136.000 ± 0.001 B/op
>
>
> switch10 allocates exactly the same as explicit up to 7 arguments, after
> that it seems that JIT doesn't want to optimize away the allocation of the
> vararg array.
>
>
> So it looks like that there's no need to burden the public API with explicit
> argument overloads.
>
> Regards, Peter
>
>
>
> On 11/08/2015 12:44 AM, Michael Hixson wrote:
>
> Hi Peter,
>
> I've attached the source code and JMH output with two new tests:
>
> 1. varargs_switch2: Has a switch with cases for 0 and 1 that invoke
> Collections.emptyList/singletonList.
> 2. varargs_switch10: Has a switch with ten cases that look exactly
> like the explicit versions.
>
> Both have a default case that falls back to System.arraycopy.
>
> switch2 seems like a more sane implementation and it performs better
> for 0 or 1 arguments. For larger numbers of arguments, they're
> roughly equivalent.
>
> Here is the end snippet from the attached results:
>
>
> Benchmark Mode Cnt Score Error Units
> ListOf.explicit_00 avgt 40 2.550 ± 0.008 ns/op
> ListOf.explicit_01 avgt 40 6.929 ± 0.100 ns/op
> ListOf.explicit_02 avgt 40 15.011 ± 1.410 ns/op
> ListOf.explicit_03 avgt 40 16.203 ± 0.396 ns/op
> ListOf.explicit_04 avgt 40 16.397 ± 0.505 ns/op
> ListOf.explicit_05 avgt 40 18.252 ± 0.229 ns/op
> ListOf.explicit_06 avgt 40 18.623 ± 0.499 ns/op
> ListOf.explicit_07 avgt 40 20.614 ± 0.231 ns/op
> ListOf.explicit_08 avgt 40 20.792 ± 0.259 ns/op
> ListOf.explicit_09 avgt 40 22.998 ± 0.350 ns/op
> ListOf.explicit_10 avgt 40 23.105 ± 0.346 ns/op
> ListOf.varargs_00 avgt 40 11.363 ± 0.131 ns/op
> ListOf.varargs_01 avgt 40 20.877 ± 0.440 ns/op
> ListOf.varargs_02 avgt 40 20.004 ± 0.130 ns/op
> ListOf.varargs_03 avgt 40 24.264 ± 0.091 ns/op
> ListOf.varargs_04 avgt 40 24.309 ± 0.120 ns/op
> ListOf.varargs_05 avgt 40 28.936 ± 0.503 ns/op
> ListOf.varargs_06 avgt 40 28.676 ± 0.052 ns/op
> ListOf.varargs_07 avgt 40 33.858 ± 1.409 ns/op
> ListOf.varargs_08 avgt 40 33.685 ± 0.763 ns/op
> ListOf.varargs_09 avgt 40 37.639 ± 0.207 ns/op
> ListOf.varargs_10 avgt 40 39.188 ± 1.577 ns/op
> ListOf.varargs_switch10_00 avgt 40 5.575 ± 0.140 ns/op
> ListOf.varargs_switch10_01 avgt 40 13.561 ± 0.353 ns/op
> ListOf.varargs_switch10_02 avgt 40 20.145 ± 0.328 ns/op
> ListOf.varargs_switch10_03 avgt 40 24.632 ± 0.121 ns/op
> ListOf.varargs_switch10_04 avgt 40 25.360 ± 0.941 ns/op
> ListOf.varargs_switch10_05 avgt 40 28.977 ± 0.336 ns/op
> ListOf.varargs_switch10_06 avgt 40 30.471 ± 1.797 ns/op
> ListOf.varargs_switch10_07 avgt 40 33.701 ± 0.128 ns/op
> ListOf.varargs_switch10_08 avgt 40 33.737 ± 0.357 ns/op
> ListOf.varargs_switch10_09 avgt 40 38.638 ± 1.564 ns/op
> ListOf.varargs_switch10_10 avgt 40 38.042 ± 0.090 ns/op
> ListOf.varargs_switch2_00 avgt 40 2.543 ± 0.006 ns/op
> ListOf.varargs_switch2_01 avgt 40 7.069 ± 0.366 ns/op
> ListOf.varargs_switch2_02 avgt 40 20.564 ± 0.739 ns/op
> ListOf.varargs_switch2_03 avgt 40 24.453 ± 0.362 ns/op
> ListOf.varargs_switch2_04 avgt 40 24.706 ± 0.587 ns/op
> ListOf.varargs_switch2_05 avgt 40 29.451 ± 0.917 ns/op
> ListOf.varargs_switch2_06 avgt 40 28.856 ± 0.126 ns/op
> ListOf.varargs_switch2_07 avgt 40 33.300 ± 0.066 ns/op
> ListOf.varargs_switch2_08 avgt 40 36.306 ± 3.242 ns/op
> ListOf.varargs_switch2_09 avgt 40 40.739 ± 1.837 ns/op
> ListOf.varargs_switch2_10 avgt 40 37.876 ± 0.114 ns/op
>
>
> -Michael
>
> On Sat, Nov 7, 2015 at 7:41 AM, Peter Levart <peter.levart at gmail.com> wrote:
>
> Hi Michael,
>
> The comparison between explicit and varargs is not fair. Varargs is using
> arraycopy, which I think prevents vararg array allocation to be eliminated.
> Try to use a switch on varargs array length and then directly reference it's
> elements with constant indices for each case and construct list arrays as
> you do in explicit methods. Let's see if this performs any better.
>
> Regards, Peter
>
> On Nov 7, 2015 9:43 AM, "Michael Hixson" <michael.hixson at gmail.com> wrote:
>
> (Oops, forgot to cc the mailing list)
>
> Thanks for the explanations, Stuart. That all sounds reasonable and
> makes sense to me.
>
> I have some additional thoughts inline below, because this is
> interesting and I can't resist, but you could ignore them and not hurt
> any feelings.
>
> I also wrote up some quick benchmarks comparing explicit versus
> varargs implementations just to see the impact for myself. The output
> and source code are included at the end of the email.
>
> -Michael
>
>
> On Fri, Nov 6, 2015 at 10:28 AM, Stuart Marks <stuart.marks at oracle.com>
> wrote:
>
> On 11/6/15 5:12 AM, Michael Hixson wrote:
>
> + static <E> List<E> of(E... es) {
> + for (E e : es) {
> + Objects.requireNonNull(e);
> + }
> + // NOTE: this can allow a null element to slip through
> + return Collections.unmodifiableList(Arrays.asList(es));
> + }
>
> Even as a skeletal implementation, this one has to be changed to be
> truly immutable, right? It currently returns a view of the (mutable)
> argument array rather than new storage. Sorry for not providing a
> proper test:
>
> Good catch! Funnily I had noticed the TOCTOU case that allowed null
> elements
> in the array to slip through, but not that the array itself was still
> modifiable from the outside. Anyway, I'll fix this. No worries about the
> test.
>
> Has anyone been able to quantify the advantage of having these
> overloads as opposed to having the varargs versions only? Is it a
> matter of performance?
>
> I ask because the overloads seem like warts on the APIs (which is a
> shame -- List and Set are such important APIs). I'm imagining a
> future where:
>
> 1. We add these overloads for performance gains now.
> 2. But they're all skeletal implementations that aren't that perfomant
> anyway. Efficient versions don't make it into Java SE 9. People that
> care a lot about performance avoid using these ones.
> 3. A few years later, varargs performance or some other language / VM
> / compiler-level change renders the overloads obsolete.
>
> Yeah, the overloads seem like warts on the API, though probably
> necessary
> ones.
>
> At present, and for the forseeable future, varargs calls allocate an
> array
> on the heap, whereas fixed-args calls do not. I don't know how to
> quantify
> the difference though. Certainly the cost of allocation and
> initialization
> is borne in-line. Then there is the cost of collection. Collecting
> short-lived objects is cheap (but not free). There is also the
> possibility
> of escape analysis eliminating the allocation. This seems unlikely to
> me;
> certainly not something to be relied upon.
>
> The most likely possible future optimization is "frozen arrays," part of
> the
> "Arrays 2.0" stuff that John Rose has talked about. This is basically
> about
> immutable arrays. Here, the possibility is to eliminate the defensive
> copy,
> if the array created to hold the varargs arguments is made immutable.
> (This
> will require some adjustment on the callee side, as yet unspecified.)
> There's still an array, though. And a defensive copy would still have to
> be
> made if the caller passes an actual array, as opposed to a varargs list.
>
> (Realizing that we're discussing details of a feature that doesn't
> exist (frozen arrays)...)
>
> It seems to me that as long as the callee invoked the method with
> comma-separated arguments instead of an array, then the callee can
> automatically be opted into frozen arrays. They never had access to
> the array box in the first place.
>
> It also seems like the varargs method could defensively call
> array.clone() and expect a no-op (return this) implementation if the
> array was already frozen, and so both sides could automatically
> benefit from frozen arrays without recompilation. No?
>
> While I can't quantify it, I do think there's an expense to creating the
> varargs array, and there is only a possibility to reduce (but not
> eliminate)
> its cost in future JDK releases. This cost is entirely avoided by
> fixed-args
> overloads. (There is the cost of cluttering up the API, though.)
>
> I asked "Is it a matter of performance?" because I thought the
> justification for similar overloads in other APIs was different. I
> thought EnumSet and Guava (for example) provided the overloads because
> @SafeVarargs did not exist at the time, and they didn't want to scare
> callers away with those silly warnings.
>
> Put another way: if the justification for these new overloads is
> simply "the other APIs did it", I hope those original motivations are
> not being wrongly applied here. It sounds like this is strictly about
> performance, though.
>
> Turning to the skeletal vs. optimized implementation, my plan is
> certainly
> to ensure that the optimized implementations get into JDK 9. Of course,
> plans can change. If the APIs get in without the optimized
> implementations,
> I think the big attractor will still be the convenience of using these
> static factory methods as opposed to conventional code. They're no
> slower
> than conventional code, and the space consumed is the same. So I think
> they'll be popular even if the space efficiency benefits aren't there
> initially.
>
> For some reason I thought the optimized implementations had already
> been moved out of scope for Java 9. I'm glad I was wrong!
>
> When the optimized implementations do get in, callers will benefit, even
> without recompilation. Thus there is some present value added based on
> potential future benefits.
>
> There is always the set of possible future events that cause something
> not
> to work out, but I think pursuing the approach I've outlined has a good
> chance of benefiting the platform in the long term.
>
> s'marks
>
> ----------------------------------------
>
> Benchmark Mode Cnt Score Error Units
> ListOf.explicit_00 avgt 40 2.564 ± 0.007 ns/op
> ListOf.explicit_01 avgt 40 7.859 ± 0.022 ns/op
> ListOf.explicit_02 avgt 40 15.808 ± 0.338 ns/op
> ListOf.explicit_03 avgt 40 19.145 ± 0.978 ns/op
> ListOf.explicit_04 avgt 40 18.558 ± 0.314 ns/op
> ListOf.explicit_05 avgt 40 23.457 ± 1.069 ns/op
> ListOf.explicit_06 avgt 40 21.398 ± 0.255 ns/op
> ListOf.explicit_07 avgt 40 25.307 ± 0.672 ns/op
> ListOf.explicit_08 avgt 40 24.137 ± 0.376 ns/op
> ListOf.explicit_09 avgt 40 27.418 ± 0.560 ns/op
> ListOf.explicit_10 avgt 40 26.871 ± 0.506 ns/op
> ListOf.varargs_00 avgt 40 13.520 ± 0.177 ns/op
> ListOf.varargs_01 avgt 40 23.740 ± 0.346 ns/op
> ListOf.varargs_02 avgt 40 23.435 ± 0.321 ns/op
> ListOf.varargs_03 avgt 40 29.564 ± 0.744 ns/op
> ListOf.varargs_04 avgt 40 29.640 ± 1.329 ns/op
> ListOf.varargs_05 avgt 40 34.552 ± 0.639 ns/op
> ListOf.varargs_06 avgt 40 34.249 ± 0.476 ns/op
> ListOf.varargs_07 avgt 40 40.656 ± 0.589 ns/op
> ListOf.varargs_08 avgt 40 39.900 ± 0.595 ns/op
> ListOf.varargs_09 avgt 40 45.060 ± 1.098 ns/op
> ListOf.varargs_10 avgt 40 44.546 ± 0.816 ns/op
>
> ----------------------------------------
>
> package rnd;
>
> import org.openjdk.jmh.annotations.Benchmark;
> import org.openjdk.jmh.annotations.BenchmarkMode;
> import org.openjdk.jmh.annotations.Fork;
> import org.openjdk.jmh.annotations.Measurement;
> import org.openjdk.jmh.annotations.Mode;
> import org.openjdk.jmh.annotations.OutputTimeUnit;
> 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.options.Options;
> import org.openjdk.jmh.runner.options.OptionsBuilder;
>
> import java.util.AbstractList;
> import java.util.Collections;
> import java.util.List;
> import java.util.Objects;
> import java.util.concurrent.TimeUnit;
>
> @State(Scope.Thread)
> @BenchmarkMode(Mode.AverageTime)
> @OutputTimeUnit(TimeUnit.NANOSECONDS)
> @Warmup(iterations = 20)
> @Measurement(iterations = 20)
> @Fork(2)
> public class ListOf {
>
> private static final String o = "";
>
> public static void main(String[] args) throws Exception {
> Options options = new OptionsBuilder()
> .include(ListOf.class.getName())
> .build();
> new Runner(options).run();
> }
>
> @Benchmark public List<String> explicit_00() { return explicit(); }
> @Benchmark public List<String> explicit_01() { return explicit(o); }
> @Benchmark public List<String> explicit_02() { return explicit(o,o); }
> @Benchmark public List<String> explicit_03() { return explicit(o,o,o); }
> @Benchmark public List<String> explicit_04() { return explicit(o,o,o,o);
> }
> @Benchmark public List<String> explicit_05() { return
> explicit(o,o,o,o,o); }
> @Benchmark public List<String> explicit_06() { return
> explicit(o,o,o,o,o,o); }
> @Benchmark public List<String> explicit_07() { return
> explicit(o,o,o,o,o,o,o); }
> @Benchmark public List<String> explicit_08() { return
> explicit(o,o,o,o,o,o,o,o); }
> @Benchmark public List<String> explicit_09() { return
> explicit(o,o,o,o,o,o,o,o,o); }
> @Benchmark public List<String> explicit_10() { return
> explicit(o,o,o,o,o,o,o,o,o,o); }
>
> @Benchmark public List<String> varargs_00() { return varargs(); }
> @Benchmark public List<String> varargs_01() { return varargs(o); }
> @Benchmark public List<String> varargs_02() { return varargs(o,o); }
> @Benchmark public List<String> varargs_03() { return varargs(o,o,o); }
> @Benchmark public List<String> varargs_04() { return varargs(o,o,o,o); }
> @Benchmark public List<String> varargs_05() { return varargs(o,o,o,o,o);
> }
> @Benchmark public List<String> varargs_06() { return
> varargs(o,o,o,o,o,o); }
> @Benchmark public List<String> varargs_07() { return
> varargs(o,o,o,o,o,o,o); }
> @Benchmark public List<String> varargs_08() { return
> varargs(o,o,o,o,o,o,o,o); }
> @Benchmark public List<String> varargs_09() { return
> varargs(o,o,o,o,o,o,o,o,o); }
> @Benchmark public List<String> varargs_10() { return
> varargs(o,o,o,o,o,o,o,o,o,o); }
>
> static <E> List<E> explicit() {
> return Collections.emptyList();
> }
>
> static <E> List<E> explicit(E e1) {
> return Collections.singletonList(Objects.requireNonNull(e1));
> }
>
> static <E> List<E> explicit(E e1, E e2) {
> return new ImmutableList<>(new Object[] {
> Objects.requireNonNull(e1),
> Objects.requireNonNull(e2)
> });
> }
>
> static <E> List<E> explicit(E e1, E e2, E e3) {
> return new ImmutableList<>(new Object[] {
> Objects.requireNonNull(e1),
> Objects.requireNonNull(e2),
> Objects.requireNonNull(e3)
> });
> }
>
> static <E> List<E> explicit(E e1, E e2, E e3, E e4) {
> return new ImmutableList<>(new Object[] {
> Objects.requireNonNull(e1),
> Objects.requireNonNull(e2),
> Objects.requireNonNull(e3),
> Objects.requireNonNull(e4)
> });
> }
>
> static <E> List<E> explicit(E e1, E e2, E e3, E e4, E e5) {
> return new ImmutableList<>(new Object[] {
> Objects.requireNonNull(e1),
> Objects.requireNonNull(e2),
> Objects.requireNonNull(e3),
> Objects.requireNonNull(e4),
> Objects.requireNonNull(e5)
> });
> }
>
> static <E> List<E> explicit(E e1, E e2, E e3, E e4, E e5, E e6) {
> return new ImmutableList<>(new Object[] {
> Objects.requireNonNull(e1),
> Objects.requireNonNull(e2),
> Objects.requireNonNull(e3),
> Objects.requireNonNull(e4),
> Objects.requireNonNull(e5),
> Objects.requireNonNull(e6)
> });
> }
>
> static <E> List<E> explicit(E e1, E e2, E e3, E e4, E e5, E e6, E e7) {
> return new ImmutableList<>(new Object[] {
> Objects.requireNonNull(e1),
> Objects.requireNonNull(e2),
> Objects.requireNonNull(e3),
> Objects.requireNonNull(e4),
> Objects.requireNonNull(e5),
> Objects.requireNonNull(e6),
> Objects.requireNonNull(e7)
> });
> }
>
> static <E> List<E> explicit(E e1, E e2, E e3, E e4, E e5, E e6, E e7, E
> e8) {
> return new ImmutableList<>(new Object[] {
> Objects.requireNonNull(e1),
> Objects.requireNonNull(e2),
> Objects.requireNonNull(e3),
> Objects.requireNonNull(e4),
> Objects.requireNonNull(e5),
> Objects.requireNonNull(e6),
> Objects.requireNonNull(e7),
> Objects.requireNonNull(e8)
> });
> }
>
> static <E> List<E> explicit(E e1, E e2, E e3, E e4, E e5, E e6, E
> e7, E e8, E e9) {
> return new ImmutableList<>(new Object[] {
> Objects.requireNonNull(e1),
> Objects.requireNonNull(e2),
> Objects.requireNonNull(e3),
> Objects.requireNonNull(e4),
> Objects.requireNonNull(e5),
> Objects.requireNonNull(e6),
> Objects.requireNonNull(e7),
> Objects.requireNonNull(e8),
> Objects.requireNonNull(e9)
> });
> }
>
> static <E> List<E> explicit(E e1, E e2, E e3, E e4, E e5, E e6, E
> e7, E e8, E e9, E e10) {
> return new ImmutableList<>(new Object[] {
> Objects.requireNonNull(e1),
> Objects.requireNonNull(e2),
> Objects.requireNonNull(e3),
> Objects.requireNonNull(e4),
> Objects.requireNonNull(e5),
> Objects.requireNonNull(e6),
> Objects.requireNonNull(e7),
> Objects.requireNonNull(e8),
> Objects.requireNonNull(e9),
> Objects.requireNonNull(e10)
> });
> }
>
> @SafeVarargs
> static <E> List<E> varargs(E... elements) {
> int length = elements.length;
> Object[] copy = new Object[length];
> System.arraycopy(elements, 0, copy, 0, length);
> for (Object e : copy) Objects.requireNonNull(e);
> return new ImmutableList<>(copy);
> }
>
> static final class ImmutableList<E> extends AbstractList<E> {
> final Object[] array;
>
> ImmutableList(Object[] array) {
> this.array = array;
> }
>
> @Override
> @SuppressWarnings("unchecked")
> public E get(int index) {
> return (E) array[index];
> }
>
> @Override
> public int size() {
> return array.length;
> }
> }
> }
>
>
More information about the core-libs-dev
mailing list