Добавлен , опубликован
Раздел:
Основы

Вступление

Для чего эта статья?

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

Но всё же.. зачем?

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

Список Мифов

Локальные хендлы всегда утекают!

раскрыть
Ранее считалось, что любая локальная сложная переменная (всё, что является handle, то бишь юниты, способности и так далее) утекают и затрачивают 4 байта памяти вне зависимости обнулена она или нет. Однако - это оказалось лишь на половину правдой, ибо утечка ЕСТЬ, однако лишь в случае, если мы СОЗДАЁМ ОБЪЕКТ и присваиваем её в локальную переменную и в конце кода мы не обнуляем как раз эту переменную. То бишь мы можем передать референс этой локальной переменной в глобальную, что позволит нам обнулить локальную переменную и фактически убрать утечку связанную со "сложными" локальными переменными.
Пример:
Это ВЫЗОВЕТ утечку.
	function DoSomething takes nothing returns nothing
		local unit u = CreateUnit( Player( 0 ), 'hpea', 0., 0., 270. )
		....
	endfunction
Однако, если же вы вызовете RemoveUnit( u ), то это не будет являться утечкой, так как юнит был обработан и удалён.
Это НЕ ВЫЗОВЕТ утечку
	function DoSomething takes nothing returns nothing
		local unit u = CreateUnit( Player( 0 ), 'hpea', 0., 0., 270. )
		....
		set u = null
	endfunction
Это НЕ ВЫЗОВЕТ утечку
	globals
		unit uTemp = null
	endglobals
	
	function main takes nothing returns nothing
		set uTemp = CreateUnit( Player( 0 ), 'hpea', 0., 0., 270. )
	endfunction

	function DoSomething takes nothing returns nothing
		local unit u = uTemp
		....
	endfunction
Чтобы понять, почему же так, стоит взглянуть на байткод Jass, взглянем на вариант без и с утечкой.
Для начала, предоставлю скриншот того, как же я получаю JASM байткод, я написал функцию получения Джасс ноды из игры по её имени, то бишь это кусок из хештаблицы, в ней же хранится ссылка на байткод, что она выполняет по оффсету 0x18.
теперь рассмотрим один из вариантов:
Мы получаем байткод:
байткод
05070000
00000F80
0E290700
00000F7D
11290000
00000F80
0C000000
00000000
27000000
Что выдаёт нам:
Результат
0x05 = local | 0x07 = type где номер = тип, 0x07 = Handle | 0x00 = nothing | 0x00 = nothing
argument = variable id ( то бишь 0xF80 ) переменные в Jass индексируются и далее вызываются по индексу.
0x0E = getvar | 0x29 = register (номер регистра) | 0x07 = type | 0x00
argument = variable id ( то бишь 0xF7D )
0x11 = setvar | 0x29 = register | 0x00 | 0x00
argument = variable id ( то бишь 0xF80 )
0x0C = literal | 0x00 | 0x00 | 0x00 - ибо функция возвращает nothing, потому в R0 записывается 0.
0x27 = return
Если же Вам всё это ничего не говорит (большинству скорее всего это ничего и не скажет), это банально значит, что локальные переменные достаточно близки к аргументам функций по логике, а аргументы обнулять не нужно. Однако если же был создан новый объект, то без обнуления локальной переменной внутриигровой счётчик хендлов повысится на 1 единицу.
Простой ответ - сами переменные не утекают ни при каких обстоятельствах и они преобразуются в нечто статичное, и им выделена память единожды. Потому, утечки вызываются фактически банальной проблемой логики в самом коде, то бишь вы создали хендл, а затем его не удалили и так может повторяться уйму раз. Но это не утечка, ибо тогда по этой логике call CreateUnit и так далее - утечки, ибо этот юнит просто создан и дальше нам нужно его найти, чтобы убить или удалить и так далее.
Видео примеры
Пример с юнитом:
Пример с локацией:
Пример с группой:
Примеры с аргументами функций:
1)
2)
Подводя итоги, можно смело сказать, что никакого "ужаса" от использования локалок попросту нету, конечно же это не отменяет факта, что даже их стоит использовать с умом и их обнуление просто на просто необходимо, чтобы занятые регистры были очищены когда функция закончится и будет выполнена 0x27 (return) что повлечёт очистку стека. Да и фактически нет никакого оправдания тем, кто не обнуляет локальные "сложные" переменные, ибо их обнуление не добавляет нагрузки на JassVM и фактически уменьшает возможные утечки.
Хотелось бы внести поправку/дополнение (за что спасибо PTR153), в случае если вы создаёте объект и присваиваете его локалке, или же создаёте какой-либо объект внутри функции и присваиваете его локальной переменной, то её ОБЯЗАТЕЛЬНО нужно обнулить, в случае если созданный объект не был удалён и вы продолжаете его использовать, то всё что нужно - это присвоить его какой-нибудь глобальной переменной и вернуть её.
Краткий пример
function TestFunctionEx takes nothing returns nothing
    local location loc = Location( .0, .0 )
    set Loc = loc
    set loc = null
    call RemoveLocation( Loc )
