RFR: updated draft API for JEP 269 Convenience Collection Factories
Michael Hixson
michael.hixson at gmail.com
Sun Nov 8 23:33:18 UTC 2015
I think the mailing list stripped my attachments, so I put it up on github:
https://github.com/michaelhixson/jmh-benchmark-listof
-Michael
On Sun, Nov 8, 2015 at 2:55 PM, Michael Hixson <michael.hixson at gmail.com> wrote:
> 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