Reputation: 1964
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
Reputation: 678
First of all you should consider
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).
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
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
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
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