XGM Forum
Сайт - Статьи - Проекты - Ресурсы - Блоги

Форуме в режиме ТОЛЬКО ЧТЕНИЕ. Вы можете задать вопросы в Q/A на сайте, либо создать свой проект или ресурс.
Вернуться   XGM Forum > Warcraft> Барахолка
Ник
Пароль
Войти через VK в один клик
Сайт использует только имя.

Ответ
 
Sergey
Старейший
offline
Опыт: 44,363
Активность:
MSCV или SCV на массивах
Зацепили меня слегка слова Alexey B.H., который, как известно, сильно не любит кешь. Я провел серию собственных экспериментов. Прискорбно, но кешь действительно ведет себя крайне нехорошо, даже при условии, что к нему обращаются нечасто.
Но вообще говоря, я протестировал и немного другую систему - вместо кеша, параметры писались в строки. Результат - не лучше. На моем компе система не выдерживала где-то 20 быстро движущихся объеков на пустой карте. Игра тормозила насмерть, работала рывками.
И под конец я проверил скорость работы с массивами. Я создал аналог SCV, за исключением того, что все записи хранятся в массивах, а номер записи находится тупым перебором.
Впрочем, перебор не совсем тупой. Конечно, количество переборов нужно уменьшать. Для этой цели я во-первых, поделил записи на типы. Т.е. к примеру если запись относится к триггерам или таймерам, их луше отнести к одному типу, юниты - к другому. Таким образом, число переборов заметно снижается. Во-вторых, каждой записи соответсвует не 1 значение, а 30!
Да, это тоже довольно тупо, но эффективно. У меня имеется 30 массивов. 10 - integer, можно использовать и для указателей, 10 - real, 10 -string. Наличие такого числа массивов позовляет снизить общее число записей. Кроме того, это даже логично. Ведь когда мы делаем сопоставление триггеру, мы обычно делаем это для решения какой-то задачи. Нам нужно, к примеру, двигать такой-то юнит к такой-то точке. Мы можем все эти параметры сохранять в одну запись.
Каков результат? Впечатляющий. Даже несмотря на тупой перебор, я создавал до 90 одновременно движущихся юнитов с периодом 0.05. Только тогда начинается некоторое торможение, но даже оно горздо меньше, чем от кеша. Никаких рывков камеры и задержек при нажатии на f10.
Высылаю сценарий с некоторым описанием и демонстрацией работы. Кому интересно - пользуйтесь. Импортируется все это не сложнее, чем SCV (только поставьте галочку в параметрах редактора -создавать переменные при копировании триггеров).
Прикрепленные файлы
Тип файла: rar MSCV.rar (21.1 Кбайт, 109 просмотров )
Старый 31.10.2005, 00:49
NETRAT

