Are Java static calls more or less expensive than non-static calls?
Asked Answered
D

11

112

Is there any performance benefit one way or another? Is it compiler/VM specific? I am using Hotspot.

Deuterogamy answered 27/9, 2010 at 15:10 Comment(1)
There might be a difference, and it might go either way for any particular piece of code, and it might change with even a minor release of the JVM. This is most definitely part of the 97% of small efficiencies that you should forget about.Kellen
S
82

Four years later...

Okay, in the hope of settling this question once and forever, I have written a benchmark which shows how the different kinds of calls (virtual, non-virtual, static) compare to each other.

I ran it on ideone, and this is what I got:

(Larger number of iterations is better.)

    Success time: 3.12 memory: 320576 signal:0
  Name          |  Iterations
    VirtualTest |  128009996
 NonVirtualTest |  301765679
     StaticTest |  352298601
Done.

As expected, virtual method calls are the slowest, non-virtual method calls are faster, and static method calls are even faster.

What I did not expect was the differences to be so pronounced: Virtual method calls were measured to run at less than half the speed of non-virtual method calls, which in turn were measured to run a whole 15% slower than static calls. That's what these measurements show; the actual differences must in fact be slightly more pronounced, since for each virtual, nonvirtual, and static method call, my benchmarking code has an additional constant overhead of incrementing one integer variable, checking a boolean variable, and looping if not true.

I suppose the results will vary from CPU to CPU, and from JVM to JVM, so give it a try and see what you get:

import java.io.*;

class StaticVsInstanceBenchmark
{
    public static void main( String[] args ) throws Exception
    {
        StaticVsInstanceBenchmark program = new StaticVsInstanceBenchmark();
        program.run();
    }

    static final int DURATION = 1000;

    public void run() throws Exception
    {
        doBenchmark( new VirtualTest( new ClassWithVirtualMethod() ), 
                     new NonVirtualTest( new ClassWithNonVirtualMethod() ), 
                     new StaticTest() );
    }

    void doBenchmark( Test... tests ) throws Exception
    {
        System.out.println( "  Name          |  Iterations" );
        doBenchmark2( devNull, 1, tests ); //warmup
        doBenchmark2( System.out, DURATION, tests );
        System.out.println( "Done." );
    }

    void doBenchmark2( PrintStream printStream, int duration, Test[] tests ) throws Exception
    {
        for( Test test : tests )
        {
            long iterations = runTest( duration, test );
            printStream.printf( "%15s | %10d\n", test.getClass().getSimpleName(), iterations );
        }
    }

    long runTest( int duration, Test test ) throws Exception
    {
        test.terminate = false;
        test.count = 0;
        Thread thread = new Thread( test );
        thread.start();
        Thread.sleep( duration );
        test.terminate = true;
        thread.join();
        return test.count;
    }

    static abstract class Test implements Runnable
    {
        boolean terminate = false;
        long count = 0;
    }

    static class ClassWithStaticStuff
    {
        static int staticDummy;
        static void staticMethod() { staticDummy++; }
    }

    static class StaticTest extends Test
    {
        @Override
        public void run()
        {
            for( count = 0;  !terminate;  count++ )
            {
                ClassWithStaticStuff.staticMethod();
            }
        }
    }

    static class ClassWithVirtualMethod implements Runnable
    {
        int instanceDummy;
        @Override public void run() { instanceDummy++; }
    }

    static class VirtualTest extends Test
    {
        final Runnable runnable;

        VirtualTest( Runnable runnable )
        {
            this.runnable = runnable;
        }

        @Override
        public void run()
        {
            for( count = 0;  !terminate;  count++ )
            {
                runnable.run();
            }
        }
    }

    static class ClassWithNonVirtualMethod
    {
        int instanceDummy;
        final void nonVirtualMethod() { instanceDummy++; }
    }

    static class NonVirtualTest extends Test
    {
        final ClassWithNonVirtualMethod objectWithNonVirtualMethod;

        NonVirtualTest( ClassWithNonVirtualMethod objectWithNonVirtualMethod )
        {
            this.objectWithNonVirtualMethod = objectWithNonVirtualMethod;
        }

