ZZ 5
ZZ 5

Reputation: 1964

When to use local variable instead of method?

When shall one use local variable instead of methods? I use the rule, that if method is used twice in particular block of code it should be assigned to local variable to reduce computations, but I'm not sure if JVM doesn't optimize it, so I'd like to get real answer. Let's assume that the method will return the same result everytime for given context.

For example this:

private boolean someMethod() {
    return true;
}

private boolean otherMethod() {
    if (someMethod()) {
      System.out.println(1);
    }
    // other logic
    if (someMethod()) {
        System.out.println(2);
    }
}

I'd refactor into:

private boolean someMethod() {
    return true;
}
private boolean otherMethod() {
    boolean localVar = someMethod();
    if (localVar) {
      System.out.println(1);
    }
    // other logic
    if (localVar) {
        System.out.println(2);
    }
}

Is it correct approach?

Upvotes: 2

Views: 750

Answers (4)

Javier
Javier

Reputation: 678

First of all you should consider

  • Readability (which way is easier to understand and maintain).
  • Value consistency. If you get accustomed to cache the value in a variable, and at some case the value changes, then you might have a bug that is hard to spot.

Then comes the performance issues. For such trivial cases it might be equal or less efficient. Even if you used the variable 1000 times.

The compiler might translate if (someMethod()) -> if (true) if you are just returning a constant or reference (see Method inlining).


UPDATE

Just for recreational purposes (fooling around with homebrew pocket benchmark) (nothing serious like JMH).

  • Indicates that for simple cases there is no benefit of creating a variable to cache the result of method returning constants.
  • Weird thing I checked the generated bytecode (jdk 1.8.0_31) and Inlining$2#execute has 3 calls to calledStaticMethod (therefore no inlining on compilation). I must be missing something (Eclipse had its own compiler? options? misconceptions?) and the virtual machine JIT optimizer doing a terrific job.

source code (Inlining.java). Contains the almost harmless Benchmark class appended

// https://stackoverflow.com/questions/32500628/when-to-use-local-variable-instead-of-method
public class Inlining {
public static int calledStaticMethod() {
    return 0;
}

public static void dumpValue(Object o) { // Fool compiler/JVM that value is going somewhere

}

public static void main(String[] args) {
    float iterations = Benchmark.iterationsFor10sec(new Benchmark("cacheCall") {
        @Override
        public void execute(long iterations) {
            int i1,i2,i3 = 0;
            for (int i = 0; i < iterations; i++) {
                int localVar = calledStaticMethod();
                i1 = localVar;
                i2 = i1 + localVar;
                i3 = i2 + localVar;
            }
            dumpValue((Integer)i3);
        }
    });
    System.out.printf("cacheCall: %10.0f\n", iterations);

    iterations = Benchmark.iterationsFor10sec(new Benchmark("staticCall") {
        @Override
        public void execute(long iterations) {
            int i1,i2,i3 = 0;
            for (int i = 0; i < iterations; i++) {
                i1 = calledStaticMethod();
                i2 = i1 + calledStaticMethod();
                i3 = i2 + calledStaticMethod();
            }
            dumpValue((Integer)i3);
        }
    });
    System.out.printf("staticCall: %10.0f\n", iterations);

    // borderline for inlining, as instance methods might be overridden.
    iterations = Benchmark.iterationsFor10sec(new Benchmark("instanceCall") {
        public int calledInstanceMethod() { return calledStaticMethod(); }
        @Override
        public void execute(long iterations) {
            int i1,i2,i3 = 0;
            for (int i = 0; i < iterations; i++) {
                i1 = calledInstanceMethod();
                i2 = i1 + calledInstanceMethod();
                i3 = i2 + calledInstanceMethod();
            }
            dumpValue((Integer)i3);
        }
    });
    System.out.printf("instanceCall: %10.0f\n", iterations);
}

}

