PDA

Просмотр полной версии : [Вопрос] Как правильно использовать стек



punkochel
05.11.2020, 02:00
Задался таким вот вопросом.
Как мне известно из различных ресурсов программирования, память которую мы используем из стека, работает на порядок быстрее сегмента данных. Мне стало интересно, как лучше использовать память стека и решил поиграться с профайлером от DC (https://pro-pawn.ru/showthread.php?12585):

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


new String_64[64];

#define Prerequisites();

#define CodeSnippet0();\
TestCode1();

#define CodeSnippet1();\
TestCode2();


stock TestCode1() // Stack
{
goto label;
new string[64];
label:
for(new i; i < sizeof(string); i++) {

string[i] = i;
if(string[i] != i)
break;
}
}

stock TestCode2() // Data size
{
for(new i; i < sizeof(String_64); i++) {

String_64[i] = i;
if(String_64[i] != i)
break;
}
}

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

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

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

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

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

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


stock OptionOne(playerid) {

new string[2048];
for(new i; i < 128; i++) {

format(string, sizeof(string), "%sКакой-то текст...\n", string);
}
ShowPlayerDialog(playerid, 0, DIALOG_STYLE_MSGBOX, "caption", string, "button1", "button2");
return 1;
}

new String_2048[2048];
stock OptionTwo(playerid) {

String_2048[0] = EOS;
for(new i; i < 128; i++) {

format(String_2048, sizeof(String_2048), "%sКакой-то текст...\n", String_2048);
}
ShowPlayerDialog(playerid, 0, DIALOG_STYLE_MSGBOX, "caption", String_2048, "button1", "button2");
return 1;
}

DeimoS
05.11.2020, 12:45
Как мне известно из различных ресурсов программирования, память которую мы используем из стека, работает на порядок быстрее сегмента данных.]

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




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

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

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


stock OptionOne(playerid) {

new string[2048];
for(new i; i < 128; i++) {

format(string, sizeof(string), "%sКакой-то текст...\n", string);
}
ShowPlayerDialog(playerid, 0, DIALOG_STYLE_MSGBOX, "caption", string, "button1", "button2");
return 1;
}

new String_2048[2048];
stock OptionTwo(playerid) {

String_2048[0] = EOS;
for(new i; i < 128; i++) {

format(String_2048, sizeof(String_2048), "%sКакой-то текст...\n", String_2048);
}
ShowPlayerDialog(playerid, 0, DIALOG_STYLE_MSGBOX, "caption", String_2048, "button1", "button2");
return 1;
}


Не получается. Если у тебя есть какая-то информация, которую тебе нужно использовать только в конкретной функции и не требуется её долговременное хранение - всегда используй только стек. Даже если у тебя массив получается большим и стандартный размер стека заполняется - просто увеличь стек, а не создавай глобальный массив. Вот тут (https://pro-pawn.ru/showthread.php?16533-%D0%92%D0%BE%D0%BF%D1%80%D0%BE%D1%81-%D0%BF%D0%BE-Stack-heap-size&p=92930&viewfull=1#post92930) я подробно описывал почему.

punkochel
05.11.2020, 13:56
Отличная статья, в голове появилось четкое понимание стека.
Так вот, со стеком понятно, теперь возникло еще 2 вопроса:

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

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

UPD: Добавил пример ко второму вопросу:

for(new i; i < 100; i++) {

SaveInDataBase(i);
}

stock SaveInDataBase(Id) {

static const fmt_query[] "UPDATE `account` SET `Money` = '%d' WHERE `Id` = '%d'";
new string[sizeof(fmt_query)+(-2+11)+(-2+3)+1];
mysql_format(dbHandle, string, sizeof(string), fmt_query, 1000, Id);
mysql_tquery(dbHandle, string);
return 1;
}
В таком случае, каждую итерацию цикла, будет создаваться массив в несколько ячеек. Или просто стоит использовать обход инициализации массива?

DeimoS
05.11.2020, 14:56
1) Проще будет дать ссылки на официальную документацию по языку



Сторонние источники информации:
Pawn Implementer's Guide (https://raw.githubusercontent.com/compuphase/pawn/66e67291326b193045e52e4d6bcb51663260d6c3/doc/pawn-imp.pdf)
Copyright (c) 1997–2006, ITB CompuPhase

Pawn Language Guide (https://raw.githubusercontent.com/compuphase/pawn/66e67291326b193045e52e4d6bcb51663260d6c3/doc/pawn-lang.pdf)
Copyright (c) 1997–2006, ITB CompuPhase



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

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

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

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

punkochel
05.11.2020, 15:08
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

За это спасибо. Нужно будет почитать обязательно.

DeimoS
05.11.2020, 15:22
Тогда хотелось бы увидеть пример реализации сохранения аккаунтов при рестарте сервера, на котором, на момент непосредственно самого рестарта, находиться 1000 игроков.

Сохраняй данные в момент их изменения.
Нет, сервер от такого не умрёт.
Да, и MySQL тоже.



Захотел я выдать всем игрокам онлайн (а их 1000 на сервере) по 1 млрд вирт. Как бы ты реализовал это?

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



Это то понятно что можно так сделать. А если эта функция используется повсеместно, и вдруг я захочу немного её изменить, то мне придется лопатить весь мод, и наверняка, где-нибудь, что-то упущу. Мне интересен момент, как можно реализовать именно с таким подходом, при это затратив минимум ресурсов.

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

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

punkochel
05.11.2020, 15:43
Я тут значит хочу что-то сложное придумать, а ты все выкручиваешься)
Со стеком разобрались, не вижу ничего страшного, если в некоторых моментах использовать память из сегмента данных.
Даже если выделить 4096*4 байт (размер дефолтного стека), ибо везде есть массив, хранящий Имя игрока, а это, на минуточку 1000*24*4 байт (MAX_PLAYERS*MAX_PLAYER_NAME*4), хотя можно хранить и в упакованном виде, но сути не меняет. Даже можно вообще отказаться от стека в отношении локальных массивов используемых для форматирования.

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

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

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

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

