Добро пожаловать на Pro Pawn - Портал о PAWN-скриптинге.
Показано с 1 по 1 из 1
  1. #1
    Аватар для Daniel_Cortez
    "Это не хак, это фича"

    Статус
    Оффлайн
    Регистрация
    06.04.2013
    Адрес
    Novokuznetsk, Russia
    Сообщений
    2,192
    Репутация:
    2589 ±

    Мифы о Pawn-скриптинге - #5

    Внимание: данная тема закрыта для защиты от копирования.
    Если есть какие-то вопросы, замечания или просто пожелания по поводу данного урока - оставляйте их здесь.


    Миф 5: "Функция sizeof медленная, а ещё с ней код медленно компилируется, не надо пользоваться ей."

    Статус: Опровергнут.

    Описание:
    Описание #не_нужно, всё уже есть в названии мифа. Равно, как и не нужно говорить, с какого форума была взята та цитата.
    Впрочем, популярен этот миф не только на г-и: http://ihost.pro-pawn.ru/image.php?di=HU2W
    И я бы не стал опровергать столь очевидный миф, если бы не "профессионалы", которые время от времени ещё и набегают сюда.

    Доказательство:
      Открыть/закрыть
    На самом деле в названии мифа сразу две ошибки.
    Во-первых, sizeof - это не функция, а оператор, с помощью которого можно заставить компилятор автоматически подсчитать размер массива в ячейках.
    Во-вторых, размер массива постоянен и результат работы оператора sizeof вычисляется компилятором. Код не может выполняться медленнее из-за константы.

    Рассмотрим это на следующем примере:
    1. #define fpub%0(%1) forward%0(%1);public%0(%1)
    2.  
    3. fpub Snippet1() // Отрывок кода #1, без использования sizeof.
    4. {
    5. new a[10];
    6. #pragma unused a
    7. return 10;
    8. }
    9.  
    10. fpub Snippet2() // Отрывок кода #2, с использованием sizeof.
    11. {
    12. new a[10];
    13. #pragma unused a
    14. return sizeof(a);
    15. }


    Скомпилируем скрипт с ключом "-a" для получения ассемблерного листинга:
    Код:
    CODE 0	; 0
    ;program exit point
    	halt 0
    
    	proc	; Snippet1
    	; line 4
    	; line 5
    	break	; c
    	;$lcl a ffffffd8
    	stack ffffffd8
    	zero.pri
    	addr.alt ffffffd8
    	fill 28
    	; line 7
    	break	; 2c
    	const.pri a
    	stack 28
    	retn
    
    	proc	; Snippet2
    	; line b
    	; line c
    	break	; 48
    	;$lcl a ffffffd8
    	stack ffffffd8
    	zero.pri
    	addr.alt ffffffd8
    	fill 28
    	; line e
    	break	; 68
    	const.pri a
    	stack 28
    	retn
    
    
    STKSIZE 1000
    Обратите внимание на Snippet1 и Snippet2: опкоды в этих функциях абсолютно одинаковые.
    Как и было сказано ранее, результат работы оператора sizeof был вычислен компилятором и подставлен на место его применения в виде числовой константы.
    Поскольку сгенерированный код в обоих отрывках совпадает, тесты скорости не нужны, т.к. производительность у них тоже будет одинаковой (разница будет равняться погрешности метода тестирования).

    Теперь проверим утверждение про замедление работы компилятора. Для этого сгенерируем два скрипта, в одном из которых будет 1000 функций, которые возвращают результат, вычисляемый с помощью sizeof, а в другом 1000 функций, которые возвращают обычные числа.
    Первый скрипт будет выглядеть примерно так:
    1. new a[128];
    2. #pragma unused a
    3.  
    4. forward Function0();public Function0()
    5. return 128;
    6.  
    7. forward Function1();public Function1()
    8. return 128;
    9.  
    10. forward Function2();public Function2()
    11. return 128;
    12.  
    13. //...

    Примерно так же будет выглядеть второй скрипт, только вместо "128" будет возвращаться "sizeof(a)".
    Можно было использовать макрос fpub для сокращения объявления функции, как в первом примере, но это увеличит время препроцессинга скрипта, а вместе с ним и общее время компиляции.

    Ещё для ускорения компиляции можно использовать ключ "-a", чтобы компилятор не генерировал файл .amx, а выдавал ассемблерный листинг (.asm).
    Всё равно при обычной компиляции этот листинг тоже генерируется, а на его основе уже создаётся файл .amx.
    Указав ключ "-a" мы избавим компилятор от бремени составления файла .amx, тем самым ещё больше увеличив время компиляции.

    Также можно указать ключи "-(+" и "-;+", чтобы сделать обязательными скобки для параметров функций и точку с запятой в конце каждого предложения (по умолчанию они не обязательны, но разработчики SA:MP зачем-то решили форсировать их обязательное использование, сделав так, чтобы редактор pawno сам запускал компилятор с этими ключами). Кроме того, не лишним будет компилятор "-v0", чтобы компилятор не выводил никаких сообщений.

    Принцип работы теста:
    • Генерируются скрипты test1.pwn и test2.pwn с 10 000 функций в каждом.
      В одном из них функции возвращают значение в виде целочисленной константы, в другом значение определяется с помощью оператора sizeof.
    • Сервер с помощью плагина pawnCompiler запускает компилятор Pawn.
      Запуск происходит асинхронно, поэтому чтобы определить момент завершения компиляции, нужно проверять существование файла с расширением *.asm - как только этот файл будет готов, компилятор завершит свою работу.
    • Чтобы во время постоянных проверок на существование файла .asm сервер не создавал большую нагрузку на процессор (~50%) и не мешал компилятору, следует перед каждой проверкой использовать оператор sleep.
      Т.к. при частом использовании этого оператора можно добиться ошибки "stack/heap collision", лучше использовать функцию dc_SleepFix.
    • Как только замеры компиляции времени будут готовы, вывести их вместе с разницей во времени, как общей, так и на одно использование sizeof.


    1. #include <a_samp>
    2. #include "../include/pawnCompiler.inc"
    3.  
    4. #define TEST_SCRIPT_1 "test1"
    5. #define TEST_SCRIPT_2 "test2"
    6. #define TEST_SCRIPT_PWN_1 TEST_SCRIPT_1 ".pwn"
    7. #define TEST_SCRIPT_PWN_2 TEST_SCRIPT_2 ".pwn"
    8. #define TEST_SCRIPT_ASM_1 TEST_SCRIPT_1 ".asm"
    9. #define TEST_SCRIPT_ASM_2 TEST_SCRIPT_2 ".asm"
    10. #define TEST_ARRAY_SIZE 128
    11. #define TEST_FUNCTIONS_COUNT 10_000
    12.  
    13.  
    14. GenerateScript(name[], bool:use_sizeof)
    15. {
    16. static const header[] =
    17. "/* !! This file has been generated automatically. !! */" "\n"\
    18. "\n"\
    19. "new a[" #TEST_ARRAY_SIZE "];" "\n"\
    20.   "#pragma unused a" "\n"\
    21.   "\n";
    22. static const function_str0[] = "forward Function";
    23. static const function_str1[] = "();public Function";
    24. static const function_str2[] = "()return ";
    25. static const function_str3[2][] =
    26. {
    27. {"" #TEST_ARRAY_SIZE ";\n"},
    28. {"sizeof(a);\n"}
    29. };
    30. new File:handle = fopen(name, io_write);
    31. handle,
    32. header
    33. );
    34. new num_buffer[11];
    35. for (new i = 0; i < TEST_FUNCTIONS_COUNT; ++i)
    36. {
    37. fwrite(handle, function_str0);
    38. valstr(num_buffer, i);
    39. fwrite(handle, num_buffer);
    40. fwrite(handle, function_str1);
    41. fwrite(handle, num_buffer);
    42. fwrite(handle, function_str2);
    43. fwrite(handle, function_str3[!!use_sizeof]);
    44. }
    45. fclose(handle);
    46. }
    47.  
    48. dc_SleepFix(time) // by Daniel_Cortez \\ pro-pawn.ru
    49. { // WARNING: This function is still only usable in main().
    50. // It just fixes the stack/heap collision error in default sleep().
    51. static heap_ptr, stack_ptr;
    52. #emit lctrl 2
    53. #emit stor.pri heap_ptr
    54. #emit lctrl 4
    55. #emit stor.pri stack_ptr
    56. sleep(time);
    57. #emit load.pri stack_ptr
    58. #emit sctrl 4
    59. #emit load.pri heap_ptr
    60. #emit sctrl 2
    61. }
    62. #if defined _ALS_sleep
    63. #undef sleep
    64. #else
    65. #define _ALS_sleep
    66. #endif
    67. #define sleep(%0) dc_SleepFix(%0)
    68.  
    69. main()
    70. {
    71. GenerateScript(!TEST_SCRIPT_PWN_1, false);
    72. GenerateScript(!TEST_SCRIPT_PWN_2, true);
    73. fremove(TEST_SCRIPT_ASM_1);
    74. fremove(TEST_SCRIPT_ASM_2);
    75. new t1, t2, t;
    76. t = GetTickCount();
    77. WinExec("pawno/pawncc.exe -(+ -;+ -v0 -a scriptfiles/" TEST_SCRIPT_PWN_1);
    78. do { sleep(0); } while (0 == fexist(TEST_SCRIPT_ASM_1));
    79. t1 = GetTickCount() - t;
    80. t = GetTickCount();
    81. WinExec("pawno/pawncc.exe -(+ -;+ -v0 -a scriptfiles/" TEST_SCRIPT_PWN_2);
    82. do { sleep(0); } while (0 == fexist(TEST_SCRIPT_ASM_2));
    83. t2 = GetTickCount() - t;
    84. new Float:dt = float(t2 - t1) / float(TEST_FUNCTIONS_COUNT);
    85. printf("Компиляция без sizeof: %d", t1);
    86. printf("Компиляция с использованием sizeof: %d", t2);
    87. "dt = %d / %d = %.4f тиков = %.8f мс",
    88. t2 - t1, TEST_FUNCTIONS_COUNT, dt, dt / 1000.0
    89. );
    90. }


    Результат:
    1. Компиляция без sizeof: 8484
    2. Компиляция с использованием sizeof: 9741
    3. dt = 1257 / 10000 = 0.1476 тиков = 0.00014769 мс

    Получается, при каждом использовании sizeof время компиляции увеличивается примерно на ~0.00015 миллисекунды или на ~147 микросекунд - крайне малый отрезок времени.
    Измерения были получены на ПК с процессором Intel Pentium E6600, 3.06 ГГц. Сейчас всё чаще можно встретить ПК и помощнее, поэтому у вас разница может оказаться ещё меньше.
    Можно больше времени потерять на подсчёт размера массива вручную, чем на компиляцию с sizeof.
    Как результат, миф о вреде использования sizeof опровергнут.


    Вывод: sizeof не влияет на производительность кода, а на время компиляции воздействие ничтожно мало и им можно принебречь.


    Специально для Pro-Pawn.ru
    Не разрешается копирование данной статьи на других ресурсах без разрешения автора.
    Индивидуально в ЛС по скриптингу не помогаю. Задавайте все свои вопросы здесь (click).

  2. 19 пользователя(ей) сказали cпасибо:
    $continue$ (13.01.2016) Alanchick (13.07.2016) ALIT13 (13.01.2016) DeimoS (13.01.2016) Desulaid (13.01.2016) DmitriyVasilev (06.07.2019) franked (17.02.2016) L0ndl3m (13.01.2016) LLIapuk (14.01.2016) MaKcuM (12.01.2017) MassonNN (18.01.2020) Nurick (13.01.2016) Salik_Davince (13.01.2016) seriu (13.01.2016) Unreal (15.01.2016) VVWVV (13.01.2016) whale (31.01.2020) [ForD] (18.01.2016) Роуди. (14.01.2016)
 

 

Информация о теме

Пользователи, просматривающие эту тему

Эту тему просматривают: 1 (пользователей: 0 , гостей: 1)

Ваши права

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