Оптимизация производительности Java приложений

Java оптимизация производительностиНе смотря на то, что описываемые ниже трюки и советы работают не только в J2ME, именно для мобильных приложений они имеют первостепенное значение в силу ограниченности ресурсов платформы.

Техникиоптимизации производительности, как правило, основаны на увеличении объема памяти, необходимой программе для работы. К сожалению, ресурсы платформы Java ME очень ограничены, и программисту приходится постоянно балансировать между производительностью и экономией системных ресурсов. На мой взгляд, рано начатая оптимизация кода ведет к усложнению и замедлению процесса разработки, поэтому большинство приведенных тут советов лучше применять уже на завершающей фазе разработки, когда уже все отлажено и работает.

1. Избегайте синхронизации

Известно, что код, в котором используется механизм синхронизации, примерно в 4 раза медленнее обычного кода. Независимо от конкретной реализации Java VM использование синхронизации требует от виртуальной машины большого количества дополнительных действий: она должна отслеживать блокировки, блокировать контекст при начале работы с ним и разблокировать, когда работа с контекстом закончена. Потоки, которые хотят получить доступ к заблокированному контексту вынуждены стоять в очереди и ждать его освобождения. Думаю, что привел достаточно убедительные доводы, и Вы будете использовать синхронизацию только там, где без нее действительно невозможно обойтись.

 2. Используйте предварительные вычисления

 Если Вы разрабатываете игру с 3D или 2.5D графикой, то наверняка используете массу математических вычислений с тригонометрическими функциями. Такие расчеты сильно нагружают процессор, поэтому стоит заранее просчитать наиболее сложные выражения и представить их в виде массива, откуда доставать готовые значения в процессе выполнения программы. Помимо графики существует масса приложений, где предварительные вычисления можно сделать заранее и оформить в виде массивов данных.
 

 3. Вытягивание массивов

 Доступ к элементам массива занимает больше времени, чем работа с обычными переменными. Многомерные массивы - еще более медленная история. Избегайте использования многомерных массивов. В большинстве случав они легко заменяются одномерными.
 
 Пример

// До оптимизации
int[][] table;// Таблица 4x4
 
// После оптимизации
int[] table;// Таблица 1x16


А еще одномерные массивы потребляют меньше динамической памяти, чем их многомерные собраться.


 4. Разворачивание циклов for

 Циклы - это замечательная штука, но они несут в себе дополнительные накладные расходы. Вместе с вызовом тела цикла на каждом шаге выполняется операция увеличения счетчика и проверка условия. Например

 void printMsg(){
for(int loop=0; loop<15; loop++){
System.out.println(msg);
}
}

Выполняется виртуальной машиной следующим образом:

void printMsg(){
int loop=0;
 
System.out.println(msg);
if(loop>=15){
return;
}
loop++;
 
System.out.println(msg);
if(loop>=15){
return;
}
loop++;
 
System.out.println(msg);
if(loop>=15){
return;
}
loop++;
 
System.out.println(msg);
if(loop>=15){
return;
}
loop++;
 
System.out.println(msg);
if(loop>=15){
return;
}
loop++;
 
System.out.println(msg);
if(loop>=15){
return;
}
loop++;
 
System.out.println(msg);
if(loop>=15){
return;
}
loop++;
 
System.out.println(msg);
if(loop>=15){
return;
}
loop++;
 
System.out.println(msg);
if(loop>=15){
return;
}
loop++;
 
System.out.println(msg);
if(loop>=15){
return;
}
loop++;
 
System.out.println(msg);
if(loop>=15){
return;
}
loop++;
 
System.out.println(msg);
if(loop>=15){
return;
}
loop++;
 
System.out.println(msg);
if(loop>=15){
return;
}
loop++;
 
System.out.println(msg);
if(loop>=15){
return;
}
loop++;
 
System.out.println(msg);
if(loop>=15){
return;
}
loop++;
}

Я специально сделал этот список таким длинным, чтобы Вы почувствовали, насколько наша жизнь стала проще с появлением циклов. Однако многие программисты привыкли видеть в цикле только абстракцию и не задумываются о накладных расходах, сопряженных с их использованием.

Если вы на этапе программирования точно знаете число необходимых итераций, Вы можете частично развернуть цикл:

void printMsg(){
 
for(int loop=0; loop<15; loop+=5){
 
System.out.println(msg);
System.out.println(msg);
System.out.println(msg);
System.out.println(msg);
System.out.println(msg);
}
}

Эта реализация будет повторяться всего 3 раза, соответственно накладные расходы уменьшатся в 5 раз по сравнению с предыдущим примером.

Не смотря на вполне очевидный выигрыш от использования разворачивания циклов, не слишком увлекаться этой техникой. В результате разворачивания генерируется больший по размеру байт код, и может сложиться ситуация, когда тело цикла не поместится полностью в кеш процессора. В результате будут задействованы механизмы подгрузки-выгрузки частей кеша, и ваш оптимизированный код будет сильно тормозить.
 


 5. Сжатие циклов for

 Смысл операции сжатия заключается в вынесении за рамки цикла всего того, что не нуждается в повторном вычислении:

// Плохо оптимизированный код
for(int loop=0; loop<10; loop++){
 
int a=5;
int b=10;
 
int c= a* b+ loop;
}
 
 
// А вот здесь - все в порядке
int a=5;
int b=10;
 
for(int loop=0; loop<10; loop++){
 
int c= a* b+ loop;
}