DeimoS
05.11.2020, 16:40
Я тут значит хочу что-то сложное придумать, а ты все выкручиваешься)
Со стеком разобрались, не вижу ничего страшного, если в некоторых моментах использовать память из сегмента данных.
Даже если выделить 4096*4 байт (размер дефолтного стека), ибо везде есть массив, хранящий Имя игрока, а это, на минуточку 1000*24*4 байт (MAX_PLAYERS*MAX_PLAYER_NAME*4), хотя можно хранить и в упакованном виде, но сути не меняет. Даже можно вообще отказаться от стека в отношении локальных массивов используемых для форматирования.

Ты не видишь ничего страшного, а твой сервер начнёт всё больше и больше памяти жрать, если ты будешь глобальные массивы по поводу и без создавать. Если у тебя есть какая-то временная информация, которая используется только внутри тела конкретной функции, и ты для неё создаёшь глобальный массив - ты написал плохой код, который нерационально расходует память. И никак иначе.


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

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

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


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

Ничего от этого не случится. Хорошо организованная таблица и правильно составленные запросы могут гарантировать то, что MySQL спокойно тысячи запросов в секунду прожуёт и не поперхнётся. А если ещё и серверное оборудование нормальное будет, то и до десятков тысяч эта цифра доходить будет.

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


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

Эмм, нет. В статье как раз таки развенчивался этот миф. Вот, перечитай - https://pro-pawn.ru/showthread.php?12988
Ну и да: params - это всё ещё локальная переменная (массив). И использует она стек, а не сегмент данных.

punkochel
05.11.2020, 17:02
+ добавляешь запрос на сохранение всех таких данных при выходе из сервера.
Ну, а я о чем? Не просто же так я приводил пример о рестарте сервера с 1000 игроков онлайна. Все-равно придется реализовать систему так:

foreach(new i:Player) {

// Запрос на сохранение для каждого игрока
}
Тем самым, ты получишь флуд запросами (1000 запросов);

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

DeimoS
05.11.2020, 17:14
Ну, а я о чем? Не просто же так я приводил пример о рестарте сервера с 1000 игроков онлайна. Все-равно придется реализовать систему так:

foreach(new i:Player) {

// Запрос на сохранение для каждого игрока
}
Тем самым, ты получишь флуд запросами (1000 запросов);

Эмм, зачем там цикл?
Ещё раз: не при рестарте, а при выходе конкретного игрока. Это будет запрос, в стиле:
format(query_string, sizeof(query_string), "UPDATE account SET mute_time=%i,jail_time=%i WHERE id=%d", ...);
mysql_tquery(...);
И этот запрос не обязательный. Просто без него игроку придётся каждый раз сидеть дополнительные сколько секунд при перезаходе на сервер, так как сохраняться данные будут только каждые 10 секунд.

punkochel
05.11.2020, 17:21
Верно, именно вот эти 10 секунд. Когда идет рестарт сервера, то вызывается OnPlayerDisconnect (Как ни странно), следовательно нужно предварительно отключить игроков от сервера с сохранением данных, а это как-раз таки цикл...
Ну или можно прописывать так:
if(IsPlayerConnected(0)) Kick(0);
if(IsPlayerConnected(1)) Kick(1);
if(IsPlayerConnected(2)) Kick(2); ...

Тему можно закрыть, узнал много полезного, но не все конечно что хотел.

DeimoS
05.11.2020, 17:36
Верно, именно вот эти 10 секунд. Когда идет рестарт сервера, то вызывается OnPlayerDisconnect (Как ни странно), следовательно нужно предварительно отключить игроков от сервера с сохранением данных, а это как-раз таки цикл...
Ну или можно прописывать так:
if(IsPlayerConnected(0)) Kick(0);
if(IsPlayerConnected(1)) Kick(1);
if(IsPlayerConnected(2)) Kick(2); ...

Тему можно закрыть, узнал много полезного, но не все конечно что хотел.

Эмм, зачем кого-то отключать от сервера? Сервер сам всё это сделает, если рестарт будет производиться заложенными в это средствами - через команды "gmx" или "exit". Если же ты убиваешь серверный процесс на стороне хостинга, то твой код в любом случае не будет вызываться, так как сервер и не поймёт, что его собираются перезагружать.

Если не всё, что хотел, узнал - задавай дополнительные вопросы.

Shaolinka
05.11.2020, 17:42
Верно, именно вот эти 10 секунд. Когда идет рестарт сервера, то вызывается OnPlayerDisconnect (Как ни странно), следовательно нужно предварительно отключить игроков от сервера с сохранением данных, а это как-раз таки цикл...
Ну или можно прописывать так:
if(IsPlayerConnected(0)) Kick(0);
if(IsPlayerConnected(1)) Kick(1);
if(IsPlayerConnected(2)) Kick(2); ...

Тему можно закрыть, узнал много полезного, но не все конечно что хотел.

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

