Re: [ TuT ] Používání a řetězců a jejich optimalizace

Sekcia určená pre programovací jazyk pawn.

[ TuT ] Používání a řetězců a jejich optimalizace

Poslaťod Iron[CZ] » Sob 16. Júl 2011 12:38:38

Používání řetězců a jejich optimalizace
V tomto návodu se budeme zabývat optimalizací řetězců. Mimo jiné se dozvíte jak s řetězci pracovat. Jdeme na to.

Základy a výpočet velikosti řetězce
Všichni snad umíme vytvořit jednoduchý řetězec a nastavit mu hodnotu.
Kód: Vybrať všetko
new str[5] = "ahoj";

Tento řetězec má ale 5 znaků, protože každému prázdnému poli v pawnu je základně přiřazena hodnota 0. Pawn to pojme jako:
Kód: Vybrať všetko
new str[5] = "ahoj\0";

Pokud vytváříte řetězec bez přiřazení hodnoty, pawn provede toto.
Kód: Vybrať všetko
new str[256];
for (new i;i<256;i++)
{
   str[i] = 0;
}

Zde se dostáváme k jádru našeho problému. Vidíte že v případě 256ti znakového řetězce dochází k provedení cyklu for 256krát. Pokud použijeme řetězec s velikostí 128 znaků, dojde k ušetření 50% času při přiřazování hodnot. Čím více řetězců, tím více ušetříme času.
Dále se dostáváme k velikosti řetězce. Každý znak má 4 bajty.
Kód: Vybrať všetko
new str[256];
256 * 4 = 1024

Rozdíl:
Kód: Vybrať všetko
new str[128];
128 * 4 = 512

Pouze v tomto řetězci dojde k ušetření 512 bajtů paměti. Pokud toto uděláme například v 200 řetězcích, kde místo 256 dáme 128, dojde již k ušetření paměti o 102,4KB. To už je poměrně velká jednotka. Používáním větších řetězců může dojít k vážným problémům, kdy vám kompilátor vyhodí následující hlášky:
Kód: Vybrať všetko
Header size:      200 bytes
Code size:       588 bytes
Data size:       512 bytes
Stack/heap size:   16384 bytes; estimated max. usage=10250 cells (41000 bytes)
Total requirements:  17684 bytes

Kód: Vybrať všetko
Header size:      216 bytes
Code size:       776 bytes
Data size:       528 bytes
Stack/heap size:   16384 bytes; estimated max. usage: unknown, due to recursion
Total requirements:  17904 bytes

To znamená, že jste využili více paměti, než je možno. V důsledku toho může dojít k padání serveru, nefunkčnosti některých publiků a dalším vážným problémům. Proto je nutné zmenšovat řetězce.
Absolutně zarážející je tento příklad pawnerů:
Kód: Vybrať všetko
dcmd_cmd(playerid, params[])
{
   #pragma unused params
   new str[256];
   new name[256];
   GetPlayerName(playerid, name, sizeof(name));
   format (string, sizeof(string), "hráč %s se zaměstnal jako popelář", name);
   SendClientMessageToAll(COLOR_CERVENA, string);
   return 1;
}

Takže zde řetězce zabírají:
Kód: Vybrať všetko
256 * 4 = 1024 * 2 = 2048 bajtů.

To je docela dost. Při naší optimalizaci:
Kód: Vybrať všetko
128 * 4 = 512
24 * 4 = 96 + 512 = 608

Dojde k ušetření paměti o 1,44KB. To je opravdu hodně.

Důvody pro nepoužívání řetězců o velikosti 256 znaků
Nepotřebujete to vůbec
Maximální délka vstupu a výstupu na chat je 128 znaků. Pokud ale chcete ušetřit ještě více místa, můžete jednotlivé řetězce počítat.
Kód: Vybrať všetko
new string[14];
format (string, sizeof(string), "delka stringu");

Jednoduše si spočítáte kolik má znaků (včetně mezer) a připočítáte jedna.
Další věcí jsou jména hráčů. Maximální velikost jména je 24 znaků, proč používat delší? Pro jméno je nejlepší dát vždy 24, protože nevíte jestli hráč má delší jméno. Pokud dáte třeba:
Kód: Vybrať všetko
new name[15];

A hráč má 20 znakové jméno, dojde k nezobrazení některých znaků.