Во втором примере переменным a и b значение присваивается всего один раз. Таким образом, по сравнению с первым вариантом нам удалось избавиться от 20 лишних операций присваивания значений.

Приведу еще одну менее очевидную технику сжатия циклов. Никогда не вызывайте методы вычисления размеров в заголовке цикла. Сравните:

//Плохой код
for(int loop=0; loop< msgs.size(); loop++){
 
System.out.println(msgs.get(loop));
}
 
 
// Хороший код
int msgCount= msgs.size();
for(int loop=0; loop< msgCount; loop++){
 
System.out.println(msgs.get(loop));
}

В первом случае на каждом шаге вычисляется размер msgs. Во втором случае - это делается один раз до начала цикла. Конечно, эта оптимизация подразумевает, что тело цикла никак не влияет на размер msgs.



6. Избегайте интерфейсных вызовов методов

В байткоде Java существует 4 типа методов. Ниже они перечислены в порядке уменьшения скорости вызова.

invokestatic
Статические методы не используют экземпляр класса, поэтому им не нужно соблюдать правила полиморфизма и искать подходящий параметрам вызова экземпляр.

invokevirtual
Обычные методы.

invokespecial
Специальные методы - конструкторы, private и super class методы.

invokeinterface
Требуют поиск подходящей реализации интерфейсного метода.

Тип используемых методов влияет на весь дизайн приложения, поэтому помните о скорости вызовов методов в процессе разработки.

Вызов статических методов обеспечивает наилучшую производительность, поскольку Java VM не нужно ничего искать. Вызов интерфейса напротив - самый медленный путь, требующий два поиска.

7. Избегайте ненужных обращений к массиву

Обращение к элементу массива - не самая быстрая операция. Если Вы видите, что какой-то элемент используется в алгоритме многократно - сохраните его в переменную, которую затем и используйте.

8. Избегайте использования аргументов

При вызове нестатических методов вы неявно передаете ссылку this вместе с другими параметрами. В Java VM вызов методов реализован по принципу стека. При каждом вызове аргументы заносятся в стек, а затем извлекаются из него при выполнении метода. В некоторых случаях можно избежать необходимости использования стека, отказавшись от передачи аргументов.

// Плохо
return multiply(int a, int b){
 
return a* b;
}
 
 
// Хорошо
int result;
int a;
int b;
 
void multiply(){
 
result= a* b;
}

С точки зрения высокоуровневого программирования приведенные в примере стили очень похожи, однако второй метод работает быстрее. Еще большей скорости вызова можно добиться, если объявить все переменные и методы как статические. В этом случае при вызове метода стек не будет задействован вообще.


9. Откажитесь от использования локальных переменных

Локальные переменные помещаются и извлекаются из стека при вызове каждого метода. С точки зрения производительности гораздо эффективнее использовать обычные переменные.

10. Не используйте getter-ы/setter-ы

Откажитесь от использования методов, устанавливающих и считывающих значения полей класса и обращайтесь к ним напрямую. Конечно, это немного противоречит базовым принципам ООП, но с точки зрения оптимизации этот шаг вполне оправдан. Обычно я избавляюсь от этих методов на финальной стадии разработки проекта. Это редкий случай, когда оптимизация производительности связана с уменьшением размера программы.

11. Эффективная математика

С точки зрения производительности, не все математические операции равны по скорости выполнения. Быстро работают сложение и вычитание. Умножение, деление, вычисление модуля - заметно медленнее. Самыми быстрыми являются побитывые операции.

// Плохо
int a=5*2;
 
 
// Уже лучше
int a=5+5;
 
 
// Хорошо: побитовый сдвиг эквивалентен умножению и делению на 2
int a=5<<1;// Сдвигаем в 5 (0000 0101) 1-бит влево, получаем 10 (0000 1010).
 
 
// Еще лучше
int a=10;// Подумайте, действительно ли вы хотите
// что-то считать во время выполнения программы?

Этими методами оптимизации пользовались еще праотцы, когда писали первые игры под DOS. Для Java они тоже подходят.


12. Пишите кратко

Применяйте операторы типа +=, поскольку они генерируют короткий байткод.

\\Плохо
x=x+1;
\\Хорошо
x+=1;


13. Используйте встроенные методы

Пользуйтесь методами, предоставляемыми платформой. Например, использование System.arraycopy для копирования элементов массива будет более эффективным, чем аналогичная собственная реализация.
 

14. Используйте StringBuffer вместо String

Если Вы работаете со строками, значения которых могут меняться по ходу выполнения программы, используйте класс StringBuffer вместо String. Любое изменение объекта String приводит к  созданию нового объекта.

Заключение

Выше были приведены простейшие методы оптимизации, которые могут с успехом использоваться при разработке приложений для мобильной платформы J2ME. Эти методы основаны на анализе байт-кода и позволяют выработать стиль программирования, обеспечивающий оптимальный результат.


Источники:j2medevcorne
javajazzup.com

Перевод:Александр Ледков





Наши соцсети

Подписаться Facebook Подписаться Вконтакте Подписаться Twitter Подписаться Google Подписаться Telegram

Популярное

Ссылки

Новости [1] [2] [3]... Android/ iOS/ J2ME[1] [2] [3]) Android / Архив

Рейтинг@Mail.ru Яндекс.Метрика
MobiLab.ru © 2005-2018
При использовании материалов сайта ссылка на www.mobilab.ru обязательна