        @Override
        public void run()
        {
            for( count = 0;  !terminate;  count++ )
            {
                objectWithNonVirtualMethod.nonVirtualMethod();
            }
        }
    }

    static final PrintStream devNull = new PrintStream( new OutputStream() 
    {
        public void write(int b) {}
    } );
}

It is worth noting that this performance difference is only applicable to code which does nothing other than invoking parameterless methods. Whatever other code you have between the invocations will dilute the differences, and this includes parameter passing. Actually, the 15% difference between static and nonvirtual calls is probably explained in full by the fact that the this pointer does not have to be passed to the static method. So, it would only take a fairly small amount of code doing trivial stuff in between calls for the difference between different kinds of calls to be diluted to the point of having no net impact whatsoever.

Also, virtual method calls exist for a reason; they do have a purpose to serve, and they are implemented using the most efficient means provided by the underlying hardware. (The CPU instruction set.) If, in your desire to eliminate them by replacing them with nonvirtual or static calls, you end up having to add as much as an iota of extra code to emulate their functionality, then your resulting net overhead is bound to be not less, but more. Quite possibly, much, much, unfathomably much, more.

Scabby answered 14/2, 2015 at 0:38 Comment(15)
'Virtual' is a C++ term. There are no virtual methods in Java. There are ordinary methods, which are runtime-polymorphic, and static or final methods, which aren't.Tubman
@levgen yes, for someone whose point of view is as narrow as the official high-level overview of the language, it is precisely as you say. But of course the high-level concepts are implemented using well established low-level mechanisms which were invented a long time before java came into existence, and virtual methods are one of them. If you take just a tiny look under the hood, you will immediately see that this is so: docs.oracle.com/javase/specs/jvms/se7/html/…Scabby
Thanks for answering the question without making presumptions about premature optimisation. Great answer.Viosterol
The large gap between the virtual call and the non-virtual call makes me wonder. I would expect the performance to be almost the same in your benchmark. Since the field holding the Runnable is final, there shouldn't be a reason to resolve the virtual method more than once. Or am I missing something?Officialdom
@Officialdom I do not know that there is a mechanism of "resolution" of virtual methods. Without JIT tricks, any such resolution would necessarily be less efficient than the simple lookup that the virtual method call actually is. If you are referring to the possibility that the JIT may detect that the call always goes to the same target and replace the virtual call with a nonvirtual call, there are two possibilities: a) JIT compilers are not that smart b) there was no JITting in my benchmarks. Feel free to try yourself, the code I posted is self-contained and runnable.Scabby
Yes, that's exactly what I meant. Anyway I just ran the test on my machine. Aside from the jitter that you can expect for such a benchmark there is no difference in speed whatsoever: VirtualTest | 488846733 -- NonVirtualTest | 480530022 -- StaticTest | 484353198 on my OpenJDK installation. FTR: That's even true if I remove the final modifier. Btw. I had to make the terminate field volatile, otherwise the test didn't finish.Officialdom
That's all very weird. Except for having to make the terminate field volatile. I would have expected it to need to be volatile, but for some reason it worked for me without being volatile, so I left it like that. The fact that it had to be volatile in order to work for you probably means that you have more optimizations enabled. But then, I do not understand your results. strange.Scabby
FYI, I get rather surprising results on a Nexus 5 running Android 6: VirtualTest | 12451872 -- NonVirtualTest | 12089542 -- StaticTest | 8181170. Not only that OpenJDK on my notebook manages to perform 40x more iterations, the static test always has about 30% less throughput. This might be an ART specific phenomenon, because I get an expected result on an Android 4.4 tablet: VirtualTest | 138183740 -- NonVirtualTest | 142268636 -- StaticTest | 161388933Officialdom
I just noticed that the Android 4.4 numbers are wrong. It's actually VirtualTest | 13818374 -- NonVirtualTest | 14226863 -- StaticTest | 16138893, so only 10% of the original numbers. I increased the DURATION by factor 10 before I ran this test. Still not slower than the Nexus 5, which is weird.Officialdom
FWIW, I just ran it on a 2.6GHz i7 and got the following: VirtualTest | 510795847 -- NonVirtualTest | 539244268 -- StaticTest | 554098864 which is more in line with the numbers you are receiving. The numbers I posted in the answer are from ideone.com. Perhaps ideone.com's CPU cache is being thrashed due to over-utilization.Scabby
@Officialdom and here are the results from my 4GHz i7-4790K: VirtualTest = 542M; NonVirtualTest = 607M; StaticTest = 684MScabby
That was debugging. Without debugging, the results are: VirtualTest = 631M; NonVirtualTest = 629M; StaticTest = 689M. (So, no difference between virtual and nonvirtual, and less pronounced difference between those and static.)Scabby
Thanks for showing benchmark numbers! I find the results here interesting, especially because Intellij has a code inspection option that checks whether a method can be declared static without changing the code.Armillia
The reason why this test may complete without declaring terminate as volatile in some environments, is that it runs cold, i.e. interpreted, then, which renders the entire “benchmark” moot. In practice, any performance critical code location would get optimizations which not only eliminate the method invocation overhead but also the redundant reads of the non-volatile variable. On my system, it reproducible hangs even without warmup. Of course, adding a volatile modifier to the flag would make the entire test worthless as well. Making a meaningful microbenchmark is not trivial.Oaxaca
@Oaxaca I do not doubt what you are saying. Any benchmark of this kind is necessarily quite inaccurate. But I think it gives a decent indication. And the accuracy can be thought of as being not quite so bad if you consider that one's production code is almost as likely to be running cold as this benchmark is.Scabby
T
79

