Android: Сколько накладных расходов генерируется бегом пустой метод?
-
30-09-2019 - |
Вопрос
Я создал класс, чтобы обрабатывать свои выходы отладки, чтобы мне не нужно раскрывать все мои выходы журнала до выпуска.
public class Debug {
public static void debug( String module, String message) {
if( Release.DEBUG )
Log.d(module, message);
}
}
Прочитав другой вопрос, я узнал, что содержимое утверждения IF не скомпилировано, если константы Release.debug является ложным.
То, что я хочу знать, сколько накладных расходов генерируется, запустив этот пустой метод? (После того, как пункт IF будет удален, в способе не остается кода). Это будет какое-либо влияние на мое приложение? Очевидно, что производительность - это большая проблема при написании мобильных телефонов = P
Спасибо
Барас
Решение
Измерения, сделанные на Nexus S с Android 2.3.2:
10^6 iterations of 1000 calls to an empty static void function: 21s <==> 21ns/call
10^6 iterations of 1000 calls to an empty non-static void function: 65s <==> 65ns/call
10^6 iterations of 500 calls to an empty static void function: 3.5s <==> 7ns/call
10^6 iterations of 500 calls to an empty non-static void function: 28s <==> 56ns/call
10^6 iterations of 100 calls to an empty static void function: 2.4s <==> 24ns/call
10^6 iterations of 100 calls to an empty non-static void function: 2.9s <==> 29ns/call
контроль:
10^6 iterations of an empty loop: 41ms <==> 41ns/iteration
10^7 iterations of an empty loop: 560ms <==> 56ns/iteration
10^9 iterations of an empty loop: 9300ms <==> 9.3ns/iteration
Я повторил измерения несколько раз. Никаких существенных отклонений не найдено. Вы можете видеть, что стоимость каждого звонка может сильно варьироваться в зависимости от рабочей нагрузки (возможно, из-за составления JIT), но можно нарисовать 3 вывода:
Dalvik / Java отстой в оптимизации мертвого кода
Статические вызовы функций могут быть оптимизированы намного лучше, чем нестатические (нестатические функции являются виртуальными и должны смотреть в виртуальную таблицу)
Стоимость на Nexus S не превышает 70ns / Call (вот ~ 70 циклов ЦП) и сопоставима со стоимостью одного пустого для итерации петли (то есть одному приращению и одному контролю на локальной переменной)
Соблюдайте, что в вашем случае строковый аргумент всегда будет оценен. Если вы делаете строковые конкатенации, это включает создание промежуточных строк. Это будет очень дорого и включает много GC. Например, выполнение функции:
void empty(String string){
}
называется аргументами, такими как
empty("Hello " + 42 + " this is a string " + count );
10 ^ 4 итерации 100 таких звонков занимают 10. Это 10US / CALL, т.е. ~ 1000 раз медленнее, чем просто пустой звонок. Это также производит огромное количество активности GC. Единственный способ избежать этого - вручную вписать функцию, то есть используйте оператор >>, если << вместо вызова функции отладки. Это безобразно, но единственный способ сделать это работать.
Другие советы
Если вы не назовите это из глубоко вложенного цикла, я бы не беспокоился об этом.
Хороший компилятор удаляет весь пустой метод, что приведет к отсутствию накладных расходов. Я не уверен, что компилятор Dalvik уже делает это, но я подозреваю, что вполне вероятно, по крайней мере, по прибытии состоится со временного компилятора с Froyo.
Смотрите также: Встроенная экспансия
С точки зрения производительности накладные расходы генерации сообщений, которые передаются в функцию отладки, будут намного серьезнее, так как его вероятно, что они делают ассигнования памяти, например,
Debug.debug(mymodule, "My error message" + myerrorcode);
Что все еще произойдет даже через сообщение. К сожалению, вам действительно нужен «если (Release.Debug)» вокруг вызовов к этой функции, а не внутри самой функции, если ваша цель - производительность, и вы увидите это во многих Android Code.
Это интересный вопрос, и мне нравится @misiu_mp Анализ, поэтому я подумал, что я буду обновлять его с тестом на 2016 год на Nexus 7 Runing Android 6.0.1. Вот тестовый код:
public void runSpeedTest() {
long startTime;
long[] times = new long[100000];
long[] staticTimes = new long[100000];
for (int i = 0; i < times.length; i++) {
startTime = System.nanoTime();
for (int j = 0; j < 1000; j++) {
emptyMethod();
}
times[i] = (System.nanoTime() - startTime) / 1000;
startTime = System.nanoTime();
for (int j = 0; j < 1000; j++) {
emptyStaticMethod();
}
staticTimes[i] = (System.nanoTime() - startTime) / 1000;
}
int timesSum = 0;
for (int i = 0; i < times.length; i++) { timesSum += times[i]; Log.d("status", "time," + times[i]); sleep(); }
int timesStaticSum = 0;
for (int i = 0; i < times.length; i++) { timesStaticSum += staticTimes[i]; Log.d("status", "statictime," + staticTimes[i]); sleep(); }
sleep();
Log.d("status", "final speed = " + (timesSum / times.length));
Log.d("status", "final static speed = " + (timesStaticSum / times.length));
}
private void sleep() {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private void emptyMethod() { }
private static void emptyStaticMethod() { }
То sleep()
был добавлен, чтобы предотвратить переполнение Log.d
буфер.
Я играл с ним много раз, и результаты были довольно согласованы с @misiu_mp:
10^5 iterations of 1000 calls to an empty static void function: 29ns/call
10^5 iterations of 1000 calls to an empty non-static void function: 34ns/call
Вызов статического метода всегда был немного быстрее, чем нестатический вызов метода, но казалось бы, что а) разрыв значительно закрылся, поскольку Android 2.3.2 и B) все еще есть затраты для создания вызовов в пустой метод, статический или нет.
Глядя на гистограмму раз раскрывает что-то интересное. Большинство звонков, будь то статические или нет, взять на 30-40 нс, и внимательно смотрите на данные, которые они практически все 30ns.
Запуск того же кода с пустыми петлями (комментирование вызовов метода) создает среднюю скорость 8ns, однако около 3/4 измеренного времени составляет 0ns, в то время как остальные имеют ровно 30ns.
Я не уверен, как объяснить эти данные, но я не уверен, что выводы ISIU_MP все еще держатся. Разница между пустыми статическими и нестатическими методами незначительна, а преобладание измерений составляют ровно 30ns. Это, как говорят, появилось бы, что есть еще некоторая ненулевая стоимость для работы пустых методов.