/* * Copyright (c) 2014, Oracle America, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * * Neither the name of Oracle nor the names of its contributors may be used * to endorse or promote products derived from this software without * specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ package org.openjdk; import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.BenchmarkMode; import org.openjdk.jmh.annotations.CompilerControl; 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 java.util.concurrent.TimeUnit; @BenchmarkMode(Mode.AverageTime) @OutputTimeUnit(TimeUnit.NANOSECONDS) @Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) @Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) @Fork(5) @State(Scope.Thread) public class InitStoreCoalesce { /* Runs are done on 1x4x2 i7-4790K (Haswell) @ 4.0 GHz; jdk9-dev on 06-Mar-2015, Linux x86_64. Out-of-box: Benchmark Mode Cnt Score Error Units InitStoreCoalesce.copy avgt 25 5.428 ± 0.071 ns/op InitStoreCoalesce.copyBaited avgt 25 5.775 ± 0.037 ns/op InitStoreCoalesce.instant avgt 25 4.993 ± 0.067 ns/op -XX:-UseCompressedOops: Benchmark Mode Cnt Score Error Units InitStoreCoalesce.copy avgt 25 5.234 ± 0.055 ns/op InitStoreCoalesce.copyBaited avgt 25 5.244 ± 0.029 ns/op InitStoreCoalesce.instant avgt 25 4.862 ± 0.022 ns/op The disassemblies below are done with "-prof perfasm" and -XX:-UseCompressedOops". */ long x1, x2, x3, x4; MyClass inst = new MyClass(x1, x2, x3, x4); /* instant() method just copies over the given benchmark fields into the new instance fields, no pre-zeroing. 0.79% 0.79% 0x00007fe620aec41c: movabs $0x7fe40beb0210,%r10 ; {metadata('org/openjdk/InitStoreCoalesce$MyClass')} 0.35% 0.21% 0x00007fe620aec426: mov 0xa8(%r10),%r11 0.93% 1.12% 0x00007fe620aec42d: mov %r11,(%rax) 3.10% 4.10% 0x00007fe620aec430: mov %r10,0x8(%rax) ;*new ; - org.openjdk.InitStoreCoalesce::instant@0 (line 95) 0.79% 0.56% 0x00007fe620aec434: mov 0x10(%rbp),%r10 ;*getfield x1 ; - org.openjdk.InitStoreCoalesce::instant@5 (line 95) 0.48% 0.49% 0x00007fe620aec438: mov %r10,0x10(%rax) ;*putfield x1 ; - org.openjdk.InitStoreCoalesce$MyClass::<init>@6 (line 151) ; - org.openjdk.InitStoreCoalesce::instant@20 (line 95) 1.28% 1.12% 0x00007fe620aec43c: mov 0x18(%rbp),%r10 ;*getfield x2 ; - org.openjdk.InitStoreCoalesce::instant@9 (line 95) 2.68% 3.76% 0x00007fe620aec440: mov %r10,0x18(%rax) ;*putfield x2 ; - org.openjdk.InitStoreCoalesce$MyClass::<init>@11 (line 152) ; - org.openjdk.InitStoreCoalesce::instant@20 (line 95) 3.83% 2.05% 0x00007fe620aec444: mov 0x20(%rbp),%r10 ;*getfield x3 ; - org.openjdk.InitStoreCoalesce::instant@13 (line 95) 0.36% 0.51% 0x00007fe620aec448: mov %r10,0x20(%rax) ;*putfield x3 ; - org.openjdk.InitStoreCoalesce$MyClass::<init>@17 (line 153) ; - org.openjdk.InitStoreCoalesce::instant@20 (line 95) 1.25% 1.81% 0x00007fe620aec44c: mov 0x28(%rbp),%r10 ;*getfield x4 ; - org.openjdk.InitStoreCoalesce::instant@17 (line 95) 2.24% 2.72% 0x00007fe620aec450: mov %r10,0x28(%rax) ;*synchronization entry ; - org.openjdk.InitStoreCoalesce::instant@-1 (line 95) 3.72% 2.08% 0x00007fe620aec454: add $0x10,%rsp 0.29% 0.31% 0x00007fe620aec458: pop %rbp 1.09% 1.09% 0x00007fe620aec459: test %eax,0xf2f8ba1(%rip) # 0x00007fe62fde5000 ; {poll_return} 3.53% 2.95% 0x00007fe620aec45f: retq */ @Benchmark @CompilerControl(CompilerControl.Mode.DONT_INLINE) public MyClass instant() { return new MyClass(x1, x2, x3, x4); } /* copy() does the pre-zeroing first, and *then* copies over the benchmark fields into the new instance, thus doing double work. Allegedly, this is done to have default values in the fields once we fired NPE. But, how could anyone observe the partially constructed instance without leaking "this" first, or doing some other instrumentation? Note the implicit nullcheck on "inst.x1" access. 0.92% 1.03% 0x00007f33a7a4f49c: movabs $0x7f31a63d0210,%r10 ; {metadata('org/openjdk/InitStoreCoalesce$MyClass')} 0.44% 0.36% 0x00007f33a7a4f4a6: mov 0xa8(%r10),%r11 2.13% 2.22% 0x00007f33a7a4f4ad: mov %r11,(%rax) 1.45% 1.33% 0x00007f33a7a4f4b0: mov %r10,0x8(%rax) 1.75% 1.90% 0x00007f33a7a4f4b4: movq $0x0,0x10(%rax) 1.06% 1.22% 0x00007f33a7a4f4bc: movq $0x0,0x18(%rax) 2.28% 2.57% 0x00007f33a7a4f4c4: movq $0x0,0x20(%rax) 1.60% 2.12% 0x00007f33a7a4f4cc: movq $0x0,0x28(%rax) ;*new ; - org.openjdk.InitStoreCoalesce::copy@0 (line 113) 1.08% 1.24% 0x00007f33a7a4f4d4: mov 0x30(%rbp),%r10 ;*getfield inst ; - org.openjdk.InitStoreCoalesce::copy@5 (line 113) 0.49% 0.45% 0x00007f33a7a4f4d8: mov 0x10(%r10),%r11 ; implicit exception: dispatches to 0x00007f33a7a4f516 1.82% 2.44% 0x00007f33a7a4f4dc: mov %r11,0x10(%rax) ;*putfield x1 ; - org.openjdk.InitStoreCoalesce$MyClass::<init>@9 (line 135) ; - org.openjdk.InitStoreCoalesce::copy@8 (line 113) 2.22% 2.40% 0x00007f33a7a4f4e0: mov 0x18(%r10),%r11 1.57% 0.87% 0x00007f33a7a4f4e4: mov %r11,0x18(%rax) ;*putfield x2 ; - org.openjdk.InitStoreCoalesce$MyClass::<init>@17 (line 136) ; - org.openjdk.InitStoreCoalesce::copy@8 (line 113) 1.31% 1.49% 0x00007f33a7a4f4e8: mov 0x20(%r10),%r11 1.50% 1.42% 0x00007f33a7a4f4ec: mov %r11,0x20(%rax) ;*putfield x3 ; - org.openjdk.InitStoreCoalesce$MyClass::<init>@25 (line 137) ; - org.openjdk.InitStoreCoalesce::copy@8 (line 113) 2.21% 2.36% 0x00007f33a7a4f4f0: mov 0x28(%r10),%r10 0.75% 0.80% 0x00007f33a7a4f4f4: mov %r10,0x28(%rax) 1.54% 1.83% 0x00007f33a7a4f4f8: add $0x10,%rsp 1.00% 0.92% 0x00007f33a7a4f4fc: pop %rbp 1.54% 1.33% 0x00007f33a7a4f4fd: test %eax,0xe3beafd(%rip) # 0x00007f33b5e0e000 ; {poll_return} 1.79% 1.40% 0x00007f33a7a4f503: retq */ @Benchmark @CompilerControl(CompilerControl.Mode.DONT_INLINE) public MyClass copy() { return new MyClass(inst); } /* Remarkably, if we bait VM into doing the nullcheck before entering the method, the implicit null-check is gone, but pre-zeroing is still intact! 0.85% 1.21% 0x00007f8eb0aebdfc: mov 0x30(%rsi),%rbp ;*getfield inst ; - org.openjdk.InitStoreCoalesce::copyBaited@1 (line 142) 0.95% 1.46% 0x00007f8eb0aebe00: test %rbp,%rbp 0.01% 0x00007f8eb0aebe03: je 0x00007f8eb0aebe9e ;*getfield x1 ; - org.openjdk.InitStoreCoalesce$MyClass::access$000@1 (line 147) ; - org.openjdk.InitStoreCoalesce::copyBaited@6 (line 143) 2.54% 2.54% 0x00007f8eb0aebe09: mov 0x60(%r15),%rax 0.56% 0.42% 0x00007f8eb0aebe0d: mov %rax,%r10 0.40% 0.42% 0x00007f8eb0aebe10: add $0x30,%r10 0.72% 0.91% 0x00007f8eb0aebe14: cmp 0x70(%r15),%r10 0x00007f8eb0aebe18: jae 0x00007f8eb0aebe8a 2.59% 3.08% 0x00007f8eb0aebe1a: mov %r10,0x60(%r15) 0.59% 0.51% 0x00007f8eb0aebe1e: prefetchnta 0xc0(%r10) 0.76% 0.84% 0x00007f8eb0aebe26: movabs $0x7f8ca3eb0210,%r10 ; {metadata('org/openjdk/InitStoreCoalesce$MyClass')} 0.63% 1.21% 0x00007f8eb0aebe30: mov 0xa8(%r10),%r11 2.49% 2.92% 0x00007f8eb0aebe37: mov %r11,(%rax) 1.82% 1.96% 0x00007f8eb0aebe3a: mov %r10,0x8(%rax) 1.17% 1.10% 0x00007f8eb0aebe3e: movq $0x0,0x10(%rax) 0.97% 1.01% 0x00007f8eb0aebe46: movq $0x0,0x18(%rax) 3.47% 3.26% 0x00007f8eb0aebe4e: movq $0x0,0x20(%rax) 1.11% 1.11% 0x00007f8eb0aebe56: movq $0x0,0x28(%rax) ;*new ; - org.openjdk.InitStoreCoalesce::copyBaited@10 (line 144) 1.73% 1.54% 0x00007f8eb0aebe5e: mov 0x28(%rbp),%r10 0.82% 0.66% 0x00007f8eb0aebe62: mov %r10,0x28(%rax) ;*putfield x4 ; - org.openjdk.InitStoreCoalesce$MyClass::<init>@33 (line 161) ; - org.openjdk.InitStoreCoalesce::copyBaited@15 (line 144) 2.75% 2.86% 0x00007f8eb0aebe66: mov 0x20(%rbp),%r10 0.58% 0.65% 0x00007f8eb0aebe6a: mov %r10,0x20(%rax) ;*putfield x3 ; - org.openjdk.InitStoreCoalesce$MyClass::<init>@25 (line 160) ; - org.openjdk.InitStoreCoalesce::copyBaited@15 (line 144) 1.82% 1.66% 0x00007f8eb0aebe6e: mov 0x18(%rbp),%r10 0.40% 0.38% 0x00007f8eb0aebe72: mov %r10,0x18(%rax) ;*putfield x2 ; - org.openjdk.InitStoreCoalesce$MyClass::<init>@17 (line 159) ; - org.openjdk.InitStoreCoalesce::copyBaited@15 (line 144) 4.12% 2.87% 0x00007f8eb0aebe76: mov 0x10(%rbp),%r10 0.50% 0.64% 0x00007f8eb0aebe7a: mov %r10,0x10(%rax) ;*synchronization entry ; - org.openjdk.InitStoreCoalesce::copyBaited@-1 (line 142) 2.00% 1.78% 0x00007f8eb0aebe7e: add $0x10,%rsp 0.45% 0.22% 0x00007f8eb0aebe82: pop %rbp 2.12% 2.21% 0x00007f8eb0aebe83: test %eax,0xf1da177(%rip) # 0x00007f8ebfcc6000 ; {poll_return} 1.96% 1.17% 0x00007f8eb0aebe89: retq */ @Benchmark @CompilerControl(CompilerControl.Mode.DONT_INLINE) public MyClass copyBaited() { MyClass l = inst; long bait = l.x1; return new MyClass(l); } private static class MyClass { private long x1, x2, x3, x4; public MyClass(long x1, long x2, long x3, long x4) { this.x1 = x1; this.x2 = x2; this.x3 = x3; this.x4 = x4; } public MyClass(MyClass other) { this.x1 = other.x1; this.x2 = other.x2; this.x3 = other.x3; this.x4 = other.x4; } } }