Аптымізацыя прадукцыйнасці Java прыкладанняў

  1. 1. Пазбягайце сінхранізацыі
  2. 2. Выкарыстоўвайце папярэднія вылічэнні
  3. 3. Выцягванне масіваў
  4. 4. Паварочванне цыклаў for
  5. 5. Сціск цыклаў for
  6. 6. Пазбягайце інтэрфейсных выклікаў метадаў
  7. 7. Пазбягайце непатрэбных зваротаў да масіву
  8. 8. Пазбягайце выкарыстання аргументаў
  9. 9. Адмоўцеся ад выкарыстання лакальных зменных
  10. 10. Не выкарыстоўвайце getter-ы / setter-ы
  11. 11. Эфектыўная матэматыка
  12. 12. Пішыце коратка
  13. 13. Выкарыстоўвайце убудаваныя метады
  14. 14. Выкарыстоўвайце StringBuffer замест String
  15. заключэнне

Не гледзячы на ​​тое, што апісваныя ніжэй трукі і парады працуюць не толькі ў 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
Пераклад: Аляксандр ледком


Яшчэ лепш int a = 10; // Падумайце, ці сапраўды вы хочаце // нешта лічыць падчас выканання праграмы?