[Tutorial] Por que nгo usar 256
#1

Por que vocк nгo deve usar strings grandes de 256 celulas

Originalmente postado por ****** (Topico)

Нndice
  • Нndice
  • Introduзгo
  • Resumo
  • Porque nгo devemos usar 256?
    • Й lento!
    • Vocк nгo precisa (1)
    • O maior tamanho de entrada й 128
    • O maior tamanho de saнda й 128
    • Vocк nгo precisa (2)
    • Uso de stack
  • Packed Strings
  • Quando eu posso usar 256?
    • SQL
    • Leitura de arquivos
  • Conclusгo

Introduзгo

As pessoas tкm alguns ESTRANHISSIMOS costumes sobre strings em PAWN, a maioria tem a ver com seus tamanhos. Muitas pessoas pensam que uma string nгo pode ser maior que 256, o porquк, eu nгo sei, parece como se fosse imposto um limite que nгo й necessбrio e de fato nгo existe limite algum. Outras pessoas pensam que todas as strings tem que ser de 256 por nenhuma razгo aparente, nгo importa o quгo longa serб sua string. Uma string em PAWN й exatamente o mesmo que qualquer outra array, as pessoas nгo tem nenhum problema com outras arrays, entгo porque strings?

*Nota-se que a algumas pessoas usam 255 no lugar de 256, mas estes sгo tгo escrotos quanto os outros.


Resumo

Uma string em PAWN й apenas uma array de caracteres, nenhuma diferenзa com outras arrays. Strings sгo terminadas em NULL, isso quer dizer que todas tem o caracter '\0' (null) no fim (cуdigo 0 na tabela ASCII, diferente do caractere '0' o qual й o cуdigo 48 da tabela ASCII). Se vocк tem a seguinte string:

pawn Код:
new
    str[3] = "hi";
Na realidade ele possui 3 cйlulas de tamanho - Uma para o 'h', uma para o 'i' e uma para o caractere NULL para sinalizar o fato de que a string terminou. Vocк tambйm pode escrever assim:

pawn Код:
new
    str[3] = {'h', 'i', '\0'};
Ou:

pawn Код:
new
    str[3] = {104, 105, 0};
104 e 105 sгo os cуdigos dos caracteres 'h' e 'i' respectivamente na tabela ASCII e 0 й o cуdigo para o caractere NULL.

Em PAWN todas as variбveis sгo setadas para 0 quando sгo declaradas, isso quer dizer que quando vocк faz:

pawn Код:
new
    str[256];
Na verdade vocк estб fazendo:

pawn Код:
new
    str[256];
for (new i = 0; i < 256; i++)
{
    str[i] = 0;
}
Й mais optimizado que isso mas mesmo assim acaba usando tempo.


Por que nгo devo usar 256?
  • Й lento
Como foi explicado no resumo, todas variбveis sгo setadas para 0 quando sгo inicializadas, quanto mais variбveis vocк tem, mais tempo elas irгo ocupar para setar para 0. Isto й optimizado no assembly, mas nгo interpretado no PAWN, mas й muito importante considerar que AMXModX escreveu um novo operador, muito similar ao 'new', que declara a variavel mas nгo seta a memoria para 0, apenas para contornar este problema.

  • Vocк nгo precisa disso (1)
Vou usar um post que eu vi recentemente (ele me motivou a escrever este post):

Quote:
Originally Posted by X_Cutterz
stock ReturnModeratorCmd(playerid,reqlvl)
{
new rmcmd[256];
format(rmcmd,sizeof(rmcmd),"Only moderators level %d+ can use this command!",reqlvl);
return SendClientMessage(playerid,Green,rmcmd);
}
Vamos assumir que tenha um mбximo de 10,000 leveis de admin para ser mais seguro (0 atй 9999), entгo o numero mais longo que vai ser inserido dentro da string tem comprimento de 4 caracteres.

Pelas minhas contas ali tem 47 caracteres na string, 2 deles sгo o "%d" que nгo aparece na contagem final da string, entгo por fim restam 45. Nos jб sabemos que o maior numero serб 4 (se vocк quer ter REALMENTE certeza de proteger o nнvel de admin, o maior numero possнvel em PAWN й 11 caracteres (-2147483647)), e nos sabemos que todas as strings requerem o NULL no fim, entгo o mais longo possнvel que esta string pode ser й:

47 - 2 + 4 + 1 = 50

50 cйlulas, entгo porque usar 256, й uma perda de 206 cйlulas (824 bytes - que chega perto de uma perda de 1 kilobyte de memoria)?

  • O mбximo de tamanho de entrada й 128
O chat box do SA:MP tem o tamanho mбximo de uma linha de 128 caracteres, se alguйm escrever alguma coisa vocк pode ter certeza que nunca serб maior q 128. Isto inclui textos e comandos, entгo porque usar um buffer duas vezes maior que o tamanho de saнda da string?

[anchor=o128]
  • O mбximo de tamanho de saнda й 128