abstract class Benchmark {
private String name;
public Benchmark(String s) { name = s; }
public String getName() { return name; }
public abstract void execute(long iterations);
public static float iterationsFor10sec(Benchmark bm) {
    long t0 = System.nanoTime();
    long ellapsed = 0L;
    // Calibration. Run .5-1.0 seconds. Estimate iterations for .1 sec
    final long runtimeCalibrate = (long)0.5e9; // half second
    long iterations = 1L;
    while (ellapsed < runtimeCalibrate) {
        bm.execute(iterations);         
        iterations *= 2;
        ellapsed = System.nanoTime() - t0;
    }
    iterations--; // Actually we executed 2^N - 1.
    int innerIterations = (int) ((double)iterations * 1e8 /* nanos/inner */ / ellapsed);
    if (innerIterations < 1) { innerIterations = 1; }
    // run rest of the time
    final long runtimeTotal = (long)1e10;
    // outer loop
    while (ellapsed < runtimeTotal) {
        // delegate benchmark contains inner loop
        bm.execute(innerIterations);
        iterations += innerIterations;
        ellapsed = System.nanoTime() - t0;
    }
    // execution time might exceed 10 seconds. rectify number of iterations
    return (float)iterations * 1e10f /* nanos total */ / (float)ellapsed;

}
}

output:

cacheCall: 21414115328
staticCall: 21423159296
instanceCall: 21357850624

Upvotes: 2

ZZ 5
ZZ 5

Reputation: 1964

FYI I've made a little microbenchmark, which looks like this:

public class Main {

public static void main(String[] args) {
    Method method = new Method();
    Variable variable = new Variable();

    long startMethod;
    long startVariable;
    long methodDuration;
    long variableDuration;

    startMethod = System.currentTimeMillis();
    method.someMethod();
    methodDuration = System.currentTimeMillis() - startMethod;

    startVariable = System.currentTimeMillis();
    variable.someMethod();
    variableDuration = System.currentTimeMillis() - startVariable;

    System.out.println("methodDuration: " + methodDuration);
    System.out.println("variableDuration: " + variableDuration);
    System.exit(0);
}
}

public class Method {

public void someMethod() {
    long x = otherMethod();
    long y = otherMethod();
    long z = otherMethod();
    x--;
    y--;
    z--;
    System.out.println(x);
    System.out.println(y);
    System.out.println(z);
}

private long otherMethod() {
    long result = 0;
    for (long i = 1; i < 36854775806L / 2; i++) {
        i = i * 2;
        i = i / 2;
        result = i;
    }
    return result;
}
}

public class Variable {

public void someMethod() {
    long x = otherMethod();
    long y = x;
    long z = x;
    x--;
    y--;
    z--;
    System.out.println(x);
    System.out.println(y);
    System.out.println(z);
}

private long otherMethod() {
    long result = 0;
    for (long i = 1; i < 36854775806L / 2; i++) {
        i = i * 2;
        i = i / 2;
        result = i;
    }
    return result;
}
}

I've ran it two times and got following results:

methodDuration: 116568
variableDuration: 37674
methodDuration: 116657
variableDuration: 37679

So it is faster (three times for calling method once instead of three times), however I guess that with standard small methods we can gain better performance of tens milliseconds or even less. I could accept all of the three answers above, not sure which one shall I choose.

Upvotes: 1

Abhijeet Kale
Abhijeet Kale

Reputation: 1716

if any method is going to return same value then it is definitely preferable to use variable than a method call.

Method call is a expensive task. But also consider cost of a object size. its always a tread-off between computation time and computation size.I think it differs from instance to instance.

Upvotes: 1

user1438038
user1438038

Reputation: 6079

It depends. If the method returns a different result each time it is called (such as a timestamp) and you need the exact same value each time in otherMethod(), then you need to store it in a local variable.

On the other hand, if someMethod() always returns the same value, you can call the method as often as you want. However, if someMethod() does some extensive calculations (in terms of costs -> run time or memory usage), you might be better off calling the method only once, if your application logic allows this.

Upvotes: 4

Related Questions