punkochel
05.11.2020, 18:11
Ты не видишь ничего страшного, а твой сервер начнёт всё больше и больше памяти жрать, если ты будешь глобальные массивы по поводу и без создавать. Если у тебя есть какая-то временная информация, которая используется только внутри тела конкретной функции, и ты для неё создаёшь глобальный массив - ты написал плохой код, который нерационально расходует память. И никак иначе.
Вопрос 1: Почему-бы, не создать один большой массив в сегменте данных, скажем размером в 4096 ячеек и использовать его для форматирования различного содержательного текста?
Да, стек так и так забрал себе память, но в таком случае пропадет необходимость ручного подсчета строки и пропадет возможная ситуация, когда массива не хватит;
Данная реализация будет даже работать быстрее чем выделение памяти из стека (см. тесты в начале темы).


Ничего от этого не случится. Хорошо организованная таблица и правильно составленные запросы могут гарантировать то, что MySQL спокойно тысячи запросов в секунду прожуёт и не поперхнётся. А если ещё и серверное оборудование нормальное будет, то и до десятков тысяч эта цифра доходить будет.
Вопрос 2: Как это "Хорошо организованная таблица", и "правильно составленные запросы"?
Приведу пример, как я понимаю это: Таблица accounts, в ней находятся поля ID, Name, Money, Level, Exp, House, Business, Member, Rank... Во время Pay Day я обрабатываю игрока:
new query[84+(-2+4)+(-2+5)+(-2+11)+(-2+8)+1];
foreach(new i:Player) {

pInfo[i][Exp]++;
if(pInfo[i][Exp] > 8) {

pInfo[i][Exp] = 0;
pInfo[i][Level]++;

if(pInfo[i][Level] == 10) {

pInfo[i][Money] += 1000;
format(query, sizeof(query), "UPDATE `accounts` SET `Level` = '%d', `Exp` = '%d', `Money` = '%d' WHERE `ID` = '%d'",
pInfo[i][Level], pInfo[i][Exp], pInfo[i][Money], pInfo[i][ID]);
}
else
format(query, sizeof(query), "UPDATE `accounts` SET `Level` = '%d', `Exp` = '%d' WHERE `ID` = '%d'",
pInfo[i][Level], pInfo[i][Exp], pInfo[i][ID]);
}
else
format(query, sizeof(query), "UPDATE `accounts` SET `Exp` = '%d' WHERE `ID` = '%d'",
pInfo[i][Exp], pInfo[i][ID]);

mysql_tquery(id_DB, query);
}

DeimoS
05.11.2020, 18:29
Создай булевую переменную для каждого игрока, при рестарте пройдись по всем игрокам, присвоив значение той булевой переменной на true. При выходе сверяй, был ли ли отключен игрок с помощью рестарта. Если да - сохраняй его данные.

Да зачем это всё? Почему вы пытаетесь решать проблему, которой нет?



Вопрос 1: Почему-бы, не создать один большой массив в сегменте данных, скажем размером в 4096 ячеек и использовать его для форматирования различного содержательного текста?
Да, стек так и так забрал себе память, но в таком случае пропадет необходимость ручного подсчета строки и пропадет возможная ситуация, когда массива не хватит;

Подсчёт строки занимает 10-30 секунд. При этом, глобальная переменная не только память лишнюю отжирает, но и увеличивает шанс допуска ошибки, которую ты уже будешь отыскивать гораздо дольше. Не говоря уже о том, что и читаемость кода ухудшается, ибо локальные переменные ты и назвать можешь информационно, и будет чётко видно где и какие данные начинают записываться. Если не забывать, что код, который ты пишешь, тебе ещё в будущем придётся поддерживать - станет очевидно, что сиюминутная польза глобального массива не стоит того, чтоб потом тратить кучу времени на попытки вспомнить, что же ты там писал пару месяцев назад. Ты либо сразу это поймёшь, либо когда напишешь достаточно объёмный скрипт. Но понимание этого придёт в любом случае. Благо люди не просто так со временем пришли к введению различных стандартов по синтаксису в разных ЯП. Это не ради забавы делается :)


Данная реализация будет даже работать быстрее чем выделение памяти из стека (см. тесты в начале темы).

Касаемо тестов:
Во-первых, они у тебя некорректные.
Во-вторых, как я и писал - разница будет только на уровне погрешности. На деле разницы по скорости не будет.



Вопрос 2: Как это "Хорошо организованная таблица", и "правильно составленные запросы"?

Это значит, что ты знаком с MySQL не на уровне уроков с Pawn-форумов, а конкретно изучил язык SQL, знаешь все его особенности и знаком с вариантами оптимизации, которые уже придумало сообщество (пример (https://ru.wikipedia.org/wiki/%D0%9D%D0%BE%D1%80%D0%BC%D0%B0%D0%BB%D1%8C%D0%BD%D0%B0%D1%8F_%D1%84%D0%BE%D1%80%D0%BC%D0%B0)).
Минимум, что нужно - это уметь создавать и работать с индексами, а так же знать все подноготные настроек таблицы, чтоб уметь её гибко настраивать.

punkochel
05.11.2020, 18:53
Спасибо, буду учиться!)

Еще вопрос тогда:
Как лучше хранить объемные тексты?

После сегодняшней дискуссии я склоняюсь к этому:
stock OtherFunction() {

static const fmt_dialog[] = !"Тут текст длиною в %i (1024) символа";
new string[sizeof(fmt_dialog)*4 +1];
strunpack(string, fmt_dialog);
format(string, sizeof(string), string, 1024);
printf("%s", string);
}

Или, все-таки не стоит хранить текст, который нужно форматировать, в упакованном виде?

DeimoS
05.11.2020, 20:04
Спасибо, буду учиться!)

