RFR: updated draft API for JEP 269 Convenience Collection Factories

Vitaly Davidovich vitalyd at gmail.com
Mon Nov 9 15:26:42 UTC 2015


There's another issue with relying on EA, which is EA will not analyze
methods above a certain (bytecode) size; IIRC it's around 150.  So if this
method gets inlined into a bulky method, the varargs will not be
eliminated, I believe.  I don't think it's a problem for this API for the
reasons I mentioned earlier in this thread, but worth considering if EA is
being relied upon.

sent from my phone
On Nov 8, 2015 2:30 PM, "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