Сколько стоит вызов метода в 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() { }
}