Сколько стоит вызов метода в Java?

Читал о том, что раньше для ускорения выполнения программы все подряд методы объявляли как final, что приводило к раннему связыванию.

Логично предположить, что методы с модификаторами private и static (т. е. неявно финальные) будут также раннесвязанными. Чего гадать? Пошёл и измерил, сколько стоят вызовы методов.

И измерил, конечно, криво. Не читайте это, читайте Шипилёва.

private static:		0.7105991247625086 ns
protected static:	0.508502125976841 ns
package static:		0.4903413357633824 ns
public static:		0.4870817067507104 ns
private:	0.48754736803823495 ns
protected:	0.48941001318833327 ns
package:	0.4884786906132841 ns
public:		0.48568472288813663 ns

Первый конфуз заключается в том, что private static (то есть раннесвязанный в квадрате вызов!) — самый дорогостоящий. Самый дешёвый — обычный, нестатичный, неприватный, позднесвязанный полиморфный вызов, хотя лидирует он без заметного отрыва.

Второй конфуз состоит в том, что длительность такта моего процессора составляет 0,476 190 476 190 476 2 нс (2,1 ГГц), т. е. вызов метода занимает чуть-чуть больше, чем один такт. Я, конечно, в курсе, что у CISC-архитектуры есть системы предсказания ветвления и прочие плюшки, но как можно исполнить jmp и ret менее, чем за два такта, — ума не приложу! Видимо, вызовы пустых и бесполезных методов как-то оптимизируются.

С новым кодом!

package net.aquadc.test;

public class Main {

    private static final long TIMES = Integer.MAX_VALUE;

    public static void main(String[] args) {
        new Main().test();
    }

    private void test() {

        long time = System.currentTimeMillis();
        for (long i = 0; i < TIMES; i++) {
            priv_stub();
        }
        System.out.println("private static:\t\t" + calc(System.currentTimeMillis()-time) );

        time = System.currentTimeMillis();
        for (long i = 0; i < TIMES; i++) {
            prot_stub();
        }
        System.out.println("protected static:\t" + calc(System.currentTimeMillis()-time) );

        time = System.currentTimeMillis();
        for (long i = 0; i < TIMES; i++) {
            pack_stub();
        }
        System.out.println("package static:\t\t" + calc(System.currentTimeMillis()-time) );

        time = System.currentTimeMillis();
        for (long i = 0; i < TIMES; i++) {
            pub_stub();
        }
        System.out.println("public static:\t\t" + calc(System.currentTimeMillis()-time) );

        time = System.currentTimeMillis();
        for (long i = 0; i < TIMES; i++) {
            privStub();
        }
        System.out.println("private:\t" + calc(System.currentTimeMillis()-time) );

        time = System.currentTimeMillis();
        for (long i = 0; i < TIMES; i++) {
            protStub();
        }
        System.out.println("protected:\t" + calc(System.currentTimeMillis()-time) );

        time = System.currentTimeMillis();
        for (long i = 0; i < TIMES; i++) {
            packStub();
        }
        System.out.println("package:\t" + calc(System.currentTimeMillis()-time) );

        time = System.currentTimeMillis();
        for (long i = 0; i < TIMES; i++) {
            pubStub();
        }
        System.out.println("public:\t\t" + calc(System.currentTimeMillis()-time) );

        System.out.println((1d / 2100000000 * 1000000000) + " ns");
        // ну да, это хардкод, который считает тактовый период моего процессора
    }

    String calc(long time) {
        double div = time;
        div *= 1000;
        div /= TIMES;
        div *= 1000;
        return String.valueOf(div) + " ns";
    }

    private static void priv_stub() { }
    protected static void prot_stub() { }
    static void pack_stub() { }
    public static void pub_stub() { }

    private void privStub() { }
    protected void protStub() { }
    void packStub() { }
    public void pubStub() { }

}

← клик, если это интересно   |   ↓ место для вопросов и идей