Добро пожаловать на Pro Pawn - Портал о PAWN-скриптинге.
Страница 1 из 4 1 2 3 ... ПоследняяПоследняя
Показано с 1 по 10 из 35
  1. #1
    Аватар для punkochel
    Пользователь

    Статус
    Оффлайн
    Регистрация
    08.12.2018
    Адрес
    Россия
    Сообщений
    146
    Репутация:
    25 ±

    Как правильно использовать стек

    Задался таким вот вопросом.
    Как мне известно из различных ресурсов программирования, память которую мы используем из стека, работает на порядок быстрее сегмента данных. Мне стало интересно, как лучше использовать память стека и решил поиграться с профайлером от DC:

    Код, который тестировался:
      Открыть/закрыть

    1. new String_64[64];
    2.  
    3. #define Prerequisites();
    4.  
    5. #define CodeSnippet0();\
    6.   TestCode1();
    7.  
    8. #define CodeSnippet1();\
    9.   TestCode2();
    10.  
    11.  
    12. stock TestCode1() // Stack
    13. {
    14. goto label;
    15. new string[64];
    16. label:
    17. for(new i; i < sizeof(string); i++) {
    18.  
    19. string[i] = i;
    20. if(string[i] != i)
    21. break;
    22. }
    23. }
    24.  
    25. stock TestCode2() // Data size
    26. {
    27. for(new i; i < sizeof(String_64); i++) {
    28.  
    29. String_64[i] = i;
    30. if(String_64[i] != i)
    31. break;
    32. }
    33. }


    Обход инициализации массива сделал чтобы уравнять "соперников".


    Результаты:
    Тестирование: <Stack> vs <Data size>
    Режим: интерпретируемый, 10000x1000 итераций.
    Stack: 124646
    Data size: 116586


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

    Вопрос 1: Получается если мне нужно выводить диалог размером, скажем 2048*4 байт, то мне лучше отдать предпочтение сегменту данных?

    Вопрос 2: Получается, лучше изначально выделить память в сегменте данных, скажем размерами 144, 256, 512... и уже пользоваться данной памятью, нежели постоянно выделять память в стеке?

    UPD: Дополнение:
    Какой из этих вариантов, будет лучше использовать?

    1. stock OptionOne(playerid) {
    2.  
    3. new string[2048];
    4. for(new i; i < 128; i++) {
    5.  
    6. format(string, sizeof(string), "%sКакой-то текст...\n", string);
    7. }
    8. ShowPlayerDialog(playerid, 0, DIALOG_STYLE_MSGBOX, "caption", string, "button1", "button2");
    9. return 1;
    10. }
    11.  
    12. new String_2048[2048];
    13. stock OptionTwo(playerid) {
    14.  
    15. String_2048[0] = EOS;
    16. for(new i; i < 128; i++) {
    17.  
    18. format(String_2048, sizeof(String_2048), "%sКакой-то текст...\n", String_2048);
    19. }
    20. ShowPlayerDialog(playerid, 0, DIALOG_STYLE_MSGBOX, "caption", String_2048, "button1", "button2");
    21. return 1;
    22. }
    Последний раз редактировалось punkochel; 05.11.2020 в 08:20. Причина: Немного изменил тестируемый код, результаты теста и добавил второй вопрос

  2. #2
    Аватар для DeimoS
    Модератор?

    Статус
    Оффлайн
    Регистрация
    27.01.2014
    Адрес
    Восточный Мордор
    Сообщений
    5,588
    Репутация:
    1984 ±
    Цитата Сообщение от punkochel Посмотреть сообщение
    Как мне известно из различных ресурсов программирования, память которую мы используем из стека, работает на порядок быстрее сегмента данных.]
    В "нормальных" языках программирования, в большинстве случаев - да. В Pawn - нет, так как стэк и куча - это один и тот же сегмент данных, просто заполняющийся с разных концов. Разница в скорости будет только на уровне погрешности.


    Цитата Сообщение от punkochel Посмотреть сообщение
    Вопрос 1: Получается если мне нужно выводить диалог размером, скажем 2048*4 байт, то мне лучше отдать предпочтение сегменту данных?

    Вопрос 2: Получается, лучше изначально выделить память в сегменте данных, скажем размерами 144, 256, 512... и уже пользоваться данной памятью, нежели постоянно выделять память в стеке?

    UPD: Дополнение:
    Какой из этих вариантов, будет лучше использовать?

    1. stock OptionOne(playerid) {
    2.  
    3. new string[2048];
    4. for(new i; i < 128; i++) {
    5.  
    6. format(string, sizeof(string), "%sКакой-то текст...\n", string);
    7. }
    8. ShowPlayerDialog(playerid, 0, DIALOG_STYLE_MSGBOX, "caption", string, "button1", "button2");
    9. return 1;
    10. }
    11.  
    12. new String_2048[2048];
    13. stock OptionTwo(playerid) {
    14.  
    15. String_2048[0] = EOS;
    16. for(new i; i < 128; i++) {
    17.  
    18. format(String_2048, sizeof(String_2048), "%sКакой-то текст...\n", String_2048);
    19. }
    20. ShowPlayerDialog(playerid, 0, DIALOG_STYLE_MSGBOX, "caption", String_2048, "button1", "button2");
    21. return 1;
    22. }
    Не получается. Если у тебя есть какая-то информация, которую тебе нужно использовать только в конкретной функции и не требуется её долговременное хранение - всегда используй только стек. Даже если у тебя массив получается большим и стандартный размер стека заполняется - просто увеличь стек, а не создавай глобальный массив. Вот тут я подробно описывал почему.
    Связаться со мной в VK можно через личные сообщения этой группы
    Заказы не принимаю

    Широко известно, что идеи стоят 0.8333 цента каждая (исходя из рыночной цены 10 центов за дюжину).
    Великих идей полно, на них нет спроса.
    Воплощение идеи в законченную игру требует долгой работы,
    таланта, терпения и креативности, не говоря уж о затратах денег, времени и ресурсов.
    Предложить идею просто, воплотить – вот в чём проблема

    Steve Pavlina

  3. Пользователь сказал cпасибо:
    punkochel (05.11.2020)
  4. #3
    Аватар для punkochel
    Пользователь

    Статус
    Оффлайн
    Регистрация
    08.12.2018
    Адрес
    Россия
    Сообщений
    146
    Репутация:
    25 ±
    Отличная статья, в голове появилось четкое понимание стека.
    Так вот, со стеком понятно, теперь возникло еще 2 вопроса:

    Вопрос 1: Чем ограничен сегмент данных? (Возможный ответ: Выделенная оперативная память);

    Вопрос 2: Допустим есть цикл, который вызывает функцию форматирования и отправки SQL-запроса. В функции выделяется память непосредственно под форматирование. Как-то видел пост от Nexus'a, что не стоит создавать переменные в теле цикла. Получается в данном случае необходимо все-таки использовать память из сегмента данных?

    UPD: Добавил пример ко второму вопросу:
    1. for(new i; i < 100; i++) {
    2.  
    3. SaveInDataBase(i);
    4. }
    5.  
    6. stock SaveInDataBase(Id) {
    7.  
    8. static const fmt_query[] "UPDATE `account` SET `Money` = '%d' WHERE `Id` = '%d'";
    9. new string[sizeof(fmt_query)+(-2+11)+(-2+3)+1];
    10. mysql_format(dbHandle, string, sizeof(string), fmt_query, 1000, Id);
    11. mysql_tquery(dbHandle, string);
    12. return 1;
    13. }

    В таком случае, каждую итерацию цикла, будет создаваться массив в несколько ячеек. Или просто стоит использовать обход инициализации массива?
    Последний раз редактировалось punkochel; 05.11.2020 в 14:04.

  5. #4
    Аватар для DeimoS
    Модератор?

    Статус
    Оффлайн
    Регистрация
    27.01.2014
    Адрес
    Восточный Мордор
    Сообщений
    5,588
    Репутация:
    1984 ±
    1) Проще будет дать ссылки на официальную документацию по языку

    Цитата Сообщение от Daniel_Cortez Посмотреть сообщение
    Сторонние источники информации:

    2) У тебя в примере много проблем.

    2.1) Ответ на вопрос: перемещаешь код из тела функции в тело цикла и выносишь создание массива за тело цикла (а лучше - рядом с созданием переменной "i").

    2.2) mysql_format в твоём варианте запроса не нуден. Эта функция нужна только если ты хочешь использовать спецификатор '%e'.

    2.3) Если ты отправляешь циклом кучу запросов за раз - в 99% случаев ты либо не продумал алгоритм своей системы нормально, либо поленился сначала собрать один большой запрос и за раз его отправить. В любом случае такой подход вряд ли оправдан.
    Связаться со мной в VK можно через личные сообщения этой группы
    Заказы не принимаю

    Широко известно, что идеи стоят 0.8333 цента каждая (исходя из рыночной цены 10 центов за дюжину).
    Великих идей полно, на них нет спроса.
    Воплощение идеи в законченную игру требует долгой работы,
    таланта, терпения и креативности, не говоря уж о затратах денег, времени и ресурсов.
    Предложить идею просто, воплотить – вот в чём проблема

    Steve Pavlina

  6. #5
    Аватар для punkochel
    Пользователь

    Статус
    Оффлайн
    Регистрация
    08.12.2018
    Адрес
    Россия
    Сообщений
    146
    Репутация:
    25 ±
    2.3) Если ты отправляешь циклом кучу запросов за раз - в 99% случаев ты либо не продумал алгоритм своей системы нормально, либо поленился сначала собрать один большой запрос и за раз его отправить. В любом случае такой подход вряд ли оправдан.
    Заинтересовал конечно. Тогда хотелось бы увидеть пример реализации сохранения аккаунтов при рестарте сервера, на котором, на момент непосредственно самого рестарта, находиться 1000 игроков. Это-ж что за запросище должен быть такой?
    Этот момент можно упустить, может быть все данные обновляются в процессе игры, ладно. Но как быть, к примеру, с этим: Захотел я выдать всем игрокам онлайн (а их 1000 на сервере) по 1 млрд вирт. Как бы ты реализовал это?

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

    1) Проще будет дать ссылки на официальную документацию по языку
    Сторонние источники информации:
    Pawn Implementer's Guide
    Copyright (c) 1997–2006, ITB CompuPhase

    Pawn Language Guide
    Copyright (c) 1997–2006, ITB CompuPhase
    За это спасибо. Нужно будет почитать обязательно.
    Последний раз редактировалось punkochel; 05.11.2020 в 15:17.

  7. #6
    Аватар для DeimoS
    Модератор?

    Статус
    Оффлайн
    Регистрация
    27.01.2014
    Адрес
    Восточный Мордор
    Сообщений
    5,588
    Репутация:
    1984 ±
    Цитата Сообщение от punkochel Посмотреть сообщение
    Тогда хотелось бы увидеть пример реализации сохранения аккаунтов при рестарте сервера, на котором, на момент непосредственно самого рестарта, находиться 1000 игроков.
    Сохраняй данные в момент их изменения.
    Нет, сервер от такого не умрёт.
    Да, и MySQL тоже.


    Цитата Сообщение от punkochel Посмотреть сообщение
    Захотел я выдать всем игрокам онлайн (а их 1000 на сервере) по 1 млрд вирт. Как бы ты реализовал это?
    Я бы имел в таблице аккаунтов столбец, который бы хранил ID игрока, если тот на сервере, или -1, если игрок не на сервере.
    А дальше просто:
    1. mysql_tquery(mysql_connection_ID, "UPDATE account SET money = money+1000000 WHERE online_id != -1", "", "");
    2. foreach(new i: Player)
    3. {
    4. //Выдаём деньги непосредственно в игре и запоминаем выдачу сервером, но не отправляем запросы в БД, так как верхний запрос уже всё сохранил
    5. }



    Цитата Сообщение от punkochel Посмотреть сообщение
    Это то понятно что можно так сделать. А если эта функция используется повсеместно, и вдруг я захочу немного её изменить, то мне придется лопатить весь мод, и наверняка, где-нибудь, что-то упущу. Мне интересен момент, как можно реализовать именно с таким подходом, при это затратив минимум ресурсов.
    Если функция используется повсеместно - перемести цикл внутрь функции. Если же тебе нужно и какой-то конкретный столбец сохранять, и сразу несколько - создай две разные функции.
    А если этот цикл - это какой-то единичный случай и во всех остальных ситуациях ты функцию вызываешь для сохранения какого-то одного ID - просто копируешь тело функции в цикл в качестве исключения и всё.

    Не нужно натягивать сову на глобус, пытаясь создавать какие-то универсальные функции на все случаи жизни. Если у тебя случаются ситуации, подобные этой - это намёк на то, что тебе пора переосмысливать структуру своего кода, а не лепить одну функцию всюду.
    Последний раз редактировалось DeimoS; 05.11.2020 в 15:24.
    Связаться со мной в VK можно через личные сообщения этой группы
    Заказы не принимаю

    Широко известно, что идеи стоят 0.8333 цента каждая (исходя из рыночной цены 10 центов за дюжину).
    Великих идей полно, на них нет спроса.
    Воплощение идеи в законченную игру требует долгой работы,
    таланта, терпения и креативности, не говоря уж о затратах денег, времени и ресурсов.
    Предложить идею просто, воплотить – вот в чём проблема

    Steve Pavlina

  8. #7
    Аватар для punkochel
    Пользователь

    Статус
    Оффлайн
    Регистрация
    08.12.2018
    Адрес
    Россия
    Сообщений
    146
    Репутация:
    25 ±
    Я тут значит хочу что-то сложное придумать, а ты все выкручиваешься)
    Со стеком разобрались, не вижу ничего страшного, если в некоторых моментах использовать память из сегмента данных.
    Даже если выделить 4096*4 байт (размер дефолтного стека), ибо везде есть массив, хранящий Имя игрока, а это, на минуточку 1000*24*4 байт (MAX_PLAYERS*MAX_PLAYER_NAME*4), хотя можно хранить и в упакованном виде, но сути не меняет. Даже можно вообще отказаться от стека в отношении локальных массивов используемых для форматирования.

    Хорошая идея - хранить Id игрока онлайн в БД. И с данными, которые обновляются в единичных случаях, тоже понятно как работать (сохранять).
    Наверняка, почти в каждом, полноценном моде, есть данные, которые обновляются каждую секунду, например, это может быть время игрока в тюрьме, время бана чата... еще не стоит забывать про патроны, не сохранять же их при каждом выстреле...

    К чему это я: к тому, что все таки придется сохранять данные при отключении игрока от сервера, ибо это будет равносильно циклу на игроков, отправляющий запрос каждую итерацию (Пример: 1000 игроков стреляют с UZI и каждый выстрел сохраняется в БД). И от системы, которая сохраняет подобные данные при выходе никак не уйти.
    Как в таком случае ты бы сделал сохранение? (Опять же условия: 1000 игроков, рестарт сервера);

    Да даже взять к примеру наркотики:
    Сожрал игрок 10 грамм - Сохранил;
    При этом я не хочу ставить анти-флуд на употребление, и игрок начинает флудить. Один то он вряд-ли сможет что-то изменить, но вот если этим занимаются 100+ игроков?

    UPD: Блин, где-то видел статью DC об использовании params, там кажется говорилось о том, что params даже лучше использовать в CMD, нежели создавать локальный массив;
    Последний раз редактировалось punkochel; 05.11.2020 в 16:24.

  9. #8
    Аватар для DeimoS
    Модератор?

    Статус
    Оффлайн
    Регистрация
    27.01.2014
    Адрес
    Восточный Мордор
    Сообщений
    5,588
    Репутация:
    1984 ±
    Цитата Сообщение от punkochel Посмотреть сообщение
    Я тут значит хочу что-то сложное придумать, а ты все выкручиваешься)
    Со стеком разобрались, не вижу ничего страшного, если в некоторых моментах использовать память из сегмента данных.
    Даже если выделить 4096*4 байт (размер дефолтного стека), ибо везде есть массив, хранящий Имя игрока, а это, на минуточку 1000*24*4 байт (MAX_PLAYERS*MAX_PLAYER_NAME*4), хотя можно хранить и в упакованном виде, но сути не меняет. Даже можно вообще отказаться от стека в отношении локальных массивов используемых для форматирования.
    Ты не видишь ничего страшного, а твой сервер начнёт всё больше и больше памяти жрать, если ты будешь глобальные массивы по поводу и без создавать. Если у тебя есть какая-то временная информация, которая используется только внутри тела конкретной функции, и ты для неё создаёшь глобальный массив - ты написал плохой код, который нерационально расходует память. И никак иначе.

    Цитата Сообщение от punkochel Посмотреть сообщение
    Наверняка, почти в каждом, полноценном моде, есть данные, которые обновляются каждую секунду, например, это может быть время игрока в тюрьме, время бана чата... еще не стоит забывать про патроны, не сохранять же их при каждом выстреле...
    Сохраняешь их каждые 5/10/15 и т.п. секунд, а не сразу. В чём проблема?
    И я не говорю про отдельный таймер. Там же, где ты вычисляешь количество секунд для той же тюрьмы, добавляешь код, а-ля:
    1. if(pInfo[playerid][pJailTime] % 10 == 0)
    2. {
    3. // Запрос на сохранение
    4. }

    + добавляешь запрос на сохранение всех таких данных при выходе из сервера.

    В итоге, даже в случае падения сервера, игрок не потеряет основную массу того прогресса, что он уже успел наиграть.

    Цитата Сообщение от punkochel Посмотреть сообщение
    Да даже взять к примеру наркотики:
    Сожрал игрок 10 грамм - Сохранил;
    При этом я не хочу ставить анти-флуд на употребление, и игрок начинает флудить. Один то он вряд-ли сможет что-то изменить, но вот если этим занимаются 100+ игроков?
    Ничего от этого не случится. Хорошо организованная таблица и правильно составленные запросы могут гарантировать то, что MySQL спокойно тысячи запросов в секунду прожуёт и не поперхнётся. А если ещё и серверное оборудование нормальное будет, то и до десятков тысяч эта цифра доходить будет.

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

    Цитата Сообщение от punkochel Посмотреть сообщение
    UPD: Блин, где-то видел статью DC об использовании params, там кажется говорилось о том, что params даже лучше использовать в CMD, нежели создавать локальный массив;
    Эмм, нет. В статье как раз таки развенчивался этот миф. Вот, перечитай - https://pro-pawn.ru/showthread.php?12988
    Ну и да: params - это всё ещё локальная переменная (массив). И использует она стек, а не сегмент данных.
    Последний раз редактировалось DeimoS; 05.11.2020 в 16:54.
    Связаться со мной в VK можно через личные сообщения этой группы
    Заказы не принимаю

    Широко известно, что идеи стоят 0.8333 цента каждая (исходя из рыночной цены 10 центов за дюжину).
    Великих идей полно, на них нет спроса.
    Воплощение идеи в законченную игру требует долгой работы,
    таланта, терпения и креативности, не говоря уж о затратах денег, времени и ресурсов.
    Предложить идею просто, воплотить – вот в чём проблема

    Steve Pavlina

  10. #9
    Аватар для punkochel
    Пользователь

    Статус
    Оффлайн
    Регистрация
    08.12.2018
    Адрес
    Россия
    Сообщений
    146
    Репутация:
    25 ±
    Цитата Сообщение от DeimoS Посмотреть сообщение
    + добавляешь запрос на сохранение всех таких данных при выходе из сервера.
    Ну, а я о чем? Не просто же так я приводил пример о рестарте сервера с 1000 игроков онлайна. Все-равно придется реализовать систему так:
    1. foreach(new i:Player) {
    2.  
    3. // Запрос на сохранение для каждого игрока
    4. }

    Тем самым, ты получишь флуд запросами (1000 запросов);
    Цитата Сообщение от DeimoS Посмотреть сообщение
    2.3) Если ты отправляешь циклом кучу запросов за раз - в 99% случаев ты либо не продумал алгоритм своей системы нормально, либо поленился сначала собрать один большой запрос и за раз его отправить. В любом случае такой подход вряд ли оправдан.

  11. #10
    Аватар для DeimoS
    Модератор?

    Статус
    Оффлайн
    Регистрация
    27.01.2014
    Адрес
    Восточный Мордор
    Сообщений
    5,588
    Репутация:
    1984 ±
    Цитата Сообщение от punkochel Посмотреть сообщение
    Ну, а я о чем? Не просто же так я приводил пример о рестарте сервера с 1000 игроков онлайна. Все-равно придется реализовать систему так:
    1. foreach(new i:Player) {
    2.  
    3. // Запрос на сохранение для каждого игрока
    4. }

    Тем самым, ты получишь флуд запросами (1000 запросов);
    Эмм, зачем там цикл?
    Ещё раз: не при рестарте, а при выходе конкретного игрока. Это будет запрос, в стиле:
    1. format(query_string, sizeof(query_string), "UPDATE account SET mute_time=%i,jail_time=%i WHERE id=%d", ...);
    2. mysql_tquery(...);

    И этот запрос не обязательный. Просто без него игроку придётся каждый раз сидеть дополнительные сколько секунд при перезаходе на сервер, так как сохраняться данные будут только каждые 10 секунд.
    Связаться со мной в VK можно через личные сообщения этой группы
    Заказы не принимаю

    Широко известно, что идеи стоят 0.8333 цента каждая (исходя из рыночной цены 10 центов за дюжину).
    Великих идей полно, на них нет спроса.
    Воплощение идеи в законченную игру требует долгой работы,
    таланта, терпения и креативности, не говоря уж о затратах денег, времени и ресурсов.
    Предложить идею просто, воплотить – вот в чём проблема

    Steve Pavlina

 

 
Страница 1 из 4 1 2 3 ... ПоследняяПоследняя

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

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

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

Ваши права

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