Zbytečné
Je zbytečné vytvářet velké řetězce, tak jako je zbytečné vytvářet více řetězců, jak ukáže následující příklad.
Kód: Vybrať všetko
dcmd_ahoj(playerid, params[])
{
   #pragma unused params
   new string[256];
   new string2[256];
   format (string, sizeof(string), "ahoj zkousime string %d", promena(nejaka));
   format (string, sizeof(string), "string je v poradku %d", promena(nejaka));
   SendClientMessageToAll(COLOR_CERVENA, string);
   SendClientMessageToAll(COLOR_CERVENA, string2);
   return 1;
}

K čemu vytvářet 2 řetězce o takové velikosti, když to můžete udělat takto:
Kód: Vybrať všetko
dcmd_ahoj(playerid, params[])
{
   #pragma unused params
   new string[128];
   format (string, sizeof(string), "ahoj zkousime string %d", promena(nejaka));
   SendClientMessageToAll(COLOR_CERVENA, string);
    format (string, sizeof(string), "string je v poradku %d", promena(nejaka));
   SendClientMessageToAll(COLOR_CERVENA, string);
   return 1;
}

Zde dojde k ušetření:
Kód: Vybrať všetko
256 * 4 = 1024 * 2 = 2048

Kód: Vybrať všetko
128 * 4 = 512
1024 - 512 = 512

O 512 bajtů místa. Také dost, že?

Kdy je potřeba použít 256ti znakové řetězce nebo i vyšší
Čtení ze souborů
Při čtení ze souboru stylem řádek po řádku je někdy zapotřebí velký řetězec. Nelze odhadnout velikosti řetězce, ale vždycky je lepší více než méně.
SQL
SQL příkazy jsou velmi dlouhé. Někdy je i zapotřebí 1024 znaků.

Práce s řetězci
Dostali jsme se do druhé části našeho návodu, kterou je práce s řetězci. Popíšu vám tady základní funkce pro práci a úpravu řetězců.

Strcmp
Velmi známá funkce, která slouží pro porovnání dvou řetězců. Struktura:
Kód: Vybrať všetko
strcmp(první string, druhý string, ignorace velikosti, délka(nepovinné))

Pokud je ignorace velikosti nastavena na hodnotu true je AhoJ to samé jako Ahoj. V případě false je to jiný řetězec.
Příklad:
Kód: Vybrať všetko
if (strcmp(string, string2, true) == 0)

Funkce vrací hodnoty:
-1 - když je string před stringem2
1 - když je string po stringu2
0 - když jsou stejné

Strfind
Tato funkce hledá část řetězce v jiném. Struktura:
Kód: Vybrať všetko
strfind(kde hledáme, co hledáme, ignorace velikosti, pozice);

Příklad:
Kód: Vybrať všetko
if (strfind("Ahoj jak se mas?", "Ahoj", true) != -1);

-1 znamená že se řetězec Ahoj může nacházet kdekoli v řetězci Ahoj jak se mas a je tak prohledáván celý řetězec.

Strdel
Tato funkce vymaže část řetězce. Struktura:
Kód: Vybrať všetko
strdel(string, začátek, konec);

Příklad:
Kód: Vybrať všetko
new string[20] = "Ahoj jak se mas?";
strdel(string,0,5);

Takže zůstane pouze 'jak se mas?'

Strins
Tato funkce vloží část řetězce do jiného. Struktura:
Kód: Vybrať všetko
strins(string, co vložit, pozice, délka(nepovinné));

Příklad:
Kód: Vybrať všetko
strins("ja jsem ", "Iron", 9);

Výsledkem bude věta 'ja jsem Iron'

Strmid
Tato funkce vytáhne část řetězce. Struktura:
Kód: Vybrať všetko
strins(kam ukládat, odkud vytahovat, začátek, konec, délka(nepovinné));

Příklad:
Kód: Vybrať všetko
strmid(string, "ahoj jak se mas?", 0, 5);

Takže se vytáhne 'ahoj'.

Strlen
Také známá funkce, která zjišťuje délku řetězce. Struktura:
Kód: Vybrať všetko
strlen(string);

Příklad:
Kód: Vybrať všetko
new len = strlen("ahoj");

Proměnná len bude nabývat hodnotou 5-

Strpack
Užitečná funkce, která zjistí počet znaků řetězce a zmenší velikost. Struktura:
Kód: Vybrať všetko
strpack(kam ukládá, originální řetězec, délka(nepovinné));

Příklad:
Kód: Vybrať všetko
strpack(string, "ahoj");

String bude dlouhý 5 znaků.

Strval
Velmi známá funkce, která převede řetězec na číslo. Struktura:
Kód: Vybrať všetko
strval(string);

Příklad:
Kód: Vybrať všetko
new cislo = strval(string);