endfunction
То бишь логика в том, что мы можем подать объект из локальной переменной в глобальную и далее уже без утечки провести с ней операцию.

Нативка X быстрее нативки Y!

раскрыть
Я думаю многие частенько встречались с обсуждениями скоростей нативок, и чаще всего под "удар" попадала GetUnitX против GetWidgetX, и общепринятно, что GetWidgetX быстрее чем GetUnitX. Однако, так ли это? Так давай же узнаем!
Для начала, я предложу Вам посмотреть псевдокод game.dll обоих функций:
Псевдокод GetUnitX и GetWidgetX
Как видите, он фактически идентичен, CUnit является расширением CWidget, однако обработка позиции делается всегда на уровне CWidget, а так как это расширенный класс, то разницы в скорости не может быть физически. Собственно потому и получается вот так:
Сравнение скорости GetUnitX и GetWidgetX
Обе функции заняли 5 мс на 10000 повторов, значит реальная задержка одного повтора 500 наносекунд +- 1-2 наносекунды погрешности. Конечно гигантский плюс GetWidgetX в её универсальности, ибо её можно применить ко всем видам виджетов без типизации, однако на этом её плюсы заканчиваются и в остальном она идентична GetUnitX.

Хештаблица медленная!

раскрыть
Это пожалуй один из самых ужасных мифов, которые просто на просто вводят меня в бешенство. Конечно же, этот миф "двоякий", ибо вопрос заключается в том, что в понятии "медленная" и в сравнении с чем? Зачастую те, кто используют vJass структуры, сравнивают хещтаблицу с этими псевдо-структурами, опираясь на то, что обращение к массиву - всегда быстрее чем обращение к хештаблице. Перед тем, как опровергнуть этот миф, я обязан согласиться с тем, что обращение к массиву конечно же быстрее, ибо JASM байткод вызывает 0x10 (getvar[]) и фактически берёт из уже "вшитого" списка функций по индексу, то бишь то, что делает хештаблица, только на более "низком" уровне.
Чтобы провести анализ скорости, давайте возьмём обычный массив чисел и число из хештаблицы и посмотрим эти варианты. Для простоты тестов, я не буду использовать StringHash для создания ключей, скорость с ними будет показан позже, и пример "ускорения" хештаблицы для особых фанатиков псевдоскорости.
Сравнение скорости сохранения значения
Сравнение скорости загрузки значения
Как мы видим getvar[] и setvar[] у массивов равен по скорости, точно так же как и Save равен по скорости Load у хештаблиц, потому далее будут сравниваться именно скорости загрузок (ибо мы чаще загружаем, чем сохраняем данные).
Смею предположить, что просмотрев примеры, будет вопрос "но тут же сравниваются нулевые ключи с последним индексом массива, так нечестно!", а что если я Вам скажу, что разница в значении числа создаёт минимальную погрешность и не более? Не верится? Так давай те же проверим!
"Честное" сравнение скорости загрузки значения
Как видите, скорость не изменилась, однако, если мы теперь рассмотрим варианты со StringHash, то разница будет более заметной. Рассмотрим в начале самый распространённый вариант применения (который в целом я использую у себя в Jass коде).
Сравнение скорости загрузки значение через StringHash
Ох как! Разница почти в 4.5 раз. Однако, не забывайте, что фактическая разница исчисляется на 1 вызов и не берёт во внимание таймер перебора всех значений в структуре, то бишь это сравнение строго одного массива и значения в хеше. Рассмотрим их фактические задержки, 3 мс / 10000 = 300 наносекунд и 13 мс / 10000 = 1300 наносекунд, то бишь борьба идёт за 1000 наносекунд, что является 0.001 мс или же 0.000001 секунды. Потому мне лично сложно представить, где эта разница даст о себе знать и как часто делается 10000 повторов или же вызовов чего-либо.
А теперь рассмотрим вариант "ускорения" хештаблицы, я думаю Вы прекрасно догадываетесь как же это можно сделать, но всё же рассмотрим это!
Ускорение хештаблицы
Как видите, разница в скорости упала вновь до +- прежнего значения с погрешность, что в итоге делает разницу вновь лишь в 2 раза, однако даже так - эта разница слишком минимальна, чтобы это вызывало каке-либо проблемы. Для сравнения, средняя функция мемхака порой в 20 раз медленнее нативок, но это не делает эти функции абсолютно непригодными для использования, ведь так?