Еще вопрос тогда:
Как лучше хранить объемные тексты?

После сегодняшней дискуссии я склоняюсь к этому:
stock OtherFunction() {

static const fmt_dialog[] = !"Тут текст длиною в %i (1024) символа";
new string[sizeof(fmt_dialog)*4 +1];
strunpack(string, fmt_dialog);
format(string, sizeof(string), string, 1024);
printf("%s", string);
}

Или, все-таки не стоит хранить текст, который нужно форматировать, в упакованном виде?

Особого смысла в этом нет. Ты лишь больше лишних действий производишь. Если бы были критичные проблемы с нехваткой памяти, тогда да - это имело бы смысл. Но сейчас ты пытаешься экономить память, которой и так в достатке, жертвуя при этом временем на обработку кода, которое как раз и не имеет больших излишков.



Ну и да: всё же если ты хочешь нормальный тест, то стоило его сделать хотя бы таким:
// Кол-во итераций в циклах.
const PROFILER_ITERATIONS_MAJOR = 10_000;
const PROFILER_ITERATIONS_MINOR = 1_000;


new const code_snippets_names[2][] =
{
{"global_array"},
{"local_array"}
};

new global_array[100];

#define Prerequisites(); \
new local_array[100];


#define CodeSnippet0(); \
if(global_array[50] == 10)\
global_array[50] = 0;\
else\
global_array[50] = 10;


#define CodeSnippet1(); \
if(local_array[50] == 10)\
local_array[50] = 0;\
else\
local_array[50] = 10;
Ибо в твоём варианте у локальных переменных будет больше действий выполняться, чем у глобальных.
Ну и сделай сразу штук 10 тестов, чтоб убедиться, что быстрее будут то локальные, то глобальные переменные (то бишь, всё на уровне погрешности)

punkochel
05.11.2020, 20:53
Тогда я попробую подвести итог темы:

Не стоит использовать память из сегмента данных для реализации локальных действий, а именно:
- Если стоит отформатировать и вывести текст, то лучше использовать локальный массив (стек), так как память в стеке выделена по умолчанию, и нет смысла создавать ему подобные реализации, тем самым впустую расходовать память;

- Если вопрос стоит в обходе подсчета строки (лень), то лучше потратить время на её подсчет и тем самым грамотно распорядиться памятью из стека, так как, опять же не стоит создавать глобальные массивы, для реализации подобного рода систем;


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

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

DeimoS
05.11.2020, 22:34
Не стоит использовать память из сегмента данных для реализации локальных действий, а именно:
- Если стоит отформатировать и вывести текст, то лучше использовать локальный массив (стек), так как память в стеке выделена по умолчанию, и нет смысла создавать ему подобные реализации, тем самым впустую расходовать память;

- Если вопрос стоит в обходе подсчета строки (лень), то лучше потратить время на её подсчет и тем самым грамотно распорядиться памятью из стека, так как, опять же не стоит создавать глобальные массивы, для реализации подобного рода систем;

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

И да, и нет. Ещё раз обращу внимание на фактор читаемости кода и на фактор устойчивости к ошибкам.

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

Касаемо устойчивости к ошибкам: используя один общий глобальный массив, ты самостоятельно убиваешь одну из способностей компилятора, которая помогает тебе отыскать твои ошибки ещё на стадии компиляции - ошибку с указанием неправильной переменной.
Приведу пример, который уже несколько раз описывал на форуме:
Представим, что у тебя есть глобальный массив "global_string". Ты используешь его при форматировании сообщений и у тебя, казалось бы, жизнь удалась. Ты просто можешь копировать уже написанный ранее код форматирования какого-то сообщения, изменить в нём текст и не тратить время на написание "format" и "SendClientMessage" со всем вытекающим. Но тут у тебя возникла ситуация, при которой ты не можешь использовать свой массив: например, у тебя есть ветвление условий, в котором ты форматируешь разный текст, и чтоб там не дублировать одинаковый SendClientMessage, ты просто вынес его за тело условий, а-ля:
switch(variable)
{
case 0:
{
// Тут какой-то код
format(global_string, sizeof(global_string), "...");
}
case 2:
{
// Тут какой-то код
format(global_string, sizeof(global_string), "...");
}
case 3:
{
// Тут какой-то код
format(global_string, sizeof(global_string), "...");
}
}
SendClientMessage(playerid, -1, global_string);

И тут тебе, например, понадобилось ещё и запросы форматировать к БД, для которых ты обычно так же global_string использовал. Ты решаешь создать новую переменную и, чтоб сэкономить время, просто копируешь форматирование запроса с другого участка кода, чуть переписав его. Напомню, что ты "global_string" используешь и для отправки запросов обычно (ибо так обычно в паблик модах и делают). В итоге получается следующее:
new query_string[...];
switch(variable)
{
case 0:
{
// Тут какой-то код
format(global_string, sizeof(global_string), "...");
format(query_string, sizeof(query_string), "запрос");
}
case 2:
{
// Тут какой-то код
format(global_string, sizeof(global_string), "...");
format(query_string, sizeof(query_string), "запрос");
}
case 3:
{
// Тут какой-то код
format(global_string, sizeof(global_string), "...");
format(query_string, sizeof(query_string), "запрос");
}
}
SendClientMessage(playerid, -1, global_string);
mysql_tquery(mysql_connection_ID, global_string, "", "");
Но вот незадача: в format ты название массива сменил, а вот в mysql_tquery забыл. Но, компилируя, ты никаких ошибок не получишь, ибо, с точки зрения компилятора, тут никаких синтаксических ошибок нет: все переменные существуют.

