PDA

Просмотр полной версии : [Урок] Мифы о Pawn-скриптинге - #9



Daniel_Cortez
13.07.2016, 16:00
Внимание: данная тема закрыта для защиты от копирования.
Если есть какие-то вопросы, замечания или просто пожелания по поводу данного урока - оставляйте их здесь (http://pro-pawn.ru/showthread.php?12774-%D0%9E%D0%B1%D1%81%D1%83%D0%B6%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5).




Миф 9: "Для экономии памяти лучше всегда использовать PVar'ы".

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

Описание:

Ещё один миф о мнимой оптимизации. Как ни странно, распространён преимущественно у более-менее разбирающихся скриптеров, нежели у новичков.
Заблудшие в подтверждение своим словам приводят доводы о том, что якобы PVar'ы занимают место только тогда, когда используются (до вызова DeletePVar), и поэтому можно сэкономить память, используя их вместо обычных переменных/массивов в Pawn.


Доказательство:
Для начала изучим структуру PVar'ов, её можно найти в утекших исходниках SA-MP 0.3d (учитывая темпы развития стагнации мультиплеера, можно смело предположить, что в последней версии ничего не изменилось).
Собственно, вот сама структура: https://github.com/Sasuke78200/open-samp/blob/master/Open%20SAMP/object/class_player_var.h#L13-L22

typedef struct PVAR_DATA_t
{
#pragma pack( 1 )
char pVarName[MAX_PVAR_NAME + 1];
BOOL isReadOnly;
uint32_t pVarType;
int intValue;
float floatValue;
char* stringValue;
} PVAR_DATA;

Для тех, кому лень считать: структура занимает 58 байт, т.е. по 58 байт занимает каждый PVar. Для сравнения, обычная переменная в Pawn занимает всего 4 байта.
Выглядит структура довольно "интересно". Свойство isReadOnly позволяет перевести PVar в состояние "только для чтения", но эта возможность нигде не используется (в SA-MP нет PVar'ов, доступных только для чтения).
Кроме того, для хранения данных отведены целых 3 отдельных свойства: одно для целых чисел, другое для вещественных и третье для строк, что довольно странно, поскольку нельзя использовать PVar для одновременного хранения данных разных типов (т.е. нельзя, например, сохранить сразу число и строку).

В том же исходном файле ниже можно найти структуру класса CPlayerVar: https://github.com/Sasuke78200/open-samp/blob/master/Open%20SAMP/object/class_player_var.h#L24-L52

class CPlayerVar
{
#pragma pack( 1 )
public:

PVAR_DATA pVars[MAX_PVARS];
BOOL isPVarActive[MAX_PVARS];
uint32_t upperIndex;

CPlayerVar();
~CPlayerVar();

int AddVar(char* varName);
int DeleteVar(char* varName);

int SetIntVar(char* varName, int varValue, BOOL readOnly = 0);
int SetStringVar(char* varName, char* varString, BOOL readOnly = 0);
int SetFloatVar(char* varName, float varValue, BOOL readOnly = 0);

int GetIntVar(char* varName);
char* GetStringVar(char* varName);
float GetFloatVar(char* varName);

int GetVarType(char* varName);
char* GetVarNameFromID(int pVarID);

int FindVarID(char* varName);
void UpdateUpperID();
};

Это структура, место для которой выделяется отдельно для каждого игрока при подключении и высвобождается при выходе игрока с сервера.
Объявление MAX_PVARS можно найти в том же файле, оно равно 800 (собственно, уже давно известно, что можно создать до 800 PVar'ов для каждого игрока).
В структуре можно сразу же заметить самое первое свойство:

PVAR_DATA pVars[MAX_PVARS];

Это говорит о том, что под все 800 PVar'ов используется один общий блок памяти на каждого игрока, а значит все утверждения о том, что "PVar можно удалить и он больше не будет занимать место" ошибочны.

Проверим наш вывод. Найдём в файле class_player_var.cpp (https://github.com/Sasuke78200/open-samp/blob/master/Open%20SAMP/object/class_player_var.cpp) все случаи использования функций malloc/calloc/free и операторов new/delete.
Никакие функции для динамического выделения памяти там не используются, а new и delete используются только для резервирования/высвобождения места под строку, сохраняемую с помощью SetPVarString.
Остальное содержимое хранится в одном общем для всех 800 PVar'ов блоке памяти, который остаётся зарезервированным на протяжении всего времени нахождения игрока на сервере. Что и требовалось доказать.

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


P.S.: Рассматривалось лишь утверждение про экомию памяти при "создании" и "удалении" PVar'ов (на самом деле память под них резервируется/высвобождается не в SetPVar(Int/Float/String)/DeletePVar, а при подключении/выходе игрока).
P.P.S: Отдельный блок памяти может выделяться под строку при использовании SetPVarString, оно же и высвобождается при вызове DeletePVar, но остальных типов данных и самих PVar'ов это не касается. Как результат, в некоторых случаях может быть целесообразно использование PVar'ов для хранения строк.



Специально для Pro-Pawn.ru (http://www.pro-pawn.ru)
Не разрешается копирование данной статьи на других ресурсах без разрешения автора.