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