Добро пожаловать на Pro Pawn - Портал о PAWN-скриптинге.
Показано с 1 по 9 из 9
  1. #1
    Аватар для punkochel
    Пользователь

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

    Использование памяти

    Доброго времени суток. В теме я хочу задать 2 похожих вопроса о правильном использовании памяти.

    1) Почему в перехватываемой функции значительно увеличивается размер стека?

    Приведу ситуацию:

    В этом случае подсчет стека идет понятно как, и тут вроде бы все правильно.


      Открыть/закрыть


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

      Открыть/закрыть


    Почему произошло увеличение размера стека?

    Код инклуда который я подключил:

      Открыть/закрыть
    Код:
    public OnDialogResponse(playerid, dialogid, response, listitem, inputtext[])
    {
    	switch(dialogid)
    	{
    		case DLG_TEST3:
    		{
    			new dialog[100];
    			format(dialog, sizeof(dialog), "%i %i %i", variable1, variable2, variable3);
    		}
    		case DLG_TEST4:
    		{
    			new dialog[100];
    			format(dialog, sizeof(dialog), "%i %i %i", variable1, variable2, variable3);
    		}
    	}
    #if defined test__OnDialogResponse
        return test__OnDialogResponse(playerid, dialogid, response, listitem, inputtext);
    #endif
    }
    #if defined _ALS_OnDialogResponse
        #undef    OnDialogResponse
    #else
        #define    _ALS_OnDialogResponse
    #endif
    #define    OnDialogResponse    test__OnDialogResponse
    #if defined test__OnDialogResponse
    forward test__OnDialogResponse(playerid, dialogid, response, listitem, inputtext[]);
    #endif


    2) Стоит ли в инклудах, где используется перехват функций использовать переменные static для создания массива?

    Как один из вариантов решения который вроде-бы более-менее практичный (на мой взгляд):

    Код:
                    case DLG_TEST3:
    		{
    			static dialog[100];
    			format(dialog, sizeof(dialog), "%i %i %i", variable1, variable2, variable3);
    		}
    		case DLG_TEST4:
    		{
    			static dialog[100];
    			format(dialog, sizeof(dialog), "%i %i %i", variable1, variable2, variable3);
    		}

    Или-же вывести static dialog[100] в общий блок колбека.
    Последний раз редактировалось punkochel; 16.06.2019 в 21:23.

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

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

    2) Нет. Этим ты перенесёшь массив из стека в глобальный сегмент данных, начав использовать новую память, а не уже выделенный стек. Если нет нужды использовать одну и ту же информацию, которую записываешь в переменные, в нескольких пабликах/функциях, всегда используй локальные переменные и, в случае нужды, увеличивай стек, а не плоди глобальные переменные с целью экономии стека. Вот тут я подробно рассказывал об этом.
    Связаться со мной в VK можно через личные сообщения этой группы
    Заказы не принимаю

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

    Steve Pavlina

  3. #3
    Аватар для Daniel_Cortez
    "Это не хак, это фича"

    Статус
    Оффлайн
    Регистрация
    06.04.2013
    Адрес
    Novokuznetsk, Russia
    Сообщений
    2,192
    Репутация:
    2589 ±
    Как вариант, можно ограничить код перехвата локальным блоком, чтобы локальные переменные уничтожались до вызова перехватываемой функции:
    1. public OnDialogResponse(playerid, dialogid, response, listitem, inputtext[])
    2. {
    3. {
    4. // Локальные переменные создавать здесь
    5. }
    6. // Здесь локальные переменные уже перестанут существовать, высвободив место в стеке
    7. #if defined test__OnDialogResponse
    8. return test__OnDialogResponse(playerid, dialogid, response, listitem, inputtext);
    9. #endif
    10. }

    Впрочем, в вашем коде switch выполняет всё ту же роль локального блока (то же самое может и if, и все виды циклов), но в отчёте компилятора расчётное использование стека всё ещё заметно повышается из-за перехвата. Объяснение этому только одно: компилятор при подсчёте учитывает не то, сколько места в стеке используется на момент вызова следующей функции в цепочке вызовов, а максимальное использование стека в пределах всей функции, из-за чего расчётное использование стека может значительно отличаться от фактического. Просто так этот баг не исправить, поэтому могу посоветовать только иметь в виду, что ваш случай довольно специфический и обращать внимание следует только если компилятор вдруг начнёт выдавать ещё большие числа неизвестно откуда.
    Индивидуально в ЛС по скриптингу не помогаю. Задавайте все свои вопросы здесь (click).

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

    Статус
    Оффлайн
    Регистрация
    08.12.2018
    Адрес
    Россия
    Сообщений
    146
    Репутация:
    25 ±
    Вариант с помещением кода в блок до вызова перехватываемой функции я пробовал, результат остался прежним, после чего я сразу побежал снова искать вашу тему о перехватах функции, где вы указали на подобный принцип экономии памяти.
    Этим я хотел сделать архитектуру проекта, где-бы каждая система была помещена в отдельный файл. Может быть у вас есть какие-нибудь предложения, как можно сделать подобное?
    Создам другую тему для решения вопроса.
    Спасибо за ответ

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

    Статус
    Оффлайн
    Регистрация
    27.01.2014
    Адрес
    Восточный Мордор
    Сообщений
    5,588
    Репутация:
    1984 ±
    Так а что не так? Если напрягает потребление памяти, то компилятор просто рассчитывает отдельно размер под вызываемые функции и отдельно под массивы. То бишь, у тебя не все функции будут складываться подобным образом, потребляя стек, а из них будет определена одна, которая потребляет больше всего стека, и учитываться будет её размер.

    То бишь, этот код, грубо говоря, будет потреблять 1000 ячеек, а не 1200.
    PHP код:
    main()
    {
        new 
    string[500];
        
    SomeFunc1();
        
    SomeFunc2();
    }

    stock SomeFunc1()
    {
        new 
    string[500];
    }

    stock SomeFunc2()
    {
        new 
    string[200];

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

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

    Steve Pavlina

  6. #6
    Аватар для Daniel_Cortez
    "Это не хак, это фича"

    Статус
    Оффлайн
    Регистрация
    06.04.2013
    Адрес
    Novokuznetsk, Russia
    Сообщений
    2,192
    Репутация:
    2589 ±
    Цитата Сообщение от DeimoS Посмотреть сообщение
    То бишь, этот код, грубо говоря, будет потреблять 1000 ячеек, а не 1200.
    PHP код:
    main()
    {
        new 
    string[500];
        
    SomeFunc1();
        
    SomeFunc2();
    }

    stock SomeFunc1()
    {
        new 
    string[500];
    }

    stock SomeFunc2()
    {
        new 
    string[200];

    Не совсем корректный пример, компилятор и так выдаёт в нём 1000 ячеек. Соль в том, что компилятор подсчитывает использование стека массивами так, как будто все массивы в цепочке вызова функций существуют одновременно, даже если это не так.
    Вот упрощённый пример:
    1. stock SomeFunc()
    2. {
    3. new arr2[500];
    4. }
    5.  
    6. main()
    7. {
    8. SomeFunc();
    9. new arr1[500];
    10. }

    На деле массивы arr1 и arr2 существуют по отдельности и использование стека не превышает 500 ячеек, но компилятор в отчёте выдаёт все 1000 (на самом деле 1007, т.к. ещё 7 ячеек используются для вызова функций main и SomeFunc).
    Индивидуально в ЛС по скриптингу не помогаю. Задавайте все свои вопросы здесь (click).

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

    Статус
    Оффлайн
    Регистрация
    27.01.2014
    Адрес
    Восточный Мордор
    Сообщений
    5,588
    Репутация:
    1984 ±
    Цитата Сообщение от Daniel_Cortez Посмотреть сообщение
    Не совсем корректный пример, компилятор и так выдаёт в нём 1000 ячеек. Соль в том, что компилятор подсчитывает использование стека массивами так, как будто все массивы в цепочке вызова функций существуют одновременно, даже если это не так.
    Вот упрощённый пример:
    1. stock SomeFunc()
    2. {
    3. new arr2[500];
    4. }
    5.  
    6. main()
    7. {
    8. SomeFunc();
    9. new arr1[500];
    10. }

    На деле массивы arr1 и arr2 существуют по отдельности и использование стека не превышает 500 ячеек, но компилятор в отчёте выдаёт все 1000 (на самом деле 1007, т.к. ещё 7 ячеек используются для вызова функций main и SomeFunc).
    Ты просто не понял суть моего примера :)
    Я отвечал на возможные опасения автора о том, что теперь каждая подключаемая функция будет потреблять стек, хотя на деле будет учитываться только одна функция, которая потребляет больше всего + переменные из паблика, а не все вызываемые функции + переменные из паблика.
    Я, собственно, то же самое пытался передать, что и ты, просто ещё добавил объяснение про вызов дополнительных функций.
    Связаться со мной в VK можно через личные сообщения этой группы
    Заказы не принимаю

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

    Steve Pavlina

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

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

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

    Статус
    Оффлайн
    Регистрация
    27.01.2014
    Адрес
    Восточный Мордор
    Сообщений
    5,588
    Репутация:
    1984 ±
    Цитата Сообщение от punkochel Посмотреть сообщение
    Получается что на практике стек будет иметь размер, подобный тому если мы просто "волшебным образом" добавим код из файла в мод?
    Нет. Если бы код из функций переместили в паблик, то и размер стека рассчитывался бы для всего кода целиком...
    В общем, постараюсь показать на примерах:
    1. main()
    2. {
    3. {
    4. new array1[500];
    5. #pragma unused array1
    6. }
    7. {
    8. new array2[500];
    9. #pragma unused array2
    10. }
    11.  
    12. /*
    13.   Хоть вместе размер двух массивов сверху будет равен 1000,
    14.   на деле под массивы будет выделено всего 500 ячеек стека, так как
    15.   оба находятся в отдельных блоках кода и каждый раз, когда
    16.   обработка конкретного блока кода будет завершена, массив будет удаляться из
    17.   стека и занимаемое им место станет доступно для другого массива.
    18.   */
    19.  
    20.  
    21. /*
    22.   С функциями работает практически тот же принцип (одна функция - один отдельный блок кода).
    23.   И, в итоге, при вычислении потребляемого стека, компилятор будет ориентироваться на функцию,
    24.   которая потребляет больше всего стека, а не будет складывать размер потребляемого стека всех функций.
    25.   То бишь, функция начала обрабатываться, создала все свои локальные переменные и когда обработка закончилась,
    26.   все переменные из памяти удаляются. Поэтому компилятор учитывает размер самой "тяжёлой" функции, так как в таком
    27.   случае этого размера хватит для всех остальных функций. В общем, тот же самый принцип, что и при рассчёте примера выше.
    28.   */
    29. SomeFunc1();
    30. SomeFunc2();
    31.  
    32. /*
    33.   Но весь нюанс заключается в том, что хоть на этапе, когда дело дойдёт до вызова функций,
    34.   массивы уже не будут существовать в памяти и функции, по идее, должны занимать стек,
    35.   который занимали массивы, компилятор этого не учитывает и просто складывает потребляемое массивами
    36.   количество ячеек с тем, которое потребляется функциями.
    37.  
    38.   То бишь, при расчёте размера стека отдельно учитывается размер для переменных и отдельно размер для функций,
    39.   а после они складываются.
    40.   */
    41. }
    42.  
    43. stock SomeFunc1()
    44. {
    45. new array[500];
    46. #pragma unused array
    47. }
    48.  
    49. stock SomeFunc2()
    50. {
    51. new array[200];
    52. #pragma unused array
    53. }


    В этом случае стек будет равен 1004 ячейкам. Если же просто вставить код функций напрямую, поместив его в отдельные блоки:
    1. main()
    2. {
    3. {
    4. new array1[500];
    5. #pragma unused array1
    6. }
    7. {
    8. new array2[500];
    9. #pragma unused array2
    10. }
    11.  
    12. {
    13. new array[500];
    14. #pragma unused array
    15. }
    16. {
    17. new array[500];
    18. #pragma unused array
    19. }
    20. }

    То размер стека станет равным 503 ячейкам. Собственно, если бы не баг компилятора, то и в первом случае он был бы равным 503 ячейкам. Поэтому вставка кода напрямую != вызов функции. По крайней мере пока не исправят этот баг с компилятором.

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

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

    Steve Pavlina

  10. Пользователь сказал cпасибо:
    punkochel (18.06.2019)
 

 

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

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

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

Ваши права

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