RS Game Maker Community
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

 
Расширенный поиск
  Главная  Форум   Вики Блоги FAQ Игры Статьи Примеры Войти Регистрация  
Вики
Все желающие приглашаются редактировать вики. Можете писать о своих (или чужих) играх, размещать туториалы, постить статьи - главное навесить категорию.
Страниц: 1   Вниз
  Печать  
Заметки о производительности
0 Пользователей и 1 Гость смотрят эту тему.
Райдо
Старожил
******

Репутация: 88
Offline Offline

Награды:
Легенда сайта
Сообщений: 2 631


05 Октября 2014 в 14:24
Собственно это не статья, а набор важных и не очень данных на эту тему.
  • Производительность отрисовки через стандартные функции спрайтов равна скорости рендерения текстурированных квадов с ручной установкой трансформаций: основная доля расходов составляет создание матрицы преобразования, и в обоих случаях эти расходы будут одинаковы. Разница становится заметна только при огромных размерах квад-меша, сотни и тысячи квадов со спрайтами. При этом, разумеется, меш должен быть как минимум готов, а лучше - заморожен, ибо добавление тысячи квадов динамически очевидно имеет высокую вычислительную стоимость.
  • Отрисовка сурфейсов (точнее подготовка к их отрисовке) занимает невразумительно огромное количество времени по сравнению с отрисовкой спрайтов, нужно использовать их по минимуму.
  • Операции семплинга текстур сильно тормозят видеокарту - программное вычисление десятков операций аналогично скорости сэмплинга одного текселя. Поэтому, например, круги можно рендерить через шейдер, который программно вычисляет цвет пикселя в зависимости от его дистанции от центра, отрисовывая 1х1 спрайт растянутый под масштаб круга. В качестве бонуса получаем очень красивую и точную картинку без изломов которая отлично выглядит при любом масштабе, с нужной толщиной линии и прочими радостями.
  • В силу неизвестных причин, рендерение геометрии через функции типа draw_circle катастрофически ухудшает производительность - их нужно избегать. Как было сказано выше, все типы геометрии можно рендерить программно в шейдере, да и результат лучше.
  • Чрезмерный своппинг текстур значительно понижает производительность даже на ПК, так что имеет смысл разнести рендерение разных атласов по разным временным промежуткам - не стоит, например,   рендерить спрайт из атласа А и там же рендерить текст из атласа В, так как на каждый такой объект будет по два свопа текстуры (минимум). При очень большом количестве таких объектов (например РПГ с кучей врагов, где над ними отображаются урон от ударов) производительность очень проседает просто от своппинга текстур. Если в комнате есть спрайты из разных атласов, то нужно задать соответствующим объектам глубину так, чтобы сначала рендерились объекты из одного атласа, потом из другого, потом из третьего и т.п. В мобильных устройствах эффект более ярковыраженный.
  • Установка юниформов в шейдере может запросто занимать больше времени, чем собственно выполнение отрисовки с этим шейдером.
  • Вообще, современные видеокарты имеют тенденцию к буферизации, так что можно очень нагрузить видеокарту при низком расходе времени на эвент отрисовки, так как данные уйдут быстро и будут обрабатываться потом, но управление уже будет передано обратно программе для дальнейших вычислений.
  • При создании кастомного вертексформата, нужно знать, что стандартные элементы (цвет/нормаль/позиция/текстура) должны именоваться именно так, как в шейдере-пустышке. Кастомные элементы добавляются в том же порядке, что и объявляются в шейдеры (именование не важно), но единственный usage, который реально работает, это vertex_usage_textcoord - скорее всего это баг. В результате другого бага, гамак выблёвывает в локацию последнего импортированного ресурса содержимое последнего созданного шейдера, пропущенного через препроцессор - там видно, что у всех кастомов тип данных указан TEXTURECOORD.
  • Создание матрицы динамически даже в вертекс-шейдере создаёт неимоверные тормоза.
  • При отрисовке стандартными функциями, в in_Position оказываются координаты вертекса в комнате. Атрибут in_TextureCoord содержит координаты на атласе, поэтому просто так спрайт тайлить не получится, зато можно заезжать на "чужую" территорию и рендерить мусор.
  • Вместо создания графики с предумноженной альфой, можно просто создать шейдер, который перед отрисовкой пикселя будет домножать его цвет на его же альфу. В моих тестах, разница в производительности была неизмеримой (см. выше про семплинг против мат.операций). Впрочем, большинство редакторов поддерживает плагины, и среди них наверняка найдётся предумножитель.
  • Создание шрифта из импортированного в редакторе спрайта просто инициализирует данные из изображений, символы будут рендериться из исходного атласа. Динамически загружаемые спрайты помещаются в свою личную текстуру и отрисовка такой графики создаёт свопы.
  • При операциях над 4-мерными векторами позиции надо помнить, что четвёртая переменная отвечает за перспективу после умножения на матрицу, x - позиция на экране по горизонтали, y - по вертикали, z - в глубину, w - множитель перспективы, причём даже в "2д" режиме, в результате случайных операций над этой компонентой будут возникать очень странные графические баги, которые очень тяжело отловить.