offline
Опыт: 83,712
Активность:
Sergey ну да, здесь бы тебе пригодились хеш-функции. Забавно, а массивы безразмерные?
Старый 31.10.2005, 01:19
Sergey
Старейший
offline
Опыт: 44,363
Активность:
Прошу прощения, вновь произошла дезинформация :(.

Испытания с кешем были неточны, т.к. параллельно я опробывал запись параметров в строки и их расшифровку.

Если же поставить чистый кешь, то результат примерно одинакоый - что у массивов, что у кеша. Т.е. примерно при 100 движущихся объектах с периодом 0.05 начинается торможение. Никакой особой разницы не заметил на своем компе средней мощности:(. То ли что-то не так с алгоритмами...

Alexey B.H. говорит, что разница должа быть сильно заметна. Но ей богу, если все оптимизировано, то оба алгоритма выдерживают примерно одинаковую нагрузку с похожим результатом.
Старый 31.10.2005, 09:29
zibada

offline
Опыт: отключен
я вот честно, не догоняю, чем принципиально "обычные" переменные отличаются от кэша :/
имя обычой переменной - тоже ведь самая обычная строчка, которую надо считать и преобразовать в реальный адрес памяти.
(у нас ведь предварительной компиляции нет)
почему-то считается, что эта операция делается моментально, а ведь по сути это то же самое, что и кэш...
Старый 31.10.2005, 11:06
NETRAT

offline
Опыт: 83,712
Активность:
Однако могут возникать проблемы превышения этого самого лимита операции. Когда я использовал сортировку - ну бубльсорт (кажись там нужно 100 * 100 / 2 = 5000 итерации) так вот когда в каждой итерации была функция обращения к кэшу - лимит превышался после 14 отсортированных элементов. А когда я сначала перенес все из кеша в массивы и сортировал массивы, а потом выставлял результат в кэш, сортировка доходила до конца, однако утечки у мя были (как освобождать локальные массивы?!)
Старый 31.10.2005, 12:18
Sergey
Старейший
offline
Опыт: 44,363
Активность:
2DimonT, кешь точно медленне. Сам омжешь провести эксперимент:
n раз писать что-то в кеш и n раз в массив.
Увеличивая n оказывается, что с кешем тормоза начинаются значительно раньше.
Старый 31.10.2005, 14:31
zibada

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

а тормознутость и того, и другого должна расти линейно с кол-вом записей.
т.е. если мы создадим n разных глобальных переменных, тормоза будут такие же =) а в большой карте глобальных переменных бывает до нескольких [десятков] тысяч.

в общем, вопрос, имхо, требует дополнительного исследования =)
Старый 31.10.2005, 16:06
Sergey
Старейший
offline
Опыт: 44,363
Активность:
Я реализовал на 1 карте 4 способа:
1) Кешь со строками (самый медленный, т.к. со строками war3 работает медленнее, чем с кешем)
2) Чистые массивы с перебором записей (гораздо быстрее, но медленнее остальных способов)
3) Чистый RS+кешь (средняя скорость)
4) RS, массивы и особый способ нахождения modulo (самый быстрый, но возмоджны баги с совпадением индексов + одному объекту можно сопоставить лишь 1 запись).

При этом, если создавать периодические триеггеры (период 0.05) для движения юнитов, торможения для методов 2-4 начинается на числе 80-90 единиц. Торможение в разной степени, но тем не менее, при любом методе гейплей становится неиграбельным.

Если же не создавать периодические триггеры, а давайть команду созданному юниту идти в точку на карте, торможения не наблюдается.

Поскольку торможение в способах 2-4 начинается примерно с одного числа, вывод - торможение НЕ связано со способом получения и записи данных (массивы, кешь). Оно связано с торможением при выполнении внутренних команд war3 вроде вычисления синусов или команд вроде переместить юнит.

Общий вывод: для триггерных заклинаний с малым периодом, которые являются главным источником тормозов, не принципиально, каким способом обрабатывать информацию. Гораздо большее значение имеет набор базовых действий (чем проще, тем лучше) и отсутсвие утечек.

Предлагаю картостроителям провести эксперименты, какие триггерные действия вызывают торможение скрипта.

Отредактировано Sergey, 31.10.2005 в 17:06.
Старый 31.10.2005, 16:30
zibada

offline
Опыт: отключен
самое простое тестирование можно провести следующим методом...

запустить бесконечный цикл, увеличивающий переменную на 1, затем смотреть, на каком значении оно оборвалось =)

вот первые результаты:

1. для глобальных переменных
Код:
function bench_globals takes nothing returns nothing
   set udg_i = 0
   loop
      set udg_i = udg_i + 1
   endloop
endfunction

результат: 42856

2. кэш.
Код:
function bench_cache_1 takes nothing returns nothing
   set udg_cache = InitGameCache("test")
   call StoreInteger(udg_cache, "test", "i", 0)
   loop
      call StoreInteger(udg_cache, "test", "i", GetStoredInteger(udg_cache, "test", "i") + 1)
   endloop
endfunction

результат: 14999

как видно, для при записи одной переменной разница менее чем 3-кратная.


сейчас попробую проверить на более "реальных" ситуациях с большим кол-вом записей.
Старый 31.10.2005, 16:53
Sergey
Старейший
offline
Опыт: 44,363
Активность:
DimonT, все это тестировали много раз. Да, результаты говорят, что кешь от 3 до 10 раз медленее массивов. Но дело вовсе не в том. Как я исследовал выше, внутренние процедуры вара тормозят игру гораздо сильнее, чем обращения к массиву и кешу. Так что именно скорость этих внутренних команд в конечном счете окажется решающей.
Старый 31.10.2005, 17:04
zibada