O chat box do SA:MP tem o tamanho maximo de uma linha de 128 caracteres, se vocк quer mandar uma mensagem pra alguйm й impossнvel ser maior que 128, entгo por que usar um buffer maior que isso? Se sua mensagem envolve alguma forma de envio (ex: um comando "/eu") entгo й altamente recomendбvel o uso de uma variбvel de tamanho 128, pois nгo tem como determinar quгo longa serб o tamanho da string que ele escreverб, mas vocк sabe que nгo pode ser maior que 128.

  • Vocк nгo precisa disso (2)
pawn Код:
public OnPlayerCommandText(playerid, cmdtext[])
{
    new
        string[256],
        cmd[256];
    cmd = strtok(cmdtext, idx);
    if (strcmp(cmd, "/num", true) == 0)
    {
        format(string, sizeof (string), "Random number: %d", random(27));//traduзгo da mensagem "Numero randomico: %d"
        SendClientMessage(playerid, 0xFF0000AA, string);
    }
}
Ai tem uma pб de coisas erradas com este cуdigo, mas infelizmente й um exemplo muito comum de padrгo de cуdigos. Ignorando o fato de estar usando strtok, que й um assunto totalmente diferente, continua sendo um cуdigo 'mal feito'.

pawn Код:
new
        string[256],
        cmd[256];
Por que dois? Por que vocк precisa de uma string enorme para o comando e outra string enorme para dados que vocк usara depois sabendo qual comando foi digitado, portanto nгo precisamos das informaзхes da variбvel do comando, apenas reutilize a mesma senгo vocк estarб dobrando o tempo de processo.

pawn Код:
new
        string[256];
Por que 256? Como foi explicado acima, vocк sabe de FATO que nunca pode ser maior que 128. Se vocк reutilizar a variбvel para o envio da mensagem, embora vocк sabendo que a saнda nгo serб maior que 18 caracteres, isto nгo й totalmente ineficiente tendo em mente que vocк futuramente ira armazenar todo o tamanho se necessбrio.


Uma versгo mais eficiente poderia ser:

pawn Код:
public OnPlayerCommandText(playerid, cmdtext[])
{
    new
        string[128]; // cmdtext NUNCA sera maior que 128
    string = strtok(cmdtext, idx);
    if (strcmp(string, "/num", true) == 0)
    {
        format(string, sizeof (string), "Random number: %d", random(27)); // Sу precisamos de 18 caracteres aqui, mas serб necessбrio 128 futuramente entгo o tamanho da variбvel й justificado
        SendClientMessage(playerid, 0xFF0000AA, string);
    }
}
  • O uso de stack
Quando vocк chama uma funзгo, sua memoria esta alocada em uma coisa chamada stack (ou heap, mas eles usam o mesmo espaзo em memoria para simplificar vamos usar somente a stack), uma funзгo de memoria nгo pode ser estaticamente alocada, pois uma funзгo pode chamar a si mesma, o que requer toda a memoria dobrada. Esta бrea de memoria й chamada de memoria dinвmica.
Exemplo:

pawn Код:
new
    gVar = 2;
stock StackUse()
{
    new
        str[9];
    format(str, sizeof (str), "gVar = %d", gVar);
    SendClientMessageToAll(0xFF0000AA, str);
    if (gVar)
    {
        gVar--;
        StackUse();
    }
}
Esta funзгo serб chamada e alocarб 9 cйlulas na stack para usa-la, entгo chamara ela mesma e alocara outras 9 cйlulas, entгo chama a si mesma de novo e aloca de novo outras 9 cйlulas. Sendo assim ela foi chamada 3 vezes, entгo sгo 27 cйlulas alocadas na stack. Na terceira vez "gVar" й 0, entгo a funзгo nгo chama ela mesma e termina removendo 9 cйlulas da stack. Assim passa o controle para a instancia anterior da funзгo, que tambйm termina e remove mais 9 cйlulas, como feito na primeira instancia da funзгo, entгo agora tem 0 cйlulas na stack desta funзгo.

Agora imagine este cуdigo:

pawn Код:
new
    gVar = 2;
stock StackUse()
{
    new
        str[256];
    format(str, sizeof (str), "gVar = %d", gVar);
    SendClientMessageToAll(0xFF0000AA, str);
    if (gVar)
    {
        gVar--;
        StackUse();
    }
}
Exatamente o mesmo que o anterior, mas desta vez sendo chamada 3 vezes a si mesma aloca 768 cйlulas (3072 bytes, 3 kilobytes) na stack, comparando com as 27 cйlulas (108 bytes, 0.1 kilobytes, a reduзгo й maior que 2800%) do original.

Alguns de vocкs devem ter visto esta mensagem (ou uma parecida) quando compilam:

Код:
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
Ou:

Код:
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
Isto significa que o compilador detectou que vocк esta usando mais espaзo da stack do que esta disponнvel. Muitas informaзхes importantes sгo guardadas na stack, como quem chamou a funзгo atual, entгo PAWN sabe quem retornar pra quem. Se vocк usar muita memoria, por causa do jeito que a informaзгo й armazenada, vocк pode sobrescrever a stack, retornando um ponto randфmico no cуdigo quase certamente crashando. Por fim vocк terб um dado corrompido que serб sobrescrito por outro dado. Quando as pessoas recebem esta mensagem a dica padrгo й usar "#pragma dynamic", que й uma 'tapeada', nгo a soluзгo. - Eu acho muito estranho que algo do tamanho de YSI nгo geram este erro mas pequenos scripts de algumas pessoas geram, vocк nгo acha tambйm?