First: you shouldn't be making the choice of static vs non-static on the basis of performance.

Second: in practice, it won't make any difference. Hotspot may choose to optimize in ways that make static calls faster for one method, non-static calls faster for another.

Third: much of the mythos surrounding static versus non-static are based either on very old JVMs (which did not do anywhere near the optimization that Hotspot does), or some remembered trivia about C++ (in which a dynamic call uses one more memory access than a static call).

Tweedsmuir answered 27/9, 2010 at 15:13 Comment(7)
@AaronDigulla I assume you mean static methods don't fit a testable code base that well?Infelicitous
@hajder: Yes. PowerMock can do it but at a price.Alible
You're absolutely right you shouldn't prefer static methods based on this alone. However, In the case static methods fit the design well, it is useful to know they are at least as fast, if not faster than instance methods and should not be ruled out on a performance basis.Maun
@AaronDigulla -.- what if I told you that I came here because I'm optimizing right now, not prematurely, but when I really need it? You assumed that OP wants to optimize prematurely, but you know that this site is kinda global... Right? I don't want to be rude, but please don't assume things like this next time.Subtile
@DaliborFilus I need to find a balance. Using static methods causes all kinds of problems, so they should be avoided, especially when you don't know what you're doing. Secondly, most "slow" code is because of (bad) design, not because Language-of-Choice is slow. If your code is slow, static methods probably won't save it unless its calling methods that do absolutely nothing. In most cases, the code in the methods will dwarf the call overhead.Alible
Downvoted. This does not answer the question. The question asked about performance benefits. It did not ask for opinions on design principles.Chadwick
If I trained a parrot to say "premature opimisation is the root of all evil" I'd get 1000 votes from people who know as much about performance as the parrot.Horizon
C
45

Well, static calls can't be overridden (so are always candidates for inlining), and don't require any nullity checks. HotSpot does a bunch of cool optimizations for instance methods which may well negate these advantages, but they're possible reasons why a static call may be faster.

However, that shouldn't affect your design - code in the most readable, natural way - and only worry about this sort of micro-optimization if you have just cause (which you almost never will).