offline
Опыт: отключен
кстати, а ведь проявляются очень интересные вещи....

3. вызов функции.

Код:
function increment_i takes nothing returns nothing
   set udg_i = udg_i + 1
endfunction

function bench_call takes nothing returns nothing
   set udg_i = 0
   loop
      call increment_i()
   endloop
endfunction

результат: 33333

4. вызов функции с передачей/возвратом значения.

Код:
function increment takes integer i returns integer
   return i + 1
endfunction

function bench_call_2 takes nothing returns nothing
   set udg_i = 0
   loop
      set udg_i = increment(udg_i)
   endloop
endfunction


результат: 21428
(т.е. уже ровно в ДВА раза хуже простого присваивания)

5. вызовы функций типа "get / set"
Код:
function set_i takes integer i returns nothing
   set udg_i = i
endfunction

function get_i takes nothing returns integer
   return udg_i
endfunction

function bench_call_3 takes nothing returns nothing
   set udg_i = 0
   loop
      call set_i(get_i() + 1)
   endloop
endfunction

результат: 18750
(всего на 25% быстрее кэша!)

наглядно видно, что тормознутость кэша в случае 2 обусловлена не столько временем работы native-функций, сколько затратами на их вызов.
(т.к. там механизм такой же, как и в случае 5)

DimonT добавил:
и, напоследок, чтобы окончательно "добить".

6. добавим get/set функциям список параметров от native-функций кэша:

Код:
function set_i_ex takes gamecache cache, string key1, string key2, integer i returns nothing
   set udg_i = i
endfunction

function get_i_ex takes gamecache cache, string key1, string key2 returns integer
   return udg_i
endfunction

function bench_call_4 takes nothing returns nothing
   set udg_i = 0
   loop
      call set_i_ex(udg_cache, "test", "i", get_i_ex(udg_cache, "test", "i") + 1)
   endloop
endfunction


результат: 8571
(в 1.5 раза МЕДЛЕННЕЕ кэша)

вот так вот..

вывод: при небольшом количестве хранимых значений время разбора игрой скрипта в разы превышает время работы внутренних функций кэша.

вопрос тормознутости при больших объемах сохраняемой информации пока остается открытым.
Старый 31.10.2005, 17:48
Sergey
Старейший
offline
Опыт: 44,363
Активность:
Результаты беглого тестирования:
(!)- действие Move Unit начинает тормозит при 200 применениях за каждые 0.05. Т.е. больше 200 юнитов двигать триггерно нецелесообразно!
- действие записи/чтения в кешь тормозит при 2000 примнений, не тормрозит при 1000.
- действие созданить точку и удалить точку тормозит при 2000 применений
- действие вычислить синус тормозит при 5000 применений
(!)- действие call ExecuteFunc(...) тормозит при 1000 применений.
- операции со строками(сложение строк, перевод числа в строку) - тормозит после 4000 применений
- изменить высоту юнита - слегка притормаживает при 2500 применениях
(!)- условный оператор с одним условием проверки - притормаживает при 2000 применениях
(!)- перебор юнитов в группе, состоящей из 1 человека - ощутимо приормаживает уже при 1000 применений
- действие issue order damage target - тормозит при 2000 применений
- действие move region притормаживает при 5000 применений.

Итого: каждая из этих функций может фигурировать в триггере с малым периодом. Тот факт, что такие триггеры тормозит кешь считаю опровергнутыми. Обычно для кеша хватает 3-7 применений на 1 юнит. Но сколько еще других операций есть в каждом скрипте? Любая из них не более, чем в 2-4 раза быстрее кеша, а некоторые - так гораздо медленнее! Проверки условий, вычисление каких-то функций, работа с группами, вызов функций - все это тормозит сильнее кеша.

Это и есть ответ для тех, кто приводит Seal Master, как пример торможения кеша. Не кешь там тормозит, а сложный и длинный алгоритм + перемещение большого числа юнитов действием move unit.

Выод спецам-jass -ерам: упрощайте алгоритмы. Это единственный надежный способ ускорить скрипт.