Strcat
Velice užitečná funkce, která spojí dva řetězce. Struktura:
Kód: Vybrať všetko
strcat(základ, co dosadit, délka(nepovinné));

Příklad:
Kód: Vybrať všetko
new string[40];
strcat(string, "Ahoj ");
strcat(string, "ja jsem iron");

Výsledek bude 'Ahoj ja jsem iron'.

Floatstr
Tato funkce převede řetězec na desetinné číslo. Struktura:
Kód: Vybrať všetko
floatstr(string);

Příklad:
Kód: Vybrať všetko
new string[5] = "14.58";
new Float:B = floatstr(string);

Hodnota B bude nabývat 14.58.

Další užitečné funkce pro práci s řetězci
Strtok
Tato funkce rozdělí řetězec v místě, kde najde určitý znak. Struktura:
Kód: Vybrať všetko
stock strtok(const string[], &index,seperator=' ')
{
   new length = strlen(string);
   new offset = index;
   new result[MAX_STRING];
   while ((index < length) && (string[index] != seperator) && ((index - offset) < (sizeof(result) - 1)))
   {
      result[index - offset] = string[index];
      index++;
   }
 
   result[index - offset] = EOS;
   if ((index < length) && (string[index] == seperator))
   {
      index++;
   }
   return result;
}

Kód: Vybrať všetko
strtok(co rozdělit, pozice(nepovinné), znak);

Příklad:
Kód: Vybrať všetko
if (strcmp(cmdtext,"/call", true) == 0)
{
   new tmp[128]; // hodnota pro strtok
   new string[128];
   idx; // index
   tmp = strtok(cmdtext, idx); //rozdeleni retezce
   format (string, sizeof(string), "hrac %s vola %s", PlayerName(playerid), tmp) // playerName jsem pouzil jako stock
   SendClientMessageToAll(COLOR_CERVENA, string);
   return 1;
}


Split
Pracuje podobně jako strtok, ale dělí celí řetězec. Struktura:
Kód: Vybrať všetko
stock split(const strsrc[], strdest[][], delimiter)
{
    new i, li;
    new aNum;
    new len;
    while(i <= strlen(strsrc))
    {
        if(strsrc[i] == delimiter || i == strlen(strsrc))
        {
            len = strmid(strdest[aNum], strsrc, li, i, 128);
            strdest[aNum][len] = 0;
            li = i+1;
            aNum++;
        }
        i++;
    }
    return 1;
}

Kód: Vybrať všetko
split(string, string kam ukládat, znak);

Příklad:
Kód: Vybrať všetko
new tmp[2][128];
split("ahoj vsichni",tmp, ' ');
print(tmp[0]);

Vypíše ahoj.

Beat
Úplně to stejné jako strtok. Pravděpodobně se s touto funkcí ani nesetkáte. Struktura:
Kód: Vybrať všetko
stock beat(string[], arg, symbol, begin = 0) {
new output[32],outLen;
while (string[begin] && string[begin] == symbol) begin++;
while (string[begin]){
if (string[begin] == symbol) {
arg--;
while (string[++begin] == symbol) {}}
if (!arg){
new ch;
while ((ch = string[begin++]) && ch != symbol && outLen < (sizeof (output) - 1)) {
output[outLen++] = ch; }
output[outLen] = EOS;
return output; }
begin++; }
return output;
}

Kód: Vybrať všetko
beat(string, argument, znak);


Chrfind
Opět to samé, nicméně doporučuji tuto funkci u DCMD příkazů pro vytvoření příkazu se 2 parametry. Struktura:
Kód: Vybrať všetko
stock chrfind(n,h[],s=0)
{
   new l=strlen(h);
   while(s<l)
   {
      if(h[s]==n)
      return s;s++;
   }
   return -1;
}

Příklad:
Kód: Vybrať všetko
dcmd_kick(playerid, params[])
{
   new vyhozeny = strval(params);
   new id;
   if (!params[0] || !(id = chrfind(' ', params) + 1) || !params[id]) return SendClientMessage(playerid, COLOR_SYSTEM, "/kick id duvod");
   Kick(vyhozeny);
   return 1;
}
Obrázok užívateľa
Iron[CZ]
Nováčik
Nováčik
 
Príspevky: 125
Registrovaný: Pon 19. Júl 2010 20:56:53
Bydlisko: Týniště nad Orlicí

Re: [ TuT ] Používání a řetězců a jejich optimalizace

Poslaťod SK_martin_SA » Sob 16. Júl 2011 17:00:13

Pekný Tutorial! Dokonca si mi pripomenul, že mám v móde