Приведённый пример, конечно, притянут за уши, ибо я не смог вспомнить реальные примеры, которые встречал неоднократно за всё время выполнение заказов по Pawn, но общая суть тут передаётся. Причину ошибки, если ты раньше ничего подобного не встречал, ты можешь искать и минуту, и десять минут, и час. А если бы ты взял за привычку работать с локальными массивами и давал бы им адекватные названия, то, при копировании кода из одной системы в другую, компилятор бы сообщил тебе об ошибке, так как в этом блоке кода такого названия бы не было. Собственно, вот так ты и усложняешь себе жизнь, повышая риск допущения ошибок.
Это, собственно, тема из той же оперы, что и присвоение нормальных названий переменным/функциям, использование строгих правил синтаксиса или же, например, правила, при котором тело функции не должно быть длиннее 60 строк. Всё это, с первого взгляда, кажется мелочью, но на деле, когда ты начинаешь всего этого придерживаться и чётко структурировать свой код, твоя производительность повышается многократно, так как ты меньше времени тратишь на исправление ошибок (ибо делаешь их меньше) и меньше времени тратишь на изучение уже написанного кода, так как он не представляет из себя хаос и разруху, которую можно увидеть в любом паблик-моде.

Shaolinka
05.11.2020, 23:31
В паблик модах забывают почистить глобальный перед началом использования, что приводит к огромнейшим проблемам. Или выделяют овер дофига ячеек по причине: «шобы была на патом», потом же в силу вступает #pragma dynamic.
И как гаварица:

https://www.youtube.com/watch?v=jHbpWb3ZNGY

punkochel
06.11.2020, 07:25
Я понял что нет смысла создавать второй отдел памяти подобный стеку, когда есть сам стек. А что касается путаницы внутри самой функции, то лично я, как правило, создаю массив перед первым его использованием и уже дальше использую его в в дальнейший функциях внутри самой функции... Капец, ну, думаю понятно объяснил.
В общем, один массив используется для всех форматирований внутри одной функции. И это почти то-же самое, что использовать глобальный массив, вернее даже, возможность запутаться будет равна между локальным и глобальным массивом.

stock OtherFunction() {

if(1 == 1)
return 1;

new string[];
else if(1 != 2) {

format(string, sizeof(string), "", );
SendClientMessage(0, -1, string);
}

else if(1 < 0) {

format(string, sizeof(string), "", );
SendClientMessage(0, -1, string);
}
return 0;
}

Или ты хочешь сказать что будет более разборчивее создавать массив в теле условия?

DeimoS
06.11.2020, 11:20
Я понял что нет смысла создавать второй отдел памяти подобный стеку, когда есть сам стек. А что касается путаницы внутри самой функции, то лично я, как правило, создаю массив перед первым его использованием и уже дальше использую его в в дальнейший функциях внутри самой функции... Капец, ну, думаю понятно объяснил.
В общем, один массив используется для всех форматирований внутри одной функции. И это почти то-же самое, что использовать глобальный массив, вернее даже, возможность запутаться будет равна между локальным и глобальным массивом.

stock OtherFunction() {

if(1 == 1)
return 1;

new string[];
else if(1 != 2) {

format(string, sizeof(string), "", );
SendClientMessage(0, -1, string);
}

else if(1 < 0) {

format(string, sizeof(string), "", );
SendClientMessage(0, -1, string);
}
return 0;
}

Или ты хочешь сказать что будет более разборчивее создавать массив в теле условия?

В создании локального массива, для использования его в небольшом участке кода, нет ничего плохого. Не идеально, но можно хотя бы чётко увидеть то, в какой момент данные могут начать записываться, а в какой данные ещё точно не записываются. Но, конечно, не стоит делать локальный массив на целый паблик, например, и т.п. Всегда нужно уметь балансировать между созданием отдельной переменной для каждого случая и объединением нескольких действий в одной переменной :)

punkochel
06.11.2020, 12:51
Спасибо, особо ничего нового не узнал, но сомнения в ряде вопросов развеялись. Заставил взглянуть меня на ситуацию с другой стороны. Раньше были какие-то стереотипы, якобы если использовать стек для создания объемного массива - это плохо, использовать большой sql-запрос - плохо, создавать PVar ради экономии памяти - хорошо, жертвуя тем самым частичкой производительности.
Не знаю на чем это основывалось.

Shaolinka
06.11.2020, 20:11
Спасибо, особо ничего нового не узнал, но сомнения в ряде вопросов развеялись. Заставил взглянуть меня на ситуацию с другой стороны. Раньше были какие-то стереотипы, якобы если использовать стек для создания объемного массива - это плохо, использовать большой sql-запрос - плохо, создавать PVar ради экономии памяти - хорошо, жертвуя тем самым частичкой производительности.
Не знаю на чем это основывалось.

