"Рекомендации по производительности" в документации по Android имеет довольно смелое утверждение:
one()
работает быстрее. Он выводит все на локальные переменные, избегая поиска. Только длина массива дает преимущество в производительности.
где он ссылается на этот фрагмент кода:
int len = localArray.length;
for (int i = 0; i < len; ++i) {
sum += localArray[i].mSplat;
}
Это меня очень удивило, потому что localArray.length
просто обращается к целому числу, и если вы будете использовать промежуточную переменную, вам придется сделать то же самое еще раз. Действительно ли мы говорим, что промежуточная переменная, которая должна перейти только в x
вместо y.x
, быстрее?
Я рассмотрел этот вопрос, который касается той же идеи, но вместо этого использует arraylist и его последующий метод .size()
. Здесь консенсус, казалось, заключался в том, что не будет никакой разницы, так как этот вызов метода, вероятно, просто будет привязан к целочисленному доступу в любом случае (это именно тот сценарий, который у нас есть здесь).
Итак, я взял байт-код, чтобы узнать, может ли это что-нибудь сказать мне.
Учитывая следующий исходный код:
public void MethodOne() {
int[] arr = new int[5];
for (int i = 0; i < arr.length; i++) { }
}
public void MethodTwo() {
int[] arr = new int[5];
int len = arr.length;
for (int i = 0; i < len; i++) { }
}
Я получаю следующий байт-код:
public void MethodOne();
Code:
0: iconst_5
1: newarray int
3: astore_1
4: iconst_0
5: istore_2
6: iload_2
7: aload_1
8: arraylength
9: if_icmpge 18
12: iinc 2, 1
15: goto 6
18: return
public void MethodTwo();
Code:
0: iconst_5
1: newarray int
3: astore_1
4: aload_1
5: arraylength
6: istore_2
7: iconst_0
8: istore_3
9: iload_3
10: iload_2
11: if_icmpge 20
14: iinc 3, 1
17: goto 9
20: return
Они отличаются в следующих инструкциях:
Метод один
6: iload_2
7: aload_1
8: arraylength
9: if_icmpge 18
12: iinc 2, 1
15: goto 6
18: return
Метод два
9: iload_3
10: iload_2
11: if_icmpge 20
14: iinc 3, 1
17: goto 9
20: return
Теперь я не уверен на 100%, как мне нужно интерпретировать 8: arraylength
, но я думаю, что это просто указывает поле, к которому вы обращаетесь. Первый метод загружает индексный счетчик и массив и обращается к полю arraylength
, а второй метод загружает индексный счетчик и промежуточную переменную.
Я сравнивал два метода с JMH (10 разминок, 10 итераций, 5 вилок), что дает мне следующий результат сравнения:
c.m.m.Start.MethodOne thrpt 50 3447184.351 19973.900 ops/ms
c.m.m.Start.MethodTwo thrpt 50 3435112.281 32639.755 ops/ms
который говорит мне, что разница пренебрежимо мала.
В чем заключается заявка на документацию Android о том, что с использованием промежуточной переменной в состоянии цикла на основе?