Returning two values from Java function efficiently

浪尽此生 提交于 2019-12-06 14:19:17

-XX:+EliminateAllocations optimization (ON by default in Java 8) works fine for that.

Whenever you return new Pair(a, b) right at the end of the callee method and use the result immediately in the caller, JVM is very likely to do a scalar replacement if the callee is inlined.

A simple experiment shows there's nearly no overhead in returning an object. This is not only an efficient 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

The benchmark:

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;
        }
    }
}

Your described solution is pretty much as good as you can get in Hotspot -- passing in an object to hold the return values and mutating it. (Java 10 value types might do something better here, but I don't think that's even in the prototype stage yet.)

That said: small short-lived objects are not actually that far from zero-overhead. Garbage collection of short lived objects is deliberately extremely cheap.

I've had to deal with that problem, and found that the best way is to instantiate a simple final class with public fields, then pass it in parameter to your method. Push the results to that instance.

If you have a loop, try to reuse that instance for as long as possible.

Having setters and getters, in Java 7 (when I did this), had a very small overhead. Same goes with instantiate new objects every loop.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!