Пвары экономят память? Тыкай (https://pro-pawn.ru/showthread.php?14044-%D0%9C%D0%B8%D1%84%D1%8B-%D0%BE-Pawn-%D1%81%D0%BA%D1%80%D0%B8%D0%BF%D1%82%D0%B8%D0%BD%D0%B3%D0%B5-9)

DeimoS
06.11.2020, 22:00
Спасибо, особо ничего нового не узнал, но сомнения в ряде вопросов развеялись. Заставил взглянуть меня на ситуацию с другой стороны. Раньше были какие-то стереотипы, якобы если использовать стек для создания объемного массива - это плохо, использовать большой sql-запрос - плохо, создавать PVar ради экономии памяти - хорошо, жертвуя тем самым частичкой производительности.
Не знаю на чем это основывалось.

Ну так pVar и правда хорошо использовать в случае, когда тебе нужно хранить какую-то информацию временно. Например, можно пароль игрока при входе загружать в pVar, а при авторизации удалять pVar. Или же, например, в ситуации, когда у тебя есть несколько диалогов, в которые игрок вводит те или иные данные (например, ID игрока и цену при продаже дома) - эти данные тоже выгоднее хранить в pVar, которые будут удалены после того, как данные больше не будут нужны.



Пвары экономят память? Тыкай (https://pro-pawn.ru/showthread.php?14044-%D0%9C%D0%B8%D1%84%D1%8B-%D0%BE-Pawn-%D1%81%D0%BA%D1%80%D0%B8%D0%BF%D1%82%D0%B8%D0%BD%D0%B3%D0%B5-9)

Статья полезная, ибо объясняет как работают pVar, но основное утверждение в ней неверное. Экономия памяти за счёт pVar заключается в том, что, если их использовать для хранения временных данных и всегда вовремя удалять, то один и тот же блок памяти будет использоваться для разных данных, для которых, в ином случае, пришлось бы выделять отдельные переменные. Тот же самый эффект можно было бы получить, создав какой-нибудь массив на MAX_PLAYERS ячеек и уже записывать данные в него, но там бы и читаемость пострадала, и пришлось бы постоянно контролировать, хранятся ли в массиве какие-либо данные, прежде чем записывать в него что-то новое.
В общем, да, за счёт pVar экономить память можно. Просто не так, как Daniel Cortez ожидает в своей статье. И даже скажу больше: не использовать их странно, с учётом того, что память под них выделяется в любом случае. А ситуаций, при которых относительная медлительность pVar будет не критична, достаточно много можно придумать. Хотя если просто не забывать, что можно создать локальную переменную, скопировать в неё значение pVar и уже использовать её в коде, то даже в каких-нибудь циклах и таймерах использование pVar не будет сильно тормозить код.

punkochel
06.11.2020, 23:07
Ну так pVar и правда хорошо использовать в случае, когда тебе нужно хранить какую-то информацию временно. Например, можно пароль игрока при входе загружать в pVar, а при авторизации удалять pVar. Или же, например, в ситуации, когда у тебя есть несколько диалогов, в которые игрок вводит те или иные данные (например, ID игрока и цену при продаже дома) - эти данные тоже выгоднее хранить в pVar, которые будут удалены после того, как данные больше не будут нужны.

Согласен, именно в таких случаях и использую их. Про пароль кстати хороший пример. У PVar, если я не ошибаюсь имеется линейная зависимость? То есть, чем больше действующих PVar создано для игрока, тем дольше будут осуществляться операции с ними.
Кстати, как можно узнать сколько памяти зарезервировано под них? И если этот лимит достигнут, то откуда будет выделяться память, стек?

Shaolinka
07.11.2020, 00:03
Согласен, именно в таких случаях и использую их. Про пароль кстати хороший пример. У PVar, если я не ошибаюсь имеется линейная зависимость? То есть, чем больше действующих PVar создано для игрока, тем дольше будут осуществляться операции с ними.
Кстати, как можно узнать сколько памяти зарезервировано под них? И если этот лимит достигнут, то откуда будет выделяться память, стек?

Когда ты пытаешься получить значение пвара, ну или установить, то цикл на 800 итераций(max colvo pvar'ov) так или иначе запуститься(Это касаемо линейной зависимости, если что-то не в том роде написал, sorry).
https://i.imgur.com/8THAha6.png

Pa4enka
07.11.2020, 01:43
Эмм, зачем кого-то отключать от сервера? Сервер сам всё это сделает, если рестарт будет производиться заложенными в это средствами - через команды "gmx" или "exit". Если же ты убиваешь серверный процесс на стороне хостинга, то твой код в любом случае не будет вызываться, так как сервер и не поймёт, что его собираются перезагружать.

При "exit" OnPlayerDisconnect может не успеть выполнится, исходя из моей скромного опыта. Но, т.к через "exit" принято производить автоматический и ручной рестарт, то перед этим безопаснее и надежнее выполнить сохранения всего имущества, данных игрока (дабы потом не сидеть и не разгребать тех.раздел), даже если вручную это все сохраняется при изменении значения. Поэтому, лично я лучше пожертвую памятью, производительностью, напрягу mysql, но все же сохраню весь сервер перед рестартом.

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

punkochel
07.11.2020, 13:59
При "exit" OnPlayerDisconnect может не успеть выполнится, исходя из моей скромного опыта. Но, т.к через "exit" принято производить автоматический и ручной рестарт, то перед этим безопаснее и надежнее выполнить сохранения всего имущества, данных игрока (дабы потом не сидеть и не разгребать тех.раздел), даже если вручную это все сохраняется при изменении значения. Поэтому, лично я лучше пожертвую памятью, производительностью, напрягу mysql, но все же сохраню весь сервер перед рестартом.
Перед тем как писать свой мод, я работал со множеством готовых и иногда обращал на это внимание, что функции запускающие рестарт сервера, сделаны именно таким способом (сохранение данных игрока в цикле было всегда), поэтому и думал что так будет правильнее.


Что касается вашей дискуссии о массивах. Скажу лишь, что на практике, все чуть-чуть иначе и не так хорошо. Я дискутировал с разными разработчиками на эту тему, и подловил себя на мысли, что все они используют глобальный массив, а не массу локальных. Их аргументация проста: "мне не нужны танцы с бубном, мне нужно быстро отформатировать строку". Вот так вот.
Именно по такому-же пониманию я и решил задаться таким вопросом) Как оказалось, это не очень хорошо. Теперь я понимаю почему это так, но все же не вижу ничего критичного. Может быть это было актуально, когда сервера работали на "калькуляторах". Подобное актуально только для pawn, т.к. в других ЯП (большинстве), стек работает быстрее и там есть что оптимизировать.


Когда ты пытаешься получить значение пвара, ну или установить, то цикл на 800 итераций(max colvo pvar'ov) так или иначе запуститься(Это касаемо линейной зависимости, если что-то не в том роде написал, sorry).
https://i.imgur.com/8THAha6.png
Откуда эта информация? Может быть цикл и запуститься, но получение информации из PVar будет происходить быстрее если он один, нежели их будет 800. Именно это я имею ввиду под "линейной зависимости".
UPD: Вот же блин вся инфа (https://wiki.pro-pawn.ru/wiki/SetPVarInt)

DeimoS
08.11.2020, 14:07
Кстати, как можно узнать сколько памяти зарезервировано под них? И если этот лимит достигнут, то откуда будет выделяться память, стек?

В той же статье всё описано. Для чисел память выделяется в любом случае. Для строк память выделяется при создании pVar и высвобождается при удалении pVar.


Может быть цикл и запуститься, но получение информации из PVar будет происходить быстрее если он один, нежели их будет 800. Именно это я имею ввиду под "линейной зависимости".

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


Именно по такому-же пониманию я и решил задаться таким вопросом) Как оказалось, это не очень хорошо. Теперь я понимаю почему это так, но все же не вижу ничего критичного. Может быть это было актуально, когда сервера работали на "калькуляторах". Подобное актуально только для pawn, т.к. в других ЯП (большинстве), стек работает быстрее и там есть что оптимизировать.

Ты ему про Фому, а он тебе про Ерёму :)
Собственно, как я и сказал: когда напишешь достаточно большой проект и достаточно долго будешь его поддерживать - понимание некоторых основ, о которых я выше писал, придёт к тебе само. Ну или не придёт и ты продолжишь страдать, тратя кучу времени на разбор своего кода и исправление ошибок, которых можно было бы избежать.


При "exit" OnPlayerDisconnect может не успеть выполнится, исходя из моей скромного опыта. Но, т.к через "exit" принято производить автоматический и ручной рестарт, то перед этим безопаснее и надежнее выполнить сохранения всего имущества, данных игрока (дабы потом не сидеть и не разгребать тех.раздел), даже если вручную это все сохраняется при изменении значения. Поэтому, лично я лучше пожертвую памятью, производительностью, напрягу mysql, но все же сохраню весь сервер перед рестартом.

То есть, ты сначала используешь команду, которая не предназначена для рестарта, а потом удивляешься тому, что у тебя что-то не сохраняется и придумываешь костыли, чтоб исправить это? Отличный план :good:


Поэтому, лично я лучше пожертвую памятью, производительностью, напрягу mysql, но все же сохраню весь сервер перед рестартом.

А можно просто сохранять все изменения по ходу работы сервера и не бояться ничего потерять.



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

Это те самые разработчики, работы которых мы можем в паблике видеть? Если да, то аргумент слабый. А даже если нет (в чём я сомневаюсь, с учётом того, каков процент говномодов и говноскриптов имеется в паблике относительно действительно качественных скриптов без багов) - тоже аргумент слабоватый. Если кто-то прыгает с крыши, то это не значит, что и тебе нужно уподобляться этому.
Впрочем, если кто-то хочет говнокодить и создавать себе проблемы из воздуха (как с тем же "exit") - ваше право.

P.S. И меня удивляет то, как у многих людей срабатывает триггер на дискуссии при виде каких-то длинных обсуждений. В каком месте ты тут дискуссию увидел - загадка. На протяжении всей темы автор задаёт вопросы, а я на них отвечаю. Всё.

punkochel
08.11.2020, 15:15
Ты ему про Фому, а он тебе про Ерёму :)
И в правду, не так понял пост))