Хештаблица занимает слишком много памяти игры!

раскрыть
Спасибо JackFastGame за напоминание об этом мифе. Для того, чтобы была возможность сравнивать или же оценивать занимает ли тот или иной объект много места, нужно найти его структуру и посмотреть, как игра работает с ними. Потому в начале стоит рассмотреть их фактические изначальные размеры, которые выделяет игра.
Размер менеджера хештаблиц
Размер хештаблицы
Для прояснения, игра создаёт менеджер Хештаблиц единожды, каждая хештаблица (всего их может быть 256) фактически - это дополнение внутриигровой хештаблицы (ибо не стоит забывать, что абсолютно все объекты в игре загружаются игрой из хештаблицы. Это в целом Вы можете увидеть достаточно подробно в МемХаке, где достаточно часто считываются объекты через их хешключи.
Пример такого в игре:
Хештаблицы игры
Но что же тогда делает хештаблица, которую мы можем создать в Jass? Она является ничем иным, как "дочерней" таблицей главной таблицы, которой выделено 0-255 ключей, то бишь 256 индексов, что можно увидеть тут:
Максимальной количество Хештаблиц
Собственно, если хештаблица не имеет никаких вписанных в неё значений, то её "изначальный" вес будет равен 0x28 байтам, далее каждый новый "хешключ" (который состоит из родительского и дочернего ключа) будет добавлять по 4 байта (то бишь никакого отличия от массивов). Исключение - это "стринг", там каждая буква = один байт, что опять же идентично обычным переменным.
Подводя итоги, можно смело сказать, что хештаблица не представляет собой какую-то громоздкую систему и её "засорение" всегда будет на руках её пользователя, однако хештаблица нам позволяет динамически очищать память, путём RemoveSavedHandle и прочего или же полной очистки дочерних ключей или же всей хештаблицы, что освободит всю занятую ей память.

Deg2Rad и Rad2Deg медленные!

раскрыть
Это как вы скорее всего и догадались очередной миф, который базируется вокруг "идеи", по которой прямое умножение джасс переменных должно быть быстрее нативок, однако это не так. Давайте начнём с примеров:
Пример Deg2Rad
Давайте теперь рассмотрим Rad2Deg:
Пример Rad2Deg
Результат одинаковый в обоих случаях, но почему же? Для этого нам нужно посмотреть в обработчик JASM байткода, который ссылается на опкод 0x22 (multiplication):
Обработкич Байткода
А теперь посмотрим на Deg2Rad:
Deg2Rad Code
В итоге получается, что умножение фактически превосходит вызов нативки по общим операциям, но всё это сводится к погрешности, которая банально делает эти операции равными. Такая же учесть следует и за Pow( dx, 2 ) в сравнении с dx * dx, они тоже равны по скорости.

Юниты жрут больше фпса чем Эффекты!

раскрыть
С выходом Warcraft 3 Reforged и с появлением API для эффектов, начал зарождаться вопрос, а что же в итоге эффективнее и меньше нагружает игру? Отвечу кратко, эффекты конечно же нагружают меньше, ввиду того, что эффект - это расширение CSpriteUber, а CUnit расширяет CWidget, который хранит не только CSpriteUber, а так же круги выделения, полосу здоровья, и прочую информацию. Говоря проще разница структур фактически колоссальная.
Однако, если вы думаете, что итог очевиден, то вы ошибаетесь. На деле игре фактически до шляпы, ибо без наведения мышки на юнита/выделения и если полосы здоровья скрыты, то нужная мощь GPU используется одинаково, как доказательство, как обычно предъявляю скриншоты.
Сравнение
Юниты:
Эффекты:
Подводя итоги, можно смело сказать, что фактически гигантской или же ощутимой разницы просто нет и мне не понятно откуда вообще взялся миф об этом. Но надеюсь это более чем опровергает этот миф.

Операция not true медленнее чем true == false (или схожие аналоги)

раскрыть
Данный миф существует с давних-давних времён, а если ещё точнее образовался с hiveworkshop, когда некто пытался замерять задержку исполнения функций (сама библиотека для измерений была в целом неточная) и множество тестов показали, что булевые операции с not оказывались медленнее чем == true или == false.
На деле это конечно же неправда и как минимум идёт в разрез с обще поставленной логикой, а если точнее - чем меньше байткода, тем быстрее он выполнится. Посмотрим же на приведённый пример.
Пример
На скриншоте мы видим две булевые операции not true и true == false, рассмотрим байткод:
Байткод (not true):
0C510800 - literal
00000001 - value
25610000 - operator not
00000000
2A510000 - jump if
00001483 - register
16000000 - call jass
00000A93 - jass function
2B000000 - jmp
00001484 - register
28000000 - label
00001483 - register
28000000 - label
00001483 - register
0C000000 - literal
00000000
27000000 - retn
Байткод (true == false):
0C510800 - literal
00000001 - va
13510000 - push 0x51
00000000
14530000 - pop 0x53
00000000
1A535352 - eq 0x53 0x53 0x52
00000000
2A530000 - jmpf
00001483 - where to
16000000 - call jass
00000A93 - func id
2B000000 - jmp
00001484 - where to
28000000 - label
00001483 - id
28000000 - label
00001484 - id
0C000000 - literal
00000000 - retval
27000000 - ret
Как вы можете увидеть, операция через not меньше не только по байткоду, но и по выделенным "данным", ибо в варианте true == false мы выделяем 2 литерала, один под true, другой под false и третий под результат, а для получения результата используется дополнительная команда eq (equals - равно).
Собственно выходит, что чем короче булевые операции, чем меньше в целом действий, тем она будет быстрее (что как обычно - логично).

Trigger​Add​Action утекает!

раскрыть
Очередной древний миф, который опять же пробрался к нам с далёких просторов hiveworkshop и был так же "перепроверен" на xgm. Однако, хоть этот миф и правдив, но правда немного в другом и виной тому, как обычно "божественные пальчики" программистов Blizzard Entertainment.
Начну с краткого, нет, сама TriggerAddAction не при чём, виной всему TriggerClearActions и DestroyTrigger, которые просто на просто не имеют кода деструктора самого Action, а вот TriggerRemoveAction его имеет! А это значит, если вы сохраните triggeraction в переменную и удалите его через TriggerRemoveAction, то никакой "утечки" не будет.
Перейдём к самим тестам!
Проверка "утечек"
Для начала проверим количество созданных хендлов после: CreateTrigger, TriggerAddAction и TriggerAddCondition.
Как видите, всего добавилось 30000 хендлов, ибо каждый цикл +3 хендла и всего 10000 циклов.
Посмотрим результат после DestroyTrigger:
Как вы можете заметить, удалилось лишь 20000 хендлов, вместо 30000, но почему же? Хоть я и дал ответ ранее, стоит рассмотреть эту проблему подробнее.
Давайте же посмотрим строго на TriggerAddAction и что же будет после DestroyTrigger:
Результат после DestroyTrigger
Как вы видите, хендлов всё так же 10000, а это значит, что TriggerClearActions не работает. Но почему же? - спросите вы, чтобы ответить на этот вопрос нужно для начала посмотреть на TriggerClearConditions, почему же он работает?
TriggerClearConditions
Большинству из вас не будет понятно куда нужно смотреть и что это вообще такое, я упрощу вам жизнь, просто запомните вот это:
(*(void (__stdcall **)(int, int))(**(uint32_t **)((signed int)result <= 0 ? 8 : result + 2) + 0x5C))( ); // CAgentWar3::Destroy
TriggerClearActions
Ничего не заметили? Если заметили, то можете похлопать себя по плечу, я горжусь вами! А если нет, то я вас понимаю, ибо разница очень и очень МАЛЕНЬКАЯ и заключается как раз в
(*(void (__stdcall **)(int, int))(**(uint32_t **)((signed int)result <= 0 ? 8 : result + 2) + 0x5C))( ); // CAgentWar3::Destroy
которой тут просто... нет... а это значит, что хендл triggeraction просто на просто не удаляется и остаётся в памяти игры.
Итог: утекает не TriggerAddAction, а отсутствие удаление деструктора triggeraction в DestroyTrigger и TriggerClearActions.

Kill​Sound​When​Done очищает sound хендл!

раскрыть
Не секрет, что в огромном количестве карт используются звуки, однако правильно ли они используются, почти во всех случаях - нет. Однако, виноваты в этом далеко не картоделы, а в очередной раз сами Blizzard.
Каламбур KillSoundWhenDone связан с тем, что да, оно удаляет выполняемый звук, однако оно не удаляет сам хендл, то бишь CSoundWar3 и так как нет нативки на прямую установку/замену звука, то остаётся висячий хендл.
Посмотрим на счётчик хендлов без и с KillSoundWhenDone:
KillSoundWhenDone Тест
Как видите на уровне хендлов разницы нет вообще, однако KillSoundWhenDone убивает сам звук, что всё-таки освободит память занятую непосредственно звуком.
А теперь посмотрим мою функцию RemoveSound, которая будет доступна в UjAPI начиная с 1.0.24.92 версии.
RemoveSound
По итогу, получается что используя стандартный метод вызова звука, а точнее:
function PlaySound takes string soundName returns nothing
    local sound soundHandle = CreateSound(soundName, false, false, true, 12700, 12700, "")
    call StartSound(soundHandle)
    call KillSoundWhenDone(soundHandle)
endfunction
Или же метод без утечки индекса:
function PlaySoundEx takes string soundName returns nothing
    local sound soundHandle = CreateSound(soundName, false, false, true, 12700, 12700, "")
    call StartSound(soundHandle)
    call KillSoundWhenDone(soundHandle)
	set soundHandle = null
endfunction
Мы всё-равно не решаем проблему утечки CSoundWar3, а это значит, что единственное верное решение (по-крайней мере на ванилле/рефе) - это каждый раз создавать уникальный звук для героя и т.д. и не удалять его через KillSoundWhenDone, а останавливать через StopSound( s, false, true ) и использовать его вновь, а не создавать новый, надясь на то, что он будет таки очищен.

Информация:

Что нужно для проверки скорости функций и прочего?

Всё достаточно просто, просто скачайте мой MemHackAPI отсюда: xgm.guru/p/wc3/memoryhackapi
В папке UselessTesting будет триггер Testing в нём есть функция TestBenchmarking, в первый и второй цикл вставьте функции, что вы хотите сравнить. Когда оба цикла завершается, будет высвечено сколько каждый из циклов занял в мс (миллисекунды). Оба цикла делаются 10000 раз, чтобы получить большее число без умножений и неточностей. Собственно, чтобы получить одиночную задержку, надо разделить полученное число на 10000 или установленное Вами значение.

Заключение:

За свою долгую историю, Warcraft 3 пожалуй создал пожалуй самое мощное начало для мододелов, что повлекло за собой уйму отдельных игр, которые ранее были картами в этой игре. Зачастую, многие создатели (включая меня) сильно недооценивали Jass в целом и всячески пытались найти тот или иной повод упрекнуть его в той или иной проблеме, не проведя нужных проверок, чтобы выявить реальную причину.
К моему большому сожалению, по сей день большинство картоделов опираются на те или иные мифы, которые услышали на Hive, а некоторые услышали что-то на xgm и других сайтах, и вроде как не от каких-либо случайных людей, а по факту проверенных, однако даже они могут ошибаться.
Потому, я очень надеюсь, что даже бегло прочитав мою статью, она развеет сложившиеся ложные впечатления о Jass и эти вопросы навсегда отпадут.
`
ОЖИДАНИЕ РЕКЛАМЫ...
3
20
11 месяцев назад
Отредактирован Unryze
3
TriggerSleepAction страдает от разности производительности процессоров, но уловить когда именно происходит десинх - не особо легко, ибо оно занимает разное время, всё что реально "железнобетонно" - это то, что разница по времени обычно в .0001мс или что-то в этом духе, короче говоря оно косячное - бесспорно, но измерять это практически невозможно.
TimerStart не имеет погрешностей, ибо создаётся отдельный "поток" для каждого таймера, проверять нечего.
TriggerRegisterTimerEventPeriodic использует Timer под капотом, опять же измерять нечего.
TriggerAddAction добавляет коллбек триггеру, который вызывает Timer который привязан через TriggerRegisterTimerEventPeriodic.
0
20
10 месяцев назад
Отредактирован Unryze
0
Последние новости!
Добавлено опровержение мифа, что KillSoundWhenDone очищает sound хендл/очищает утечку.
2
28
10 месяцев назад
2
Было бы неплохо обновить информацию про TriggerAddAction, так как там что-то странное в коде.
На первом, где мы смотрим просто кол-во хендлов, закомментены TriggerRemoveAction и TriggerRemoveCondition. На втором и третьем вместо них TriggerClearActions и TriggerClearConditions. При этом третий скрин подписан как будто там используется TriggerRemoveAction и TriggerRemoveCondition. Точнее там все эти функции почему имеют окончание s, что очень странно.
Четвёртый скрин подписан TriggerRemoveConditions, хотя показан код TriggerClearConditions.
По итогу не ясно, помогает ли TriggerRemoveAction избежать проблемы или нет.

А ну Unryze уже отвечал, но всё же было бы неплохо исправить названия функций и дополнить, что TriggerRemoveAction полностью очищает действие.
1
20
10 месяцев назад
1
PT153:
TriggerRemoveAction и TriggerRemoveCondition закомментированы, они не выполняются вообще. Весь смысл был показать, что они не делают ровно ничего для Actions.
А подпись скрина поправлю, забыл. Спасибо.
2
28
10 месяцев назад
2
Как вы видите, хендлов всё так же 10000, а это значит, что TriggerRemoveActions не работает. Но почему же? - спросите вы, чтобы ответить на этот вопрос нужно для начала посмотреть на TriggerRemoveConditions, почему же он работает?
Вот тут надо Remove на Clear заменить
0
20
10 месяцев назад
0
> Как вы видите, хендлов всё так же 10000, а это значит, что TriggerRemoveActions не работает. Но почему же? - спросите вы, чтобы ответить на этот вопрос нужно для начала посмотреть на TriggerRemoveConditions, почему же он работает?
Вот тут надо Remove на Clear заменить
Исправил, спасибо.
0
27
9 месяцев назад
0
Ну как я понял по теме KillSoundWhenDone, надо звук в глобалку записать. И проигрывать нужный звук. И потом запаузить. И снова переиспользовать запустить еще раз А если мне нужно несколько таких звуков одновременно проиграть, он модет проиграть?

0
28
9 месяцев назад
0
Создай несколько и потом переиспользуй
7 комментариев удалено
0
20
6 месяцев назад
Отредактирован Unryze
0
Удалена опровергнутая информация: " Однако есть исключения - группа, группа почему-то всегда и стабильно вызывает утечку в 1 байт, даже со схожим принципом." Оказалось что тест был относительно неверный и при создании 10к любого типа хендлов и затем их удалении, удаляется не совсем всё, а если точнее остаются "заготовки" памяти, которые будут выделены под новые хендлы этого же типа. Так как все "агенты/хендлы" работают по принципу LinkedList.
То бишь все хендлы создаются CAgentBaseAbs с подтипом и указателем на сам агент, при их удалении, очищаются хранимые данные, но память остаётся "занятой", чтобы последующий созданный хендл занял именно эту память.
0
14
6 месяцев назад
Отредактирован host_pi
0
тут исследование по скорости вывода BJDebugMsg и DisplayTimedTextToPlayer
тут еще один вопрос по скорости - Нагружает ли варик проигрывание музыки , эффектов , и просчёт переменных для неактивных слотов? (1. недобавленные слоты в карту, 2. открытые свободные слоты, 3. слоты на которых сидит компьютер)
т.е. есть ли смысл заморачиваться, обрезая работу всего кода только под слоты с активными игроками?
например если варик по дефолту обсчитывает весь код 12 раз (колво слотов в 1.26)
1
20
6 месяцев назад
1
тут исследование по скорости вывода BJDebugMsg и DisplayTimedTextToPlayer
тут еще один вопрос по скорости - Нагружает ли варик проигрывание музыки , эффектов , и просчёт переменных для неактивных слотов? (1. недобавленные слоты в карту, 2. открытые свободные слоты, 3. слоты на которых сидит компьютер)
т.е. есть ли смысл заморачиваться, обрезая работу всего кода только под слоты с активными игроками?
например если варик по дефолту обсчитывает весь код 12 раз (колво слотов в 1.26)
DisplayTimedTextToPlayer будет всегда быстрее, ибо это нативка, ну и конечно же, если игрок 1, то DisplayTimedTextToPlayer будет ещё быстрее, ибо вызовется ровно 1 раз. Для отображения текста всем хватает пропихивать GetLocalPlayer( ).
Однако эти выигрыши скорости не дают прямо большой буст, но они выгоднее в любом случае.
Неактивные слоты - никак не влияют ни на что. Всё и вся рисуется локально, если игрок не видит зону или если музыка не глобальная, то она у него и не воспроизведётся.
0
8
4 месяца назад
0
Не миф, но всё же любопытно, оказывает ли хоть какое-то влияние constant перед native/function?
0
20
4 месяца назад
0
Не миф, но всё же любопытно, оказывает ли хоть какое-то влияние constant перед native/function?
По моим проверкам - нет, я склоняюсь к тому, что они были скорее визуальным индикатором, что мол "в эти значения может писать только игра", не более того. Скорость вызова всегда одинаковая.
0
12
4 месяца назад
0
Где-то была инфа что обычные boolexpr не плодятся а кэшируются, но если юзать And Or Not (те которые для булекспров) то вот тогда они будут постоянно плодится. Хотя это не миф наверное.
2
20
4 месяца назад
2
Где-то была инфа что обычные boolexpr не плодятся а кэшируются, но если юзать And Or Not (те которые для булекспров) то вот тогда они будут постоянно плодится. Хотя это не миф наверное.
Не совсем так. And, Or и Not создают хендлы "самих себя", куда по итогу записывается исход и так на каждый вызов (что логично), такая же логика у Location, Rect ибо им неоткуда брать "заготовку", ибо координаты/данные динамические.
Хотя на деле Condition/Filter кэшируется лишь за счёт того, что количество функций ограничено, потому можно хранить сами функции и брать базируясь на ней кондишен. Что опять же логично. :)
Ответы (2)
0
17
1 месяц назад
0
Unryze, "количество функций ограничено" - то есть? Я не могу написать код с 100500 функциями и настанет лимит и новые функции не будут работать?
1
28
1 месяц назад
Отредактирован PT153
1
EugeAl, если у тебя в JASS коде N функций, во время работы карты их всегда будет N, ни меньше, ни больше. Вызов Filter и Condition на одной и той же функции не создают нового объекта, поэтому максимум новых объектов будет N. А вот And и Or каждый раз создают новый объект, поэтому кол-во оных не ограничено.
2
20
1 месяц назад
2
Иногда мне кажется, что Unryze в одиночку сделал больше чем близзы и сообщество за двадцать лет! Спасибо тебе за все, что ты для нас делаешь!)))
0
29
1 месяц назад
0
Иногда мне кажется, что Unryze в одиночку сделал больше чем близзы и сообщество за двадцать лет! Спасибо тебе за все, что ты для нас делаешь!)))
Сделать больше нуля не так уже и сложно.
Чтобы оставить комментарий, пожалуйста, войдите на сайт.