Sergey добавил:
Димон, еще один потрясающий факт: торможение не имеет прямого отношения к числу максимально возможных применений!

К примеру, зациклив до бесконечности действие move unit максимальное число применений составит ~24000.
Для действия r = sin(1) это число будет всего лишь ~16000. НО! при выполнении цикла с действием move unit игра затормозит на несколько секунд, а при вычислении синусов все будет мгновенно. Причем даже если время перемножить в 1.5 раза, все равно получится гораздо меньше времения ожидания, чем для move unit.

ВЫВОД: команды скриптов отличаются друг от друга не только максимальным числом применений (которое можно вычислить), но и реальной скоростью выполнения (которую можно определить лишь на глазок).
Старый 31.10.2005, 18:49
NETRAT

offline
Опыт: 83,712
Активность:
Мда, что-то типа 'цена' и 'скорость'. Однако для функции set эта цена оказывается намного меньше чем для пары getstored/setstored
Старый 31.10.2005, 23:46
TiM
Старичок
offline
Опыт: 8,594
Активность:
Забей, ГЛ
Старый 01.11.2005, 03:40
zibada

offline
Опыт: отключен
Sergey, я это уже обнаружил...

итак, есть два разных, не связанных между собой метода торможения:
1. лимит действий, по исчерпании которого игра убивает поток.
вызов любой native-функции отнимает одинаковое количество таких операций.
такое "торможение" легко можно точно измерить для любого куска скрипта, что и было проделано выше.
2. обычное торможение, воспринимаемое "на глазок".
его точно измерить невозможно, только вызывать каждую функцию по неск. тысяч раз и сравнивать приблизительное время работы...

так вот.

с кэшом же проявляются еще более интересные вещи.

оказывается, сильнее всего тормозят следующие операции:
- запись НОВОГО ключа, тормознутость растет пропорционально общему числу записей.
(если в функции разом добавлять по 10000 новых записей, то каждый последующий ее вызов у меня выполнялся на 2-3 сек дольше предыдущего)
- первая попытка чтения любого ключа, в том числе несуществующего.

НО! следующие операции работают быстро (тормоза даже при работе с 10000 записей подряд не заметны):
- модификация записи с ранее использовашимся ключом;
- чтение уже однажды прочитанной записи (даже если она модифицировалась).


прикладываю тестовую карту, на которой все это измерял...
желающие могут сами попробовать дважды набрать строчку "write 0 10000" и убедиться, что на второй раз тормозить будет меньше.
(каждый раз сохраняются случайные числа под последовательно идущими ключами)
Старый 01.11.2005, 11:47
NETRAT

offline
Опыт: 83,712
Активность:
DimonT брр, а native функции самые низкие по цене? или у них цена просто для всех константа?

Ну это можно обьяснить тем что при записи нового ключа - фактически расширяется кэш (может там индексация или поиск/сортировка проходят). А первая попытка - фик его знает, может там используются хеш-таблицы?! Эти предположения обьясняют пункты "НО!"

Да, я такое же при повторной инициализации инва наблюдаю...
Старый 01.11.2005, 12:26
zibada

offline
Опыт: отключен
видимо, там юзается хэш-таблица, автоматически увеличивающая свой размер при переполнении...
т.к. при каждом увеличении требуется полная ее перестройка, отсюда и тормоза.
здесь есть некоторая аналогия с mpq - размер хэш-таблицы изначально фиксируется, и чтобы его изменить, надо переупаковывать все файлы (это же делает функция "Compact" в WinMPQ).
тормознутость первого чтения может быть обусловлена тем, что при чтении несуществующего ключа он создается (возможно, вызывая тем самым очередную перестройку таблицы), и ему сопоставляется нулевое (null, 0, false, "" - в зависимости от типа) значение.
но это лишь догадки..
Старый 01.11.2005, 17:36
Sergey
Старейший
offline
Опыт: 44,363
Активность:
Димон, я долго искал и все таки нашел причину торможения wisp swarm. Тестировал на скорость каждую команду, а причина то была перед глазами. Вообщем заклинание пало смертью храбрых в борьбе... со спецэффектами.