Собственно, как я и сказал: когда напишешь достаточно большой проект и достаточно долго будешь его поддерживать - понимание некоторых основ, о которых я выше писал, придёт к тебе само. Ну или не придёт и ты продолжишь страдать, тратя кучу времени на разбор своего кода и исправление ошибок, которых можно было бы избежать.
Над модом работаю уже больше 10-ти месяцев, можно в принципе представить какой уже его объем, так вот, изначально я создал массив на 2048 ячеек для использования его только для: 1 -ая строка: Форматирование; 2 -ая строка: Отправление; Всё, больше никаких операций я с ним не произвожу.
Почему я его вообще создал: Создал я его из-за того, что весь мод построен из отдельных функций которые вызываются в разных файлах. (https://ziggi.org/arhitektura-pawn-proekta-dlya-sa-mp/)
Часто возникают ситуации когда необходимо вызывать функцию сохранения чего либо, например денег:

GivePlayerCash(playerid, money) {

new string[64];
format(string, sizeof(string), "QUERY");
mysql_tquery(ID, string);
return 1;
}

PayDay() {

foreach(new i:Player) {

GivePlayerCash(i, 100);
}
return 1;
}

Так вот, чтобы не создавать каждую итерацию массив string размером в 64 ячейки, я использую массив из дата сегмента на 2048. Вот почему это все.

Pa4enka
08.11.2020, 15:53
DeimoS, касаемо массивов, я попросту высказал альтернативную точку зрения, и заметь, не навязывал ничего ни тебе, ни автору. Просто дал небольшую интересную на мой взгляд информацию.

Остальной блок твоих сообщений улыбнул, спасибо)