Пока что всё.
Последнее редактирование: 06 Октября 2014 в 21:52 от Райдо

 
ELGReeN
Активный участник
**

Репутация: 8
Offline Offline

Сообщений: 109


Ответ № 1 21 Марта 2016 в 19:29
Набрел на тесты коней https://habrahabr.ru/post/66562/
Решил воспроизвести в ГМС таким кодом:

t=current_time;
    r = 0;
    for (i = 0; i < 10000; i++) {
        for (j = 0; j < 10000; j++) {
            r = (r + (i * j) % 100) % 47;
        }
    }
t=current_time-t;
    show_message(string(t/1000));

Простые запуски дали такие цифры, замерял на глазок:
Win 49sec
YYC 19sec (повторный запуск кода без перезапуска игры дали уже 9сек что сопоставимо с результатом С++)
Правда у меня нормальный проц. i5, но все равно радует что ГМС не такой уж и медленный.


Последнее редактирование: 21 Марта 2016 в 20:09 от EL-GReeN-U

Райдо
Старожил
******

Репутация: 88
Offline Offline

Награды:
Легенда сайта
Сообщений: 2 631


Ответ № 2 21 Марта 2016 в 22:59
Что, кстати, говорит о том, что ненужные вычисления не оптимизируются нафиг из кода. Так что математические выражения нельзя писать как попало и рассчитывать что компилятор сам исправит. Кстати в крестах такой код будет оптимизирован в нуль, так что надо юзать волатильную переменную, а это само по себе сильно снижает производительность даже в оптимизированном коде из-за невозможности держать переменную в регистре и необходимости каждый раз читать её из памяти и тут же писать её обратно.

ГоК
Начинающий
Старожил
******

Репутация: 360
Offline Offline

Награды:
Легенда сайтаЗа интересные проекты
Сообщений: 2 013


Ответ № 3 21 Марта 2016 в 23:20
Правда у меня нормальный проц. i5, но все равно радует что ГМС не такой уж и медленный.
Для чистоты результата, хорошо бы запустить GML и C++ коды на одной и той же машине.
Последнее редактирование: 22 Марта 2016 в 10:46 от Райдо
ELGReeN
Активный участник
**

Репутация: 8
Offline Offline

Сообщений: 109


Ответ № 4 23 Марта 2016 в 00:11
Правда у меня нормальный проц. i5, но все равно радует что ГМС не такой уж и медленный.
Для чистоты результата, хорошо бы запустить GML и C++ коды на одной и той же машине.
Нашелся бы доброволец...

Ява и с++ можно, на современных виндах оно есть, нужны скомпилированные программы которые покажут сколько сек. отрабатывало.
Остальное это нужно накатывать софт и шарить в них. Вчера пхп не заработал на денвере, бага. Питон виснет. C++ .exe отрабатывает слишком быстро и на глазок не понятно, ну 0.5-2сек. Перл и руби вообще без понятия что оно такое, бросил.

Кто-тo
Ветеран форума
*****

Репутация: 80
Offline Offline

Сообщений: 1 076


