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

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

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

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


    Миф 2: "Если кода меньше, значит он лучше."

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

    Описание:
    Чем-то похоже на предыдущий миф о якобы влиянии объёма исходного кода на оптимизацию, однако здесь имеется в виду сравнение не двух одинаковых кусков кода, один из которых "утрамбован" в одну строку, а просто разного кода.
    Например, некоторые "профессионалы" с govno-info твердят, что цикл for быстрее, чем while, или что "соединение" строк с помощью format происходит быстрее, чем с помощью strcat.

    Доказательство:
      Открыть/закрыть
    Проверим этот миф на примере "format vs strcat", сравним скрепление 2 и более строк.
    И, в отличие от предыдущего мифа, код будет проверяться на скорость с помощью профайлера.
    Метод с использованием функции format:
    1. new string[128];
    2. format(string, sizeof(string), "%s%s", s1, s2);

    Метод с использованием strcat:
    1. new string[128];
    2. string = s1;
    3. strcat(string, s2);

    Код с использованием strcat несколько объёмнее, чем с format, и будет ещё объёмнее, если нужно будет соединить не 2, а 3 и более строк, т.к. для каждой новой строки нужен будет отдельный вызов strcat.
    Также обратите внимание: константы s1 и s2 не объявлены.
    С ними мы разберёмся позже, пока что нужно ещё разобраться с измерением скорости выполнения кода из обоих методов.
    Для такого измерения прекрасно подойдёт профайлер:
    1. /*Настройки.*/
    2. const PROFILE_ITERATIONS_MAJOR = 10_000;
    3. const PROFILE_ITERATIONS_MINOR = 1_000;
    4.  
    5. new const code_snippets_names[2][] =
    6. {
    7. {"format (2)"},
    8. {"strcat (2)"}
    9. };
    10.  
    11. #define Prerequisites();\
    12.   static const s1[] = "1234567890123456789012345678901234567890";\
    13.   static const s2[] = "1234567890123456789012345678901234567890";\
    14.   new str[256];
    15.  
    16. #define CodeSnippet1();\
    17.   format(str, sizeof(str), "%s%s", s1, s2);
    18.  
    19. #define CodeSnippet2();\
    20.   str = s1, strcat(str, s2);
    21. /*Конец настроек.*/

    Результаты профилирования:
    Код:
    Тестирование: <format (2)> vs <strcat (2)>
    Режим: интерпретируемый, 10000x1000 итераций.
    format (2): 8406
    strcat (2): 2811
    Как видно из результатов, метод с использованием strcat работает более 2 раз быстрее, чем format.
    И это несмотря на то, что метод с format более лаконичен в плане объёма исходного кода.
    Справедливости ради, следует отметить, что чем больше строк нужно соединить, тем меньше выигрыш при использовании strcat.
    Поэтому при сцеплении 8 и более строк format вырывается вперёд:
    1. /*Настройки.*/
    2. const PROFILE_ITERATIONS_MAJOR = 10_000;
    3. const PROFILE_ITERATIONS_MINOR = 1_000;
    4.  
    5. new const code_snippets_names[2][] =
    6. {
    7. {"format (8)"},
    8. {"strcat (8)"}
    9. };
    10.  
    11. #define Prerequisites();\
    12.   static const s1[] = "1234567890123456789012345678901234567890";\
    13.   static const s2[] = "1234567890123456789012345678901234567890";\
    14.   static const s3[] = "1234567890123456789012345678901234567890";\
    15.   static const s4[] = "1234567890123456789012345678901234567890";\
    16.   static const s5[] = "1234567890123456789012345678901234567890";\
    17.   static const s6[] = "1234567890123456789012345678901234567890";\
    18.   static const s7[] = "1234567890123456789012345678901234567890";\
    19.   static const s8[] = "1234567890123456789012345678901234567890";\
    20.   new str[sizeof(s1) * 8];
    21.  
    22. #define CodeSnippet1();\
    23.   format(str, sizeof(str), "%s%s%s%s%s%s%s%s", s1, s2, s3, s4, s5, s6, s7, s8);
    24.  
    25. #define CodeSnippet2();\
    26.   str = s1, strcat(str, s2), strcat(str, s3), strcat(str, s4);\
    27.   strcat(str, s5), strcat(str, s6), strcat(str, s7), strcat(str, s8);
    28. /*Конец настроек.*/

    Результат:
    Код:
    Тестирование: <format (8)> vs <strcat (8)>
    Режим: интерпретируемый, 10000x1000 итераций.
    format (8): 20433
    strcat (8): 21238
    Но даже если strcat выигрывает не во всех случаях, то хотя бы в нескольких, причём самых распространённых - соединять 8 и более строк приходится сравнительно редко.

    Рассмотрим ещё один пример: сравнение двух вещественных чисел.
    Образец 1:
    1. new Float: x = Float: random(0);
    2. new bool:b;
    3. b = (x == 1.0);

    Образец 2:
    1. new Float: x = Float: random(0);
    2. new bool:b;
    3. b = (_:x == _:1.0);

    Следует заметить, что в Pawn не предусмотрены инструкции для работы с вещественными числами, для таких операций используются нативные функции (floatcmp для сравнения, floatadd для сложения, floatsub для вычитания, floatmul для умножения и т.д.)
    Поэтому в первом примере сравнение вещественных чисел преобразуется в вызов нативной функции floatcmp, которая их и сравнивает.
    Иными словами, код
    1. b = (x == 1.0);

    превратится в
    1. b = (floatcmp(x, 1.0) == 0);

    Во втором же примере сравниваются не сами вещественные числа, а их внутренние представления, которые, благодаря замене тегов, воспринимаются, как целые числа.

    Для сравнения производительности снова воспользуемся профайлером:
    1. /*Настройки.*/
    2. const PROFILE_ITERATIONS_MAJOR = 100_000;
    3. const PROFILE_ITERATIONS_MINOR = 1_000;
    4.  
    5. new const code_snippets_names[2][] =
    6. {
    7. {"Образец 1"},
    8. {"Образец 2"}
    9. };
    10.  
    11. DoNothing(arg)
    12. {
    13. #pragma unused arg
    14. }
    15.  
    16. #define Prerequisites();\
    17.   new bool:b, Float:x = Float: random(0);\
    18.   DoNothing(_:b);// Подавить варнинг 204 (неиспользуемая переменная "b").
    19.  
    20. #define CodeSnippet1();\
    21.   b = (x == 1.0);
    22.  
    23. #define CodeSnippet2();\
    24.   b = (_:x == _:1.0);
    25. /*Конец настроек.*/


    Результаты тестирования:
    Код:
    Тестирование: <Образец 1> vs <Образец 2>
    Режим: интерпретируемый, 100000x1000 итераций.
    Образец 1: 8404
    Образец 2: 3025
    Результаты того же тестирования, только с JIT-компиляцией:
    Код:
    Тестирование: <Образец 1> vs <Образец 2>
    Режим: с JIT-компиляцией, 100000x1000 итераций.
    Образец 1: 1270
    Образец 2: 384
    Итак, в обоих рассмотренных примерах быстрее работают именно те образцы, в которых больше кода.

    На практике же встречается ещё больше техник.
    Пример: кэширование никнеймов игроков при подключении к серверу.
    На само запоминание никнейма может уйти какое-то время. Да и кода может оказаться предостаточно (перехваты OnPlayerConnect, OnPlayerDisconnect, GetPlayerName, SetPlayerName).
    С другой стороны, этим можно ускорить получение никнеймов игроков, заменив вызовы GetPlayerName на обращения к массиву с кэшированными никами.
    Похожую картину можно наблюдать в инклуде foreach.inc от Y_Less: много кода, затраты на добавление и удаление игроков из итератора, но в противовес получаем крайне эффективный метод перебора подключенных игроков.

    Как результат, утверждение о том, что чем меньше кода, тем больше производительность - неверно. Что и требовалось доказать.

    Вывод: производительность алгоритма ни коим образом не зависит от размера исходного кода.
    Самый адекватный способ узнать пользу той или иной реализации одного и того же алгоритма - сравнить время их выполнения в тех или иных ситуациях. Для таких целей прекрасно подойдёт профайлер.


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

  2. 11 пользователя(ей) сказали cпасибо:
    $continue$ (20.10.2015) BadPawn (16.04.2016) Desulaid (20.10.2015) Disinterpreter (20.10.2015) George (21.10.2015) Jackal (23.10.2015) MaKcuM (12.01.2017) Osetin (19.10.2015) oukibt (05.06.2021) Seregamil (20.10.2015) [ForD] (20.10.2015)
 

 

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

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

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

Ваши права

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