Camphor answered 27/9, 2010 at 15:14 Comment(7)
They're possible reasons why a static call may be faster Could you explain me those reasons?Peepul
@JavaTechnical: The answer explains those reasons - no overriding (which means you don't need to work out the implementation to use each time and you can inline) and you don't need to check whether you're calling the method on a null reference.Camphor
@JavaTechnical: I don't understand. I've just given you things that don't need to be computed/checked for static methods, along with an inlining opportunity. Not doing work is a performance benefit. What's left to understand?Camphor
Are static variables retrieved faster than non-static variables?Peepul
@JavaTechnical: Well there's no nullity check to perform - but if the JIT compiler can remove that check (which will be context-specific), I wouldn't expect much difference. Things like whether the memory is in cache would be much more important.Camphor
let us continue this discussion in chatPeepul
I have to be honest that I use static methods in cases where it is otherwise difficult to unit test methods in classes. In particular classes that involve platform-specific calls like Bluetooth and Android APIs. Making an instance of the class often involves instantiation of Bluetooth classes which require complicated support like Robolectric on Android in unit tests. If I can pack some of that awful byte decoding from characteristics into a static method I can unit test that decoding method by just passing in canned byte arrays. Anyways, you get the point.Thornie
Q
20

It is compiler/VM specific.

  • In theory, a static call can be made slightly more efficient because it doesn't need to do a virtual function lookup, and it can also avoid the overhead of the hidden "this" parameter.
  • In practice, many compilers will optimize this out anyway.

Hence it's probably not worth bothering about unless you have identified this as a truly critical performance issue in your application. Premature optimization is the root of all evil etc...

However I have seen this optimization give a substantial performance increase in the following situation:

  • Method performing a very simple mathematical calculation with no memory accesses
  • Method being invoked millions of times per second in a tight inner loop
  • CPU bound application where every bit of performance mattered

If the above applies to you, it may be worth testing.

There is also one other good (and potentially even more important!) reason to use a static method - if the method actually has static semantics (i.e. logically is not connected to a given instance of the class) then it makes sense to make it static to reflect this fact. Experienced Java programmers will then notice the static modifier and immediately think "aha! this method is static so it doesn't need an instance and presumably doesn't manipulate instance specific state". So you will have communicated the static nature of the method effectively....

Quinary answered 27/9, 2010 at 15:39 Comment(0)
R
20

7 years later...

I don't have a huge degree of confidence in the results that Mike Nakis found because they don't address some common issues relating to Hotspot optimisations. I've instrumented benchmarks using JMH and found the overhead of an instance method to be about 0.75% on my machine vs a static call. Given that low overhead I think except in the most latency sensitive operations it's arguably not the biggest concern in an applications design. The summary results from my JMH benchmark are as follows;

java -jar target/benchmark.jar

# -- snip --

Benchmark                        Mode  Cnt          Score         Error  Units
MyBenchmark.testInstanceMethod  thrpt  200  414036562.933 ± 2198178.163  ops/s
MyBenchmark.testStaticMethod    thrpt  200  417194553.496 ± 1055872.594  ops/s

You can look at the code here on Github;

https://github.com/nfisher/svsi

The benchmark itself is pretty simple but aims to minimise dead code elimination and constant folding. There are possibly other optimisations that I've missed/overlooked and these results are likely to vary per JVM release and OS.

package ca.junctionbox.svsi;

import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.infra.Blackhole;

class InstanceSum {
    public int sum(final int a, final int b) {
        return a + b;
    }
}

class StaticSum {
    public static int sum(final int a, final int b) {
        return a + b;
    }
}

public class MyBenchmark {
    private static final InstanceSum impl = new InstanceSum();

    @State(Scope.Thread)
    public static class Input {
        public int a = 1;
        public int b = 2;
    }

    @Benchmark
    public void testStaticMethod(Input i, Blackhole blackhole) {
        int sum = StaticSum.sum(i.a, i.b);
        blackhole.consume(sum);
    }

    @Benchmark
    public void testInstanceMethod(Input i, Blackhole blackhole) {
        int sum = impl.sum(i.a, i.b);
        blackhole.consume(sum);
    }
}
Reduplicate answered 26/12, 2017 at 21:5 Comment(2)
Purely academic interest here. I'm curious about any potential benefits this sort of micro-optimization might have on metrics other than ops/s primarily in an ART environment (e.g. memory usage, reduced .oat file size etc). Do you know of any relatively simple tools/ways one could attempt to benchmark these other metrics?Rossie
Hotspot figures out there are no extensions to InstanceSum in the classpath. Try adding another class that extends InstanceSum and overrides the method.Nammu
C
15

As previous posters have said: This seems like a premature optimization.

However, there is one difference (a part from the fact that non-static invokations require an additional push of a callee-object onto the operand stack):

Since static methods can't be overridden, there will not be any virtual lookups in runtime for a static method call. This may result in an observable difference under some circumstances.

The difference on a byte-code level is that a non-static method call is done through INVOKEVIRTUAL, INVOKEINTERFACE or INVOKESPECIAL while a static method call is done through INVOKESTATIC.

Cowley answered 27/9, 2010 at 15:16 Comment(3)
A private instance method, however, is (at least typically) invoked using invokespecial since it is not virtual.Korella
Ah, interesting, I could only think of constructors, that's why I omitted it! Thanks! (updating answer)Cowley
The JVM will optimize if there's one type is instantiated. If B extends A, and no instance of B has been instantiated, method calls on A will not need a virtual table lookup.Archenteron
S
12

It is unbelievably unlikely that any difference in the performance of static versus non-static calls is making a difference in your application. Remember that "premature optimization is the root of all evil".

Secundas answered 27/9, 2010 at 15:13 Comment(2)
would you please further explains what does ""premature optimization is the root of all evil"."?Thomasenathomasin
The question was "Is there any performance benefit one way or another?", and this exactly answers that question.Secundas
M
12

For the decision if a method should be static, the performance aspect should be irrelevant. If you have a performance problem, making lots of methods static isn't going to save the day. That said, static methods are almost certainly not slower than any instance method, in most cases marginally faster:

1.) Static methods are not polymorphic, so the JVM has less decisions to make to find the actual code to execute. This is a moot point in the Age of Hotspot, since Hotspot will optimize instance method calls that have only one implementation site, so they will perform the same.