Ответ № 5 23 Марта 2016 в 00:56
i3-3120M, 2.5 ГГц
[~]$ g++ --version
g++ (GCC) 5.3.1 20151207 (Red Hat 5.3.1-2)
[~]$ g++ test.cpp; ./a.out
Time: 2.364714
[~]$ g++ -O2 test.cpp; ./a.out
Time: 0.000001

ПоказатьИсходный код
#include <stdio.h>
#include <ctime>

int main()
{
    clock_t t=clock(); //current_time
    int i,j,r;
    for (i=0; i<10000; i++)
    {
        for (j=0; j<10000; j++)
        {
            r=(r+(i*j)%100)%47;
        }
    }
    t=clock()-t; //current_time-t;
    printf("Time: %f\n", (float)t/CLOCKS_PER_SEC);
}
Если же использовать volatile int r;
[~]$ g++ test.cpp; ./a.out
Time: 1.891216
[~]$ g++ -O2 test.cpp; ./a.out
Time: 1.739450
Последнее редактирование: 23 Марта 2016 в 01:09 от Кто-тo
Огион
Завсегдатай
****

Репутация: 121
Offline Offline

Сообщений: 923


Ответ № 6 23 Марта 2016 в 01:57
Я как-то задался вопросом о том, насколько же GM медленнее Си. Решил ещё раз попробовать запустить.
Машина:
Windows 8.1 x64
Intel Core i5 4690K 3,50 ГГц
GeForce GTX 980

GM 8.1:
Около 115 fps.

Си (GCC с оптимизацией O2):
Около 170 fps.

GM Studio:
Около 355 fps!

Прирост производительности в GMS впечатляет — и это без YYC. Примечательно также, что даже мой ужасный говнокод на Си работает быстрее максимально оптимизированного кода в GM 8.1.
Прикреплённые файлы
* loop_test.7z (3741.77 Кб - загружено 34 раз.)
input.txt
Не очень
Активный участник
**

Репутация: 14
Offline Offline

Сообщений: 102


12.8 Gb

Ответ № 7 23 Марта 2016 в 03:12
Так теперь на гамаке можно операционные системы писать, раз он быстрее С !

<оффтоп> А что это у тебя в подписи?
Райдо
Старожил
******

Репутация: 88
Offline Offline

Награды:
Легенда сайта
Сообщений: 2 631


Ответ № 8 23 Марта 2016 в 12:52
Ну ты вообще-то не производительность языков замерял, а движков, причём в сишном коде у тебя самописный супер-неоптимизированный движок на базе еще одного фреймворка.

Когда я в прошлый раз измерял скорость вычислений, Сишный код был в 200 раз быстрее гамачного. То был GM7 правда. Да и тест был не помню насколько кривой.

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

int main ( int argc, char ** argv )
{
    volatile int x, y, z, v;
    struct timespec timer;

    timer_start ( &timer );
    for ( x = 0; x < 1000; x++ )
        for ( y = 0; y < 1000; y++ )
            for ( z = 0; z < 1000; z++ )
                v += x + y + z;
    timer_stop ( &timer );

    printf ( "calculation took %ld usec", timer.tv_nsec / 1000 );
    return 0;
}
468075 микросекунд, за полсекунды управился. Для 4.6 ГГц процессора неплохо ящетаю. Инспектирование промежуточного асмокода подтверждает, что он по-честному крутит миллиард циклов.

var xx, yy, zz, vv;

var t = get_timer ( );
for ( xx = 0; xx < 1000; xx++ )
    for ( yy = 0; yy < 1000; yy++ )
        for ( zz = 0; zz < 1000; zz++ )
            vv = xx + yy + zz;
t = get_timer ( ) - t;

show_message ( "calculation took " + string ( t ) + " usec" );
Заняло это дело 323058148 микросекунд. 5 минут с половиной почти. Хвалёный УУЦ не проверял, не установлена студия и прочая шняга которая там требуется для компиляции.

Разница на лице, что называется. Ну и не забываем про то, что мультитрединга в гамаке не видать как своих ушей.
Последнее редактирование: 23 Марта 2016 в 14:00 от Райдо

Огион
Завсегдатай
****

Репутация: 121
Offline Offline

Сообщений: 923