Kód: Vybrať všetko
public OnPlayerConnect(playerid)
{
        new prip[500]; //xD
        format(prip,250,"Hrac %s sa pripojil na server",PlayerName(playerid));
        SCMTA(COLOR_RED,prip);
        return 1;
}
SK_martin_SA
 

Re: [ TuT ] Používání a řetězců a jejich optimalizace

Poslaťod Angel » Sob 16. Júl 2011 17:03:50

lol martin, to ako ťa mohol napadnúť taký string ?  ;D  ???
btw: pekne spravené, prehľadné, aspoň to nemusím prekladať z wiki :D

//fú, ak by ich tam takých veľkých stringov bolo viac, pekne by to lagovalo server :D
Naposledy upravil Angelus dňa Sob 16. Júl 2011 18:06:12, celkovo upravené 1
Angel
 

Re: [ TuT ] Používání a řetězců a jejich optimalizace

Poslaťod SK_martin_SA » Sob 16. Júl 2011 18:05:00

To som si okopčil z môjho prvého módu (90% original copy mode ;D )
SK_martin_SA
 

Re: [ TuT ] Používání a řetězců a jejich optimalizace

Poslaťod Rytmus » Str 20. Júl 2011 12:54:14

Zvyšujem karmu o +18. Naozaj prekrasny TuT :)
Víťaz Netopeer Awards v kategórii - Administrátor roka 2013.

Obrázok
Obrázok
Obrázok užívateľa
Rytmus
NTP Member
NTP Member
 
Príspevky: 2115
Registrovaný: Sob 16. Jan 2010 23:41:12
Bydlisko: Myjava

Re: [ TuT ] Používání a řetězců a jejich optimalizace

Poslaťod Iron[CZ] » Str 20. Júl 2011 14:07:08

Díky, zkusím napsat ještě něco takového.
Obrázok užívateľa
Iron[CZ]
Nováčik
Nováčik
 
Príspevky: 125
Registrovaný: Pon 19. Júl 2010 20:56:53
Bydlisko: Týniště nad Orlicí

Re: [ TuT ] Používání a řetězců a jejich optimalizace

Poslaťod bzuco » Pon 01. Aug 2011 7:39:19

Ja osobne par kb neriesim, kedze mame 2GB ramky na servery, v prvom rade treba optimalizovat suborove operacie, ktore zaberaju najviac casu a nie riesit nejaku velkost pola.

Kód: Vybrať všetko
new str[256];
for (new i;i<256;i++)
{
   str[i] = 0;
}

V pwn nieje potrebne explicitne inicializovat pole.

Kód: Vybrať všetko
Header size:      216 bytes
Code size:       776 bytes
Data size:       528 bytes
Stack/heap size:   16384 bytes; estimated max. usage: unknown, due to recursion
Total requirements:  17904 bytes

Toto nieje ziadny problem modu...

Nezabudnite, ze ked je pole prilis male, a pristupujete k prvku mimo rozsahu pola, tak server padne. Preto je vhodnejsie mat rezervu.
Obrázok užívateľa
bzuco
VIP Member
VIP Member
 
Príspevky: 1954
Registrovaný: Štv 25. Dec 2008 21:25:06

Re: [ TuT ] Používání a řetězců a jejich optimalizace

Poslaťod Iron[CZ] » Pon 01. Aug 2011 9:22:29

bzuco píše:
Kód: Vybrať všetko
new str[256];
for (new i;i<256;i++)
{
   str[i] = 0;
}



Taky jsem nepsal že tohle provádíme my, ale píšu že když my dáme:
Kód: Vybrať všetko
new string[256];

Co s tím udělá pawn.
A header size vyskočí, když překročíte stanovený počet znaků. V důsledku toho mi nefungoval třeba public OnPlayerDeath,
Naposledy upravil Iron[CZ] dňa Pon 01. Aug 2011 10:03:58, celkovo upravené 1
Obrázok užívateľa
Iron[CZ]
Nováčik
Nováčik
 
Príspevky: 125
Registrovaný: Pon 19. Júl 2010 20:56:53
Bydlisko: Týniště nad Orlicí

Re: [ TuT ] Používání a řetězců a jejich optimalizace

Poslaťod SK_martin_SA » Pon 01. Aug 2011 9:26:32

bzuco má pravdu. Pred dvoma dňami som riešil ten problém so súbormy. Skoro každá vec sa ukladala do iného súbora v inom priečinku...no hrôza  ;D
SK_martin_SA
 


Späť na Pawn

Kto je on-line

Užívatelia prezerajúci fórum: Žiadny registrovaný užívateľ nie je prítomný a 1 hosť

cron