DeimoS
08.11.2020, 16:35
Часто возникают ситуации когда необходимо вызывать функцию сохранения чего либо, например денег:

GivePlayerCash(playerid, money) {

new string[64];
format(string, sizeof(string), "QUERY");
mysql_tquery(ID, string);
return 1;
}

PayDay() {

foreach(new i:Player) {

GivePlayerCash(i, 100);
}
return 1;
}

Так вот, чтобы не создавать каждую итерацию массив string размером в 64 ячейки, я использую массив из дата сегмента на 2048. Вот почему это все.

Так тут проблема не в создании массива каждую итерацию, а, в целом, в том, что ты каждую итерацию отправляешь запрос, когда всё можно сделать проще и оптимизированнее (примеры давал выше).

Ну и да: даже если представить, что тех вариантов, о которых я говорю, не существует для Pawn и SAMP, то создавать глобальный массив, выделяя кучу памяти, только для того, чтоб не создавать локальные массивы в цикле, как в случае с твоей ситуацией - это в любом случае нерациональное решение. Твоя ситуация с циклом - это исключение из правил для твоей функции выдачи денег. Гораздо разумнее избавиться от вызова твоей кастомной функции и прописать форматирование запроса напрямую в цикл.

А то, что ты сделал сейчас - это как ситуация с "exit" у Pa4enka. То бишь, ты сам надумал себе условие в виде: "Я хочу обязательно всюду использовать GivePlayerCash", - и сам же после героически начал выдумывать нерациональные костыли для её решения. Как я писал в одном из предыдущих сообщений - не нужно натягивать сову на глобус, пытаясь создать одну функцию на все случаи жизни. Если встречаешься с ситуацией, когда реализация твоей функции начинает проигрывать по оптимизации, то нужно либо переписывать функцию, либо писать новую функцию конкретно для тех ситуаций, в которых старая функция проигрывает, либо просто прописывать код напрямую, если не получается оптимально сделать всё через функцию. Иначе рано или поздно ты начнёшь встречать сложные алгоритмы, в которых твои "универсальные" функции начнут делать кучу лишних действий, нерационально растрачивая память/время и т.п.


DeimoS, касаемо массивов, я попросту высказал альтернативную точку зрения, и заметь, не навязывал ничего ни тебе, ни автору. Просто дал небольшую интересную на мой взгляд информацию.

А я просто ответил на твою информацию и не более) Брать за пример какие-то сферические массы скриптеров - идея изначально плохая, ибо большая часть из них - это самоучки, которые изучали Pawn по видосам, снимающимся такими же самоучками, не особо понимающими о том, что они снимают. Отсюда и все эти максросы, по типу "SPD", "SCM" и прочая чепуха, которая тоже делается под аргументом: "мне не нужны танцы с бубном, мне нужно быстро напечатать код". И плевать они хотели на все последствия, ибо у них попросту ещё опыта недостаточно и они не встречались с этими последствиями лицом к лицу.


Остальной блок твоих сообщений улыбнул, спасибо)

Не очень понял что там может улыбнуть, но ок)

punkochel
08.11.2020, 18:27
А я просто ответил на твою информацию и не более) Брать за пример какие-то сферические массы скриптеров - идея изначально плохая, ибо большая часть из них - это самоучки, которые изучали Pawn по видосам, снимающимся такими же самоучками, не особо понимающими о том, что они снимают. Отсюда и все эти максросы, по типу "SPD", "SCM" и прочая чепуха, которая тоже делается под аргументом: "мне не нужны танцы с бубном, мне нужно быстро напечатать код". И плевать они хотели на все последствия, ибо у них попросту ещё опыта недостаточно и они не встречались с этими последствиями лицом к лицу.

А что в SCM то плохого? :black_eye:


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

Именно это я уже уяснил, ну и конечно-же пример не очень я привёл. Вскоре переделаю систему сохранения данных, на ту, что ты посоветовал.
Вообще, у меня был ужасный учитель (RLS copy samp-rp), там много чего реализовано так, и я почему-то принял это за должное, но потом спустя некоторое время, я начал переписывать функции, которые были хоть и частично скопированы оттуда, потом, спустя еще какое-то время, я понял что сделал полнейший шлак, и все уже не исправить. Итог: Начал писать мод с 0, не смотря ни в какие моды, все системы продумывал у себя в голове (мб велосипед изобретал, но тем не менее), а вот некоторые привычки - остались.

DeimoS
08.11.2020, 23:53
А что в SCM то плохого? :black_eye:

То, что:
1) Это сильно ухудшает читаемость, так как капсом обычно пишутся названия макросов?
2) Это ломает совместимость с любым адекватным кодом, который не использует эти макросы.

Почитай для чего люди в разных ЯП придумывают определённые спецификации по синтаксису. Зачем в том же Python есть PEP 8. Люди не просто так всё это придумывают, а потому что это как производительность конкретного человека повышает, так и производительность группы программистов, работающих над одним проектом. И нормальный редактор кода, заточенный под Python, тебя будет сильно по рукам бить, если ты там будешь вытворять то же самое, что обычно делают в Pawn всякие любители "экономить" время (а потом рожать из под своих рук моды с кучей багов, которые потом приходится месяцами вылавливать и исправлять).