Димон,ты конечно говоришь сам о себе "лентяй", но чтобы настолько... :)

Твои дикие виспы с периодом 0.02 секунды создают и уничтожают по 1 спецэффекту. Еще спецэффект создается при столкновении виспа с врагом.

Я протестировал спецэффекты в триггерах с малыми периодами. Если период 0.05, то всего лишь 10 создаваемых и уничтожаемых создаваемых спецэффектов тормозят игру очень сильно. Я в твоем скрипте просто отключил спецэффекты и тормоза исчезли даже при 60 виспах. Точнее некоторые тормоза наблюдаются - но они другого рода. Не стоит забывать - у тебя на экране 30 крипов и 15 виспов и герой, и все движутся. Если снизить число крипов, то тормоза также исчезнут даже при большом количестве виспов.

Димон, лень тебя подвела :). Проще было сделать отдельный юнит - быстрый висп с отдельной моделью. Уже этого достаточно, чтобы убрать тормоза.

--------
Попутно я получил довольно интересные для jass-еров результаты. Дело в том, что юнит можно двигать действием MoveUnit, но есть и другое действие - SetUnitX, SetUnitY. Оказывается, что эти действия ГОРАЗДО быстрее, чем MoveUnit. Если MoveUnit тормозит при 200 применениях, то SetUnit... тормозят где-то при 600 и более числе применений. Так что делать много движущихся объектов возможно.

Второй интересный вывод: я протестировал скорость перемещения юнитов при создании ттриггера с событием Time periodic и при помощи таймеров. Таймеры показывают большую быстродейственность (хотя, возможно учитывались не все возможные факторы. Например, общее число скриптов может повлиять на скорость работы таймеров, т.к. один из аргументов таймера - функция, запускаемая по его окончании).

Вывод: на данный момент времени набиболее быстрый алгоритм перемещения юнита-снаряда без торможения достигается таймерами + командами SetUnitX, SetUnitY. Кстати, именно такой метод и реализован в wisp swarm.

Другие факты, которые удалось откопать:
1. Я несколько ошибся с оценкой тормознутости условий. Если брать условный оператор на jass без вызова левых функций, то для торможения нужно использовать 10000 условных операторов каждые 0.05 секунд. Впрочем, функции внутри оператора, очевидно, также снижают скорость. Т.е. скорость ограничена в основном скоростью используемых в проверке функций.
2. В варе обнаружились и другие методы торможения. Например, периодически с большой частотой менять цвет юнита, то игра вроде бы не тормозит. Но если перекрашиваемфый юнит начнет двигаться - все повиснет на жутких тормозах.
Еще открыт такой вот баг: я создавал и удалял n триггеров с событием timer expires. Если число n=200, то игра почти мгновенно зависает. Если n=50, то игра игра зависает, но не сразу, а постепенно. Причем такое поведение наблюдается только для динамически создаваемых триггеров таким событием. Похоже на какой-то баг разработчиков.
Правда, динамически создавать триггеры с таким событием вряд ли кому-нибудь понадобится.

Отредактировано Sergey, 02.11.2005 в 11:19.
Старый 02.11.2005, 09:59
zibada

offline
Опыт: отключен
Цитата:
Димон, я долго искал и все таки нашел причину торможения wisp swarm.

вот сколько раз я, интересно, писал, что тормозят там именно эффекты, а не что бы то ни было еще.
только тормоза напрямую зависят от модели спецэффекта, поэтому точно измерять здесь что-то невозможно..
Старый 02.11.2005, 11:29
Sergey
Старейший
offline
Опыт: 44,363
Активность:
:)
Димон, но ты не писал, что создаешь 50 эффектов в секунду на каждого быстрого виспа + 1-15 эффектов при столкновении виспа с врагом, которое в толпе вргаов происходит раза 3 в секунду для каждого виспа.
Вот так и появился миф про тормознутый кеш :).
Старый 02.11.2005, 11:35
Ответ

Опции темы Поиск в этой теме
Поиск в этой теме:

Расширенный поиск

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

BB-коды Вкл.
[IMG] код Вкл.
HTML код Выкл.
Быстрый переход



Часовой пояс GMT +3, время: 00:56.