Optimization
-XX:+EliminateAllocations (enabled by default in Java 8) is great for this.
Whenever you return new Pair(a, b) right at the end of the called method and immediately use the result in the caller, the JVM will most likely perform a scalar replacement if the called user is nested.
A simple experiment shows that when returning an object there is almost no overhead. This is not only an effective way, but also the most readable one.
Benchmark Mode Cnt Score Error Units ReturnPair.manualInline thrpt 30 127,713 ± 3,408 ops/us ReturnPair.packToLong thrpt 30 113,606 ± 1,807 ops/us ReturnPair.pairObject thrpt 30 126,881 ± 0,478 ops/us ReturnPair.pairObjectAllocated thrpt 30 92,477 ± 0,621 ops/us
Test:
import org.openjdk.jmh.annotations.*; import org.openjdk.jmh.infra.Blackhole; import java.util.concurrent.ThreadLocalRandom; @State(Scope.Benchmark) public class ReturnPair { int counter; @Benchmark public void manualInline(Blackhole bh) { bh.consume(counter++); bh.consume(ThreadLocalRandom.current().nextInt()); } @Benchmark public void packToLong(Blackhole bh) { long packed = getPacked(); bh.consume((int) (packed >>> 32)); bh.consume((int) packed); } @Benchmark public void pairObject(Blackhole bh) { Pair pair = getPair(); bh.consume(pair.a); bh.consume(pair.b); } @Benchmark @Fork(jvmArgs = "-XX:-EliminateAllocations") public void pairObjectAllocated(Blackhole bh) { Pair pair = getPair(); bh.consume(pair.a); bh.consume(pair.b); } public long getPacked() { int a = counter++; int b = ThreadLocalRandom.current().nextInt(); return (long) a << 32 | (b & 0xffffffffL); } public Pair getPair() { int a = counter++; int b = ThreadLocalRandom.current().nextInt(); return new Pair(a, b); } static class Pair { final int a; final int b; Pair(int a, int b) { this.a = a; this.b = b; } } }
source share