2.) Another subtle difference is that static methods obviously have no "this" reference. This results in a stack frame one slot smaller than that of an instance method with the same signature and body ("this" is put in slot 0 of the local variables on the bytecode level, whereas for static methods slot 0 is used for the first parameter of the method).

Massie answered 27/9, 2010 at 16:2 Comment(0)
O
0

In theory, less expensive.

Static initialization is going to be done even if you create an instance of the object, whereas static methods will not do any initialization normally done in a constructor.

However, I haven't tested this.

Outsail answered 27/9, 2010 at 15:13 Comment(3)
@R. Bemrose, what does static initialization have to do with this question?Ela
@Kirk Woll: Because Static Initialization is done the first time the class is referenced... including prior to the first static method call.Outsail
@R. Bemrose, sure, as is loading the class into the VM to begin with. Seems like a non-sequitor, IMO.Ela
F
0

As Jon notes, static methods can't be overridden, so simply invoking a static method may be -- on a sufficiently naive Java runtime -- faster than invoking an instance method.

But then, even assuming you're at the point where you care about messing up your design to save a few nanoseconds, that just brings up another question: will you need method overriding yourself? If you change your code around to make an instance method into a static method to save a nanosecond here and there, and then turn around and implement your own dispatcher on top of that, yours is almost certainly going to be less efficient than the one built into your Java runtime already.

Fariss answered 27/9, 2010 at 15:39 Comment(0)
S
-3

I would like to add to the other great answers here that it also depends on your flow, for example:

Public class MyDao {

   private String sql = "select * from MY_ITEM";

   public List<MyItem> getAllItems() {
       springJdbcTemplate.query(sql, new MyRowMapper());
   };
};

Pay attention that you create a new MyRowMapper object per each call.
Instead, I suggest to use here a static field.

Public class MyDao {

   private static RowMapper myRowMapper = new MyRowMapper();
   private String sql = "select * from MY_ITEM";

   public List<MyItem> getAllItems() {
       springJdbcTemplate.query(sql, myRowMapper);
   };
};
Snakemouth answered 10/7, 2015 at 0:10 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.