- 1. Уникайте синхронізації
- 2. Використовуйте попередні обчислення
- 3. Витягування масивів
- 4. Розгортання циклів for
- 5. Стиснення циклів for
- 6. Уникайте інтерфейсних викликів методів
- 7. Уникайте непотрібних звернень до масиву
- 8. Уникайте використання аргументів
- 9. Відмовтеся від використання локальних змінних
- 10. Не використовуйте getter-и / setter-и
- 11. Ефективна математика
- 12. Пишіть коротко
- 13. Використовуйте вбудовані методи
- 14. Використовуйте StringBuffer замість String
- висновок
Не дивлячись на те, що описувані нижче трюки і поради працюють не тільки в J2ME, саме для мобільних додатків вони мають першорядне значення в силу обмеженості ресурсів платформи.
Техніки оптимізації продуктивності, як правило, засновані на збільшенні обсягу пам'яті, необхідної програмі для роботи. На жаль, ресурси платформи Java ME дуже обмежені, і програмісту доводиться постійно балансувати між продуктивністю і економією системних ресурсів. На мій погляд, рано розпочата оптимізація коду веде до ускладнення і уповільнення процесу розробки, тому більшість наведених тут порад краще застосовувати вже на завершальній фазі розробки, коли вже все налагоджено і працює.
1. Уникайте синхронізації
Відомо, що код, в якому використовується механізм синхронізації, приблизно в 4 рази повільніше, ніж зазвичай коду. Незалежно від конкретної реалізації Java VM використання синхронізації вимагає від віртуальної машини великої кількості додаткових дій: вона повинна відстежувати блокування, блокувати контекст при початку роботи з ним і розблокувати, коли робота з контекстом закінчена. Потоки, які хочуть отримати доступ до заблокованого контексту змушені стояти в черзі і чекати його звільнення. Думаю, що привів досить переконливі доводи, і Ви будете використовувати синхронізацію тільки там, де без неї дійсно неможливо обійтися.
2. Використовуйте попередні обчислення
Якщо Ви розробляєте гру з 3D або 2.5D графікою, то напевно використовуєте масу математичних обчислень з тригонометричними функціями. Такі розрахунки сильно навантажують процесор, тому варто заздалегідь прорахувати найбільш складні вирази і представити їх у вигляді масиву, звідки діставати готові значення в процесі виконання програми. Крім графіки існує маса програм, де попередні обчислення можна зробити заздалегідь і оформити у вигляді масивів даних.
3. Витягування масивів
Доступ до елементів масиву займає більше часу, ніж робота зі звичайними змінними. Багатовимірні масиви - ще більш повільна історія. Уникайте використання багатовимірних масивів. У більшості випадків вони легко замінюються одновимірними.
приклад
А ще одномірні масиви споживають менше динамічної пам'яті, ніж їх багатовимірні зібратися.
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 ++; } Я спеціально зробив цей список таким довгим, щоб Ви відчули, наскільки наше життя стало простіше з появою циклів. Однак багато програмісти звикли бачити в циклі тільки абстракцію і не замислюються про накладні витрати, пов'язаних з їх використанням.
Якщо ви на етапі програмування точно знаєте число необхідних ітерацій, Ви можете частково розвернути цикл:
Ця реалізація буде повторюватися всього 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 зайвих операцій присвоювання значень.
Наведу ще одну менше очевидну техніку стиснення циклів. Ніколи не викликайте методи обчислення розмірів в заголовку циклу. Порівняйте:
У першому випадку на кожному кроці обчислюється розмір 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
Переклад: Олександр льодком
Ще краще int a = 10; // Подумайте, чи дійсно ви хочете // щось вважати під час виконання програми?