O primeiro erro que eu postei quer dizer que vocк esta tentando usar mais stack do que й disponнvel, mas por isso tem algumas funзхes chamando elas mesmas e o compilador nгo pode dizer exatamente quanto vocк precisa. Note que o compilador nгo pode determinar quantas vezes uma funзгo pode chamar ela mesma, mesmo no exemplo acima, o numero pode mudar em algum lugar do programa, e geralmente nгo й uma constante de qualquer jeito. O segundo erro й gerado pelo programa sem exatidгo.

Isso se aplica a TODAS arrays, mas strings sгo as maiores culpadas.


Packed Strings

Um aspecto de PAWN raramente usado em SA:MP (devido а algumas natives nгo suporta-la) й a 'Packed Strings'. Strings normais guardam um caractere por cйlula e uma cйlula possui o tamanho de 4 bytes (fazendo cйlulas de 256 da exatamente o tamanho de 1 kilobyte), packed strings guardam 4 caracteres por cйlulas:

Nгo packed:

pawn Код:
new
    string[12] = "Hello there"; // 12 celulas, 48 bytes
Packed:

pawn Код:
new
    string[12 char] = !"Hello there"; // 3 celulas, 12 bytes
Ambas strings estгo no manual do pawn, entгo eu nгo vou entrar em muitos detalhes, mas se vocк tem arrays longas de string para armazenar isso ajudaria para ler em packed strings e usa-las para armazenar. Se este mйtodo fosse usado no exemplo do ReturnModeratorCmd, combinado com um tamanho decente de string poderia reduzir o consumo da funзгo de 1 kilobyte(1024 bytes) para 50 bytes, esta й uma reduзгo maior que 2000%.


Quando eu posso usar 256?

Tendo dito tudo isto, ainda tem vezes que o uso de grandes arays podem ser uteis, mas elas devem ser usadas com moderaзгo somente quando necessбrias.

  • SQL
Leituras em SQL podem ser muito longas, eu tenho escrito algumas que eu precisei de quase 1024 celulas, porйm isto й somente uma exceзгo do ponto "Vocк nгo precisa disto", neste caso vocк PRECISA dela.

  • Lendo arquivos
Este й um exemplo de entrada sem limite. Se vocк lк uma linha de um arquivo vocк nгo tem como saber quгo longa й e acessando a memoria para caber tudo e diferentemente do texto digitado dentro do SA:MP, este pode ter qualquer tamanho. Neste caso um belo numero redondo como 256 pode cobrir quase todas as eventualidades.


Conclusгo

"Em caso de duvida - nгo entendeu algo". Sгo MUITO poucos os casos em que usa-se strings pesadas, pense sobre o que vocк faz para determinar o tamanho de qualquer array, baseado nisto, aplique o mesmo processo em strings.
Reply
#2

Belo post e uma bonita traduзгo(apenas alguns pontos as palavras ficam estranhas, mas em nenhum ponto chega a ser ilegнvel).
Alйm de que essa й uma informaзгo muito importante, que muitos sabem mas esquecem ou apenas nгo ligam para isso.
Reply
#3

Interessante, eu sempre sу usava 256 e nem sabia porque(acho quer por garantia).

Muito bem explicado.
Reply
#4

Muito bom, gostei
Reply
#5

new query[2048];
Reply
#6

Quote:
Originally Posted by Larceny
Посмотреть сообщение
Belo post e uma bonita traduзгo(apenas alguns pontos as palavras ficam estranhas, mas em nenhum ponto chega a ser ilegнvel).
Alйm de que essa й uma informaзгo muito importante, que muitos sabem mas esquecem ou apenas nгo ligam para isso.
Obrigado, eu tentei traduzir o mais legivel possivel, e eu tava sem net qdo traduzi ai sem tradutor sem nada tinha algumas palavras digamos...Cultas no sentido de PAWN ai ficou mei dificil de traduzir.

Quote:
Originally Posted by Jason_King
Посмотреть сообщение
Interessante, eu sempre sу usava 256 e nem sabia porque(acho quer por garantia).

Muito bem explicado.
Se й pra ser por garantia usa 128...
Reply
#7

Quote:
Originally Posted by Jason_King
Посмотреть сообщение
Interessante, eu sempre sу usava 256 e nem sabia porque(acho quer por garantia).

Muito bem explicado.
jason й bom contar as cells, para economizar memуria, 1 caractere = 1 cell


@topic:

Muito bom, +rep pra vocк.
Reply
#8

Otimo Tuto Cara
Reply
#9

Quote:
Originally Posted by BrunoBSF
Посмотреть сообщение
Otimo Tuto Cara
Agradeзa ao ******, eu sу traduzi...
Reply
#10

Legal a traduзгo *--*
Reply


Forum Jump:


Users browsing this thread: 2 Guest(s)