Ответ № 9 23 Марта 2016 в 14:22
Ну да. По-хорошему нужно замерять время выполнения конкретного участка кода, а не максимальный FPS, и сишную реализацию тоже сделать на битмпах с трансформацией. Но мне лень ставить Аллегро.
<оффтоп> А что это у тебя в подписи?
А это мы как-то играли в шифры. Мою подпись так никто и не расшифровал, хотя я и открыл секрет шифра.
Последнее редактирование: 23 Марта 2016 в 17:05 от Огион
ELGReeN
Активный участник
**

Репутация: 8
Offline Offline

Сообщений: 109


Ответ № 10 24 Марта 2016 в 10:42
Ну и не забываем про то, что мультитрединга в гамаке не видать как своих ушей.
Какие можно придумать костыли?
Запуск 2+ программ и их синхронизация, только вот как синхронизировать их так что бы не потерять выигрыш от мильтрединга?
Может есть расширения.

input.txt
Не очень
Активный участник
**

Репутация: 14
Offline Offline

Сообщений: 102


12.8 Gb

Ответ № 11 24 Марта 2016 в 13:07
Ну, формально, многопоточность в студии есть - см. события из вкладки Asynchronous.
А ещё есть GMThreads.
Кто-тo
Ветеран форума
*****

Репутация: 80
Offline Offline

Сообщений: 1 076


Ответ № 12 14 Апреля 2016 в 19:32
i3-3120M, 2.5 ГГц
[~]$ g++ test.cpp; ./a.out
Time: 2.364714
[~]$ g++ -O2 test.cpp; ./a.out
Time: 0.000001

Если же использовать volatile int r;
[~]$ g++ test.cpp; ./a.out
Time: 1.891216
[~]$ g++ -O2 test.cpp; ./a.out
Time: 1.739450

[~]$ g++ test.cpp; ./a.out
Time: 1.025189
[~]$ g++ test-volatile.cpp; ./a.out
Time: 0.757309
[~]$ g++ -O2 test-volatile.cpp; ./a.out
Time: 0.728492


Тот же самый процессор (i3-3120M, 2.5 ГГц), но исправил тормоза в системе о которых не знал.
Прошу прощения за ложные результаты.
Райдо
Старожил
******

Репутация: 88
Offline Offline

Награды:
Легенда сайта
Сообщений: 2 631


Ответ № 13 14 Апреля 2016 в 20:25
Вообще-то флаг -О2 включается по умолчанию. Имеет смысл юзать флаги -O0, -Os и -Ofast.

Раз у тебя не-волатильный тест работает медленнее, чем волатильный, прозреваю что у тебя код кривой. У меня не-волатильный тест выполняется мгновенно, ровно за 0 микросекунд. Потому что компилятор полностью убирает весь код, выполнение которого не несёт функциональной нагрузки.

Кто-тo
Ветеран форума
*****

Репутация: 80
Offline Offline

Сообщений: 1 076


Ответ № 14 14 Апреля 2016 в 21:45
Вообще-то флаг -О2 включается по умолчанию. Имеет смысл юзать флаги -O0, -Os и -Ofast.
-O0
0.956336
-Os
0.000001
-Ofast
0.000001
-O1
0.043226
-O2
0.000001
Volatile:
-O0
0.754985
-Os
1.194613
-Ofast
0.705793
-O1
0.730279
-O2
0.704156
У меня похоже что по-умолчанию используется -O0.

Раз у тебя не-волатильный тест работает медленнее, чем волатильный, прозреваю что у тебя код кривой. У меня не-волатильный тест выполняется мгновенно, ровно за 0 микросекунд. Потому что компилятор полностью убирает весь код, выполнение которого не несёт функциональной нагрузки.
Код не мой, я просто добавил таймер.
ELGReeN
Активный участник
**

Репутация: 8
Offline Offline

Сообщений: 109


Ответ № 15 17 Октября 2016 в 12:17
Ну ты вообще-то не производительность языков замерял, а движков, причём в сишном коде у тебя самописный супер-неоптимизированный движок на базе еще одного фреймворка.

Когда я в прошлый раз измерял скорость вычислений, Сишный код был в 200 раз быстрее гамачного. То был GM7 правда. Да и тест был не помню насколько кривой.

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

int main ( int argc, char ** argv )
{
    volatile int x, y, z, v;
    struct timespec timer;

    timer_start ( &timer );
    for ( x = 0; x < 1000; x++ )
        for ( y = 0; y < 1000; y++ )
            for ( z = 0; z < 1000; z++ )
                v += x + y + z;
    timer_stop ( &timer );

    printf ( "calculation took %ld usec", timer.tv_nsec / 1000 );
    return 0;
}
468075 микросекунд, за полсекунды управился. Для 4.6 ГГц процессора неплохо ящетаю. Инспектирование промежуточного асмокода подтверждает, что он по-честному крутит миллиард циклов.

var xx, yy, zz, vv;

var t = get_timer ( );
for ( xx = 0; xx < 1000; xx++ )
    for ( yy = 0; yy < 1000; yy++ )
        for ( zz = 0; zz < 1000; zz++ )
            vv = xx + yy + zz;
t = get_timer ( ) - t;

show_message ( "calculation took " + string ( t ) + " usec" );
Заняло это дело 323058148 микросекунд. 5 минут с половиной почти. Хвалёный УУЦ не проверял, не установлена студия и прочая шняга которая там требуется для компиляции.

Разница на лице, что называется. Ну и не забываем про то, что мультитрединга в гамаке не видать как своих ушей.
Позапускал ваш код ГМ(проц ноутбучный i5 3210m 2.5-3.1GHz сам разгоняется):
GMS 1.4.1763
запуск/время/приоритет реального времени
win YYC без компиляции
1) 42 150467 usec
2) 43 613264 usec
3) 41 919298 usec
4) 41 987158 usec приоритет

win YYC с компиляцией
1) 41 827340 usec
2) 41 810821 usec
3) 41 427242 usec приоритет

win простой без компиляции
1) 305 783632 usec

win простой с компиляцией
1) 303 741563 usec приоритет

HTML5
Edge
1) 835000 usec
2) 785000 usec
3) 803000 usec
4) 706000 usec

SeaMonkey
1) 727000 usec
2) 714000 usec
3) 711000 usec
4) 710000 usec

Chrome
1) 1416000 usec
2) 1083000 usec
3) 1126000 usec
4) 1129000 usec

Примерно:
Win YYC 42 сек. - быстрее Win в 7 раз
Просто Win 304 сек.
Си 0.497 сек. - быстрее Win в 611 раз, и в 84 раза чем YYC, и в 1.8 раз чем броузеры
Броузеры 0.895 сек. - быстрее в 339 раз чем Win, и в 46 чем YYC // Почему броузеры такие быстрые?

Скомпилить ваш сишный код не смог, поэтому пришлось так:
Ваш Win 323 сек. - это на 6.25% медленнее моего
Ваш Си 468075 usec * 1.0625 = 497329 usec (0.497 сек.)
Дадите экзешник проверю более четко.
Последнее редактирование: 17 Октября 2016 в 12:49 от EL-GReeN-U

ELGReeN
Активный участник
**

Репутация: 8
Offline Offline

Сообщений: 109


Ответ № 16 17 Октября 2016 в 14:50
Ставим броузеры на место
room_speed = 60 потому что броузеры у меня больше не показывают
Draw:
repeat(1500) {
    draw_text(10,10,"FPS = "+string(fps)+" webgl_enabled = "+string(webgl_enabled));
    draw_sprite(sprite0,0,10,100); // спрайт 32х32 залитый одним цветом
    draw_sprite(sprite1,-1,100,100); // анимация 32х32 залитая одним цветом 10 спрайтов
};
GMS 1.4.1763
win YYC без компиляции
50-52 фпс

win YYC с компиляцией
FATAL ERROR

win простой без компиляции
37-40 фпс

win простой с компиляцией
FATAL ERROR

HTML5
Edge (открыто 3 "левые" вкладки)
11-12 фпс
SeaMonkey (открыто 80-150 "левых" вкладок)
6 фпс
Chrome (открыто 17 "левых" вкладок)
15-16 фпс

Страниц: 1   Вверх
  Печать  
 
Перейти в:  

RSGMC (gmakers.ru) © 2007—2017
Счётчик–@Mail.ru