adic3x
offline
Опыт:
107,539Активность: |
[Info] Оптимизация JASS кода
тут я хочу выложить несколько интересный мыслей по поводу производительности. хорошо, я пишу это в состоянии помутненного рассудка, так что делайте скидку на это. многие идеи носят параноидальный характер. итак, приступим
несоветую читать это новичкам в Jass, врядли это произведет позитивный еффект. узкие местасначала хочу рассказать о самом важном нюансе - в оптимизации нуждаются в первую очередь узкие места, т.е. те участки которые выполняются достаточно часто. к примеру поток которые совершает массу действий через короткие промежутки времени - достаточно узкое место. тригер, срабатывающий на событие, которое происходит часто тоже претендует на узкое место. а вот к примеру поток из main() быть узким не может никак. хорошо, это не значит что не нужно оптимизировать и их - как правило написание оптимального кода по времени и трудозатратам почти равны. итак, надеюсь вы поняли это, идем дальше. тригерычасто я вижу жуть которую делают чисто из следованию какой то глупой традиции. пример жуткого кода Код:
немного лучше, но тоже кошмарно Код:
но в таком случае количество вызовов функции GetSpellAbilityId() = количеству таких спелов. удобнее, короче и быстрее Код:
в таком случае сразу растить дерево может быть нерациональным, тем более если количество спелов неизвестно заранее, легче сделать тупо линейнывй поиск, но потом переделать под дерево. глобальное мышлениехорошо, предположим нам надо сделать 5 спелов. делаем первый, потом делаем второй, потом третий, который начинает конфликтовать с первым (ну к примеру пишет в UserData или использует какой либо отлов урона), исправляем первый и тогда перестает работать второй и приходится делать в нем кучу проверок... в общем понятно хороший вариант - сначала сесть и продумать как что должно быть, примерно представить задачу, спроектировать код, определиться с системами и т.д. в результате избавляем себя от кучи гемора и делаем код более "чистым" это действительно будет избавлять вас от очень большой рутины. inline и ветвлениесейчас речь идет конечно не о vJass, а о том, что некоторые функции стоит делать внутри других. если функция которая вызывается очень часто и при этом занимает несколько строк есть смысл писать ее непосредсвенно. так же помните, что чем больше аргументов тем больше вызов функции сам по себе. отлично. линейный код всегда будет быстрее. помните это когда пытаетесь сделать цикл который сработает 4 раза или что то вроде. хотя это скорее просто совет, т.к. размер нашего кода нас тоже немного но волнует. помните также что несколько if (дерево) и один call или даже инлайн будет быстрее TriggerExecute/Evaluate или ExecuteFunc. http://xgm.guru/forum/showthread.php?t=17923 - читайте также эту тему, там есть интересные вариант ветвления. пусть умрет BJнивкоем случае не используйте BJ функции. в любом случае вы теряете временя как минимум на вызове. более того большинство BJ могут либо создавать утечки, либо вообще работать достаточно коряво. В TESH и JassCraft можно совершенно легко увидеть "начинку" любой функции. я считаю их использование просто признаком плохого тона. также наследием BJ являеться вариант кода, когда bool для получения его же самого сравнивают с другим bool Код:
это не нужно! также не используйте Код:
используйте программу ConvOrder и указывайте сразу значение утечкихорошо, утечки в коде можно разделить на две категории: неудаленные обьекты и неудаленные ссылки (первое куда страшнее, хотя в некоторых случаях второе тоже не хорошо) неудаленный обьект - к примеру мы использовали констуркцию Код:
после этого мы обязанны удалить эту локацию, иначе память отведенная под нее не освободиться и она будет все время "висеть". более того неудаленный юнит будет грузить движок - и как результат лаги. однако, иногда есть смысл создавать обьекты только один раз, и не удалять их, а использовать повторно и не создавать новых (это быстрее) - яркий пример организации dummy cast из CasterSystem векса. обнуление некоторых переменных - тоже очень важно (особенно если вы запоминаете что то через RB и массивы - рост хендлов будет абузить это) - но ради всего не надо обнулять int, real, string, bool, code! обнуляются локальные переменны типа handle и его наследники, и то только те, которые не высвабаждают хендл без обнуления (texttag и player к примеру обнулять нестоит) просто war не считает хендл занятым пока на него существуют ссылки (он находится в переменной), а локальная переменная - висячая ссылка. не используйте кешькеш намного медленнее массивов, по моим расчетам примерно в 5 раз. более того он делает код нечитабельным. и отучает думать. многие задачи решаются куда более елегантно без него (если приложить немного мысли). опастность кеша в том что к примеру если записать хендл юнита, потом через некоторое время попытаться его извлечь - мы можем получить совершенно другой обьект (т.к. юнит может быть удален и ссылки на него потерты, в результате чего war посчитает что этот хендл можно присвоить другому обьекту) передача аргументов в другую функцию также легко делается через один на карту набор глобальных переменных, главное следить за потоками. таже если массив может быть заменен прост переменной отлично! так и стоит поступить. цикл по группамужастно: Код:
не могу видеть это больше, типичный пример старомодного кода. оптимизированный вариант: Код:
отлично! аргументы могут быть спокойно переданы через глобальные переменные. это большое подспорье если надо к примеру сделать какое либо одноразовое действие на юнитами в радиусе и т.д. хорошо, но не забывайте о ForGroup, если условие будет истинным и юнит будет добалвен в группу - самое просто перебирать группу именно через эту функцию. числавозможны вы привыкли к вашей "умной" среде разработки, которая set i=1+1 превращает в set i=2 в момент компиляции. но тут такое не пройдет. старайтесь использовать сразу вычисленные числа. меня всегда корежило от кода типа Код:
правильно: Код:
помните, что некоторые native принимают значение в градусах (CreateUnit, SetUnitFacing) а некоторые в радианах (Cos, Sin) так что стоит думать какое значение вам нужно в данном случае. тем более SetUnitFacing(u, 360.) и SetUnitFacing(u, 720.) будут работать одинаково. также старайтесь использовать максимально простые формулы (к примеру если мы считаем длину вектора не стоит из суммы квадратов его протекций на координатные оси извлекать квадратный корень и сравнивать его с какой либо длиной, куда удобней возвести длину в квадрат у сравнивать уже с просчитанным заранее квадратом) http://xgm.guru/articles.php?section=wc3&name=about_int - хе-хе, я так же рекомендую читать эту статью для поиска идей по оптимизации. черт, о чем я хотел еще рассказать? таймеры, разделение потоковхорошо, как правило для отсроченных действий принято использовать таймеры. ранее я видел кучу кодов, где на хендл таймера через кешь вешалось все что только не жалко. это неправильно. панацеей может быть использование struct из vJass (или их аналога, лично я пишу их сам, хм, иногда мне казалось по коментариям некоторых товарищей что до vJass массивов вообще небыло) в таком случае надо только приаттачить структуру и все, это и быстрее и намного удобнее. примерно так Код:
также события с малым периодом (движение частиц) могут обрабатываться одним таймеро в циклах, ссылку на тему с обсуждением я уже выкладывал. теперь, если ваш поток вызывает лаги и имеется такая возможность - сделайте глобальный таймер, который скажем будет срабатывать каждые .5 сек и делать что либо. это может быть полезно при создание большого количества обьектов, загрузке чего либо либо просто для разругзке движка. к примеру так Код:
и т.д. либо зациклить его на одну функцию и передавать значение через какой либо счетчик сработок. еще немного о переменныхвсе переменные - DWORD (используйте RB во благо). локальные переменные несколько быстрее глобальных. массивы медленне обычных переменных. на обявление локальных переменных тоже нужно время. Код:
медленне чем Код:
т.е. если вам надо вызвать какую либо функцию несколько раз, крепко подумайте, стоит ли ее результат заносить в локальную переменную. есть еще интересный нюанс - можно использовать метку аргумента как переменную, т.е. если мы передаем в функцию юнита, используем его а потом он нам не нужен - мы можем поместить в эту переменную другого юнита, это может быть короче, удобнее и быстрее (т.е. не надо инициализировать лишнюю локальную переменную) более того такие переменные не нуждаются в обнулении. эффектыэто очень важный, хоть и косвенны вопрос. часто для визуальных эффектов (к примеру делается какой либо красивый нестандартный спелл) используются юниты в качестве эффектов. это вполне оправданно если эффект должен двигаться по какой либо нетривиальной траектории, либо к примеру отскакивать от поверхности рельефа. но если есть возможность сделать импортированную модель и использовать конструкцию типа DestroyEffect(AddSpecialEffect(...)) то это то, что я бы рекомендовал место создания кучи юнитов. опять же это более простой и удобный вариант. StopWatch nativeвходящие в состав NewGen WE пак нативов большое подспорье в проверках на скорость алгоритма. если вы нуждаетесь в сравнение - вы обязанны использовать это (благо последнии версии стабильно запускаются). достаточно исчерпывающую информацию можно найти в ReadMe.txt (правда я правил ongameload.lua). в завершениихорошо, я писал тут о достаточно простых и порой очевидных вещах (если я что то придумаю еще или я просто что то забыл я добавлю это после). по хорошему вы должны при написании каждой строки кода спрашивать себя - "как я могу сделать это лучше?". хорошо, и я хочу закончить этот труд фразой взятой из другого не моего и не жассового тутора по оптимизации: Цитата:
Отредактировано ADOLF, 15.08.2008 в 13:13. |
|
13.08.2008, 11:51 | #1
+0/−0
Профиль |
Приват |
Поиск |
Цитата |
IP: Записан
|
alexkill
offline
Опыт:
19,072Активность: |
Цитата:
Ты бы хоть описал, что должно быть в условии (я так понял, 256-ричное представление) Код:
Цитата:
Единственное, с чем соглашусь бесспорно. Цитата:
Я тебе объясню, почему новички (и не только) используют его: в статьях о Jass, на которые очень часто и с душой даются ссылки, очень детально расписан процесс перехода с глобальных на локальные переменные. Посему использование глобалок, как думают многие пользователи, отходит на второй план и подсознательно ищутся способы обойти их (кеш, строки и т.п.). Как-то нужно смягчить резкость. Цитата:
Использование старых (проверенных временем) способов - не всегда пример дурного тона. Пример с группами не исключение. Цитата:
Ты и сейчас это увидишь... =) Цитата:
теперь соглашусь, месяц назад бы поспорил _________ P.S. В целом статья - больше излияние души, как показалось. Понять в принципе можно. |
||||||
13.08.2008, 12:22 | #2
+0/−0
Профиль |
Приват |
Поиск |
Цитата |
IP: Записан
|
J
expert
offline
Опыт:
48,747Активность: |
мало полезной информации
Цитата:
все остальное вода... Отредактировано Jon, 13.08.2008 в 17:16. |
|
13.08.2008, 14:54 | #3
+0/−0
Профиль |
Приват |
Поиск |
Цитата |
IP: Записан
|
kvaDrug
offline
Опыт:
1,601Активность: |
Все было очень полезно для меня. Автор - хороший человек)
передача аргументов в другую функцию также легко делается через один на карту набор глобальных переменных, главное следить за потоками.
плз, ссылку на статью о потоках, можно англ.
SetUnitFacing - а, простите за тупой вопрос, между чем это угол. |
13.08.2008, 16:48 | #4
+0/−0
Профиль |
Приват |
Поиск |
Цитата |
IP: Записан
|
akkolt
offline
Опыт:
13,826Активность: |
kvaDrug, что задашь, то и будет. А так это просто функция, пока не принявшая никаких данных.
|
13.08.2008, 17:05 | #5
+0/−0
Профиль |
Приват |
Поиск |
Цитата |
IP: Записан
|
ShadoW DaemoN
offline
Опыт:
37,078Активность: |
Насчет триггеров - хмм, можно же сделать что-то вроде:
Код:
и, имхо вместо действия можно поставить условие: Код:
Насчет if IsUnitType(u, ut)==true then - хмм, буржуи советуют писать проверку для конкретно этой функции, ибо она может косячить, для всех остальных случаев можно без проверки. Заключение классное) set i = i + 1 Как я могу сделать это лучше? |
13.08.2008, 17:24 | #6
+0/−0
Профиль |
Приват |
Поиск |
Цитата |
IP: Записан
|
kvaDrug
offline
Опыт:
1,601Активность: |
akkolt, явно не джазз панда) Абсолютно в теме.
Я вообщето хотел узнать больше о потоках. Глянул структуры - суровая штука, иду использовать. |
13.08.2008, 17:40 | #7
+0/−0
Профиль |
Приват |
Поиск |
Цитата |
IP: Записан
|
adic3x
offline
Опыт:
107,539Активность: |
Jon, нет, это небыло попыткой как либо обозначить что это делал именно ты, я видел это и раньше, когда к примеру при движении юнита по поляркам сохраняли именно в градусах угол и потом каждый раз умножали на константу
Цитата:
да, мб, хотя имхо и немного с других сторон, в любом случае я решил написать все в одной Цитата:
невидел никогда в природе Цитата:
ну я поробно описал как и почему оно бывает, в любом случае кешь медленее, а статья о том как сделать код быстрее Цитата:
то что пишу я труевее у одбней Цитата:
я думаю это и так ясно Цитата:
это дольше ADOLF добавил: я также добавил один подпункт) |
||||||
13.08.2008, 18:28 | #8
+0/−0
Профиль |
Приват |
Поиск |
Цитата |
IP: Записан
|
Sebra
offline
Опыт:
5,603Активность: |
Цитата:
Кажется, конкретно эта функция не всегда возвращает корректный boolean. Помнится я про это у Векса читал. Вызывает проблемы в фильтрах. Цитата:
Если не ошибаюсь, это единственная быстрая функция. Потери на запуск любой другой функции превысят потери на локальную переменную. Цитата:
Типа так: Код:
8 абил 3 сравнения. |
|||
13.08.2008, 19:56 | #9
+0/−0
Профиль |
Приват |
Поиск |
Цитата |
IP: Записан
|
PlayerDark
Coraline
offline
Опыт:
10,569Активность: |
ADOLF Такой стиль кода быстрее но 1) его понятность уменьшается во много раз. 2) На современных компьютерах потеря скорости при обращении к кешу а так же как и неоптимизированные функции работают все равно довольно быстро. Единственное чего надо избегать это утечек памяти.
PlayerDark добавил: Да, и редактирование такого кода тоже более трдоемкая задача. |
14.08.2008, 13:30 | #10
+0/−0
Профиль |
Приват |
Поиск |
Цитата |
IP: Записан
|
adic3x
offline
Опыт:
107,539Активность: |
Цитата:
в варе булы как и все типы 32, в них 0 фалсе, остальное все тру, а специально через РБ возращал все значения в виде була (И2Б(64)) к примеру в фильтры - все работало нормально Цитата:
стопВач нативе в руки и тестить) Цитата:
понятность кода определяется именованием меток понятным и удобным (функций и переменных) а так же толково раставленными коментариями Цитата:
в любом случае вы рано или поздно достигаете рубежа "производительности" Цитата:
чем же? если в коде предусмотренно что бы он мог быть расширенным - то это делается так же просто |
|||||
14.08.2008, 15:27 | #11
+0/−0
Профиль |
Приват |
Поиск |
Цитата |
IP: Записан
|
Sebra
offline
Опыт:
5,603Активность: |
Цитата:
Цитата:
|
||
14.08.2008, 19:40 | #12
+0/−0
Профиль |
Приват |
Поиск |
Цитата |
IP: Записан
|
Sunn
To feel joy, not be blue
offline
Опыт:
4,975Активность: |
На известном забугорном сайте прочитал, что следует вместо локаций использовать координаты. Да и тут видел нечто подобное. Насколько это умный совет? Ведь всеравно, чтоб указать точку при помощи координат, мы пишем Location(x, y), а ф-я Location() возвращает location. Тоесть работаем со все той же location, но чтоб ее получить надо запомнить 2 значения и вызвать функцию. Вместо того чтоб просто передать/запомнить location. А если с нашими координатами работает несколько функций? Тогда количество действий увеличивается кратно количеству этих самых функций... обьясните, что и как, плиз. |
28.08.2008, 14:36 | #13
+0/−0
Профиль |
Приват |
Поиск |
Цитата |
IP: Записан
|
Лось
offline
Опыт:
7,223Активность: |
Мы не используем ф-ию Location, когда работаем с координатами, зачем нам указывать точку, если можно указать X и Y? Тока если узнать высоту рельефа ( ф-ия GetLocationZ(location loc) ), а так все нативки работают с координатами. |
28.08.2008, 15:07 | #14
+0/−0
Профиль |
Приват |
Поиск |
Цитата |
IP: Записан
|
adic3x
offline
Опыт:
107,539Активность: |
Цитата:
действительно это так !!! надо написать о правильном ветвление и о координатах если тут этого нету !!! Цитата:
... для всех функций берущих или возращающих локации есть аналоги которые используют две риала место лока (кроме высоты терайна и точки каста) CreateUnit(player, integer, realX, realY, real) CreateUnitAtLoc(player, integer, location, real) прототипы такого рода функций почему лушче координаты?! 1) не надо создавать обьект 1.1) ненадо удалять обьект 1.2) ненадо обнулять 2) для их изменения мы не вызываем функцию, а просто меняем значение в переменной (что быстрее намного) 2.1) для получения координаты по одной из осей не надо вызывать функцию, можно обратиться напрямую 2.2) любое действие с локацией будет медленне чем с координатами, поскольку идет вызов функции 3) локации не разработанны под 3д, системы на координатах можно заточить под что угодно 3.1) локации и векторы это уже бред, а вот координаты прикрасно с ними вяжуться, по сути если вы используете ооп подход как на вЖасс (я пишу тоже на "лоу левел") то вы можете разработать такую систему, которая будет максимально удобной |
||
28.08.2008, 15:08 | #15
+0/−0
Профиль |
Приват |
Поиск |
Цитата |
IP: Записан
|
Sunn
To feel joy, not be blue
offline
Опыт:
4,975Активность: |
Лось, как не используя функцию Location можно написать следуйщее:
Код:
И еще, где можно почитать про struct тому, кто впервые это видит в жассе? |
28.08.2008, 15:15 | #16
+0/−0
Профиль |
Приват |
Поиск |
Цитата |
IP: Записан
|
Лось
offline
Опыт:
7,223Активность: |
Код:
Xenosapien, common.j тебе в руки... |
28.08.2008, 15:18 | #17
+0/−0
Профиль |
Приват |
Поиск |
Цитата |
IP: Записан
|
adic3x
offline
Опыт:
107,539Активность: |
скачайте ген пак - он подсвечивает бж сразу красным - будет вам счастье))) |
28.08.2008, 15:26 | #18
+0/−0
Профиль |
Приват |
Поиск |
Цитата |
IP: Записан
|
Sunn
To feel joy, not be blue
offline
Опыт:
4,975Активность: |
Оу, спасибо Лось. Хотя конечно справочник по наптивам в *тхт это злоба дня. |
28.08.2008, 15:27 | #19
+0/−0
Профиль |
Приват |
Поиск |
Цитата |
IP: Записан
|
adic3x
offline
Опыт:
107,539Активность: |
Цитата:
есть ридми в самом паке на буржуйсок наречии и есть перевод в этом разделе... |
|
28.08.2008, 15:28 | #20
+0/−0
Профиль |
Приват |
Поиск |
Цитата |
IP: Записан
|