delphi strings (o que, quando e como) - otimizações.docx

8
Delphi strings (O que, quando e como) - Otimizações As versões mais recentes do Delphi oferecem pelo menos 4 tipos de strings. Cada uma delas com características e comportamentos próprios, tendo sua aplicabilidade destinada a situações diferentes. Esse artigo está dividido em duas partes. A primeira irá descrever esses quatro tipos de strings e suas características; a segunda etapa é composta por análises de situações comuns na programação e que constituem oportunidades para um otimizações. Breve Histórico ShortString Este é um tipo de string oriundo das primeiras versões do Delphi e herdado do Turbo Pascal, que possuia este como o único tipo de string. A menos que uma alocação manual seja feita, o tipo ShortString reside na stack e não na heap e, do ponto de vista de alocação de memória, tem o mesmo comportameto que os tipos básicos alocados estaticamente (integer, boolean, char, recors, enum, ...). Quando o tipo ShortString é utilizado o Delphi pré-aloca um bloco de 256 bytes e utiliza o primeiro byte (AStr[0]) para armazenar o tamanho 'utilizado'. var AStr: ShortString; Você também pode especificar um tamanho máximo para as strings, mas esse valor não pode passar 255. var s: string[50]; e: string[256]; // error PChar A limitação de 255 bytes para string representa um fator crítico para aplicações do mundo real. No Delphi 1 foi introduzido o tipo PChar que era um tipo semelhante ao "char *" da linguagem C. Entretanto, devido a segmentação da memória do Windows ser de 16-bits, o tipo PChar estava limitado a 65535 bytes (64 KB). Diferentemente do tipo ShortString, PChar não possui um "campo" destinado a armazenar o tamanho da string, mas possui um terminador "null".

Upload: luis-alfredo-g-caldas-neto

Post on 16-Sep-2015

12 views

Category:

Documents


0 download

TRANSCRIPT

Delphi strings (O que, quando e como) - OtimizaesAs verses mais recentes do Delphi oferecem pelo menos 4 tipos de strings. Cada uma delas com caractersticas ecomportamentos prprios, tendo sua aplicabilidadedestinada a situaes diferentes.Esse artigo est dividido em duas partes. A primeira ir descrever esses quatro tipos de strings e suas caractersticas; a segunda etapa composta por anlises de situaes comuns na programao e que constituem oportunidades para um otimizaes.

Breve Histrico

ShortStringEste um tipo de string oriundo das primeiras verses do Delphi e herdado do Turbo Pascal, que possuia este como o nico tipo de string.A menos que uma alocao manual seja feita, o tipoShortStringreside na stack e no na heap e, do ponto de vista de alocao de memria, tem o mesmo comportameto que os tipos bsicos alocados estaticamente (integer, boolean, char, recors, enum, ...).Quando o tipo ShortString utilizado o Delphi pr-aloca um bloco de 256 bytes e utiliza o primeiro byte (AStr[0]) para armazenar o tamanho 'utilizado'.var AStr: ShortString;

Voc tambm pode especificar um tamanho mximo para as strings, mas esse valor no pode passar 255.var s: string[50]; e: string[256];// error

PCharA limitao de 255 bytes para string representa um fator crtico para aplicaes do mundo real.No Delphi 1 foi introduzido o tipoPCharque era um tipo semelhante ao "char *" da linguagem C. Entretanto, devido a segmentao da memria do Windows ser de 16-bits, o tipo PChar estava limitado a 65535 bytes (64 KB).Diferentemente do tipo ShortString, PChar no possui um "campo" destinado a armazenar o tamanho da string, mas possui um terminador "null".

Long String (AnsiString)No Delphi 2 foi introduzido o tipoAnsiStringcom a finalidade de prover uma forma eficiente e rpida de trabalhar com strings grandes (32-bits). Agora era possvel manipular strings com at 2 GB.Comparando a estrutura do tipo AnsiString podemos dizer que ele uma "forma hbrida" dos tipos PChar e ShortString. Uma porque ele utiliza um terminador "null" para indicar o final da string (igual ao PChar) e, segundo, porque adota um campo para armazenar o tamanho da string e o primeiro caracter inicia na posio 1.var s: AnsiString;

Observe o mapa de memria da varivels, alm do campo "Lenght" esse tipo de sring mantm um outro campo, "RefCount". Resumidamente, esse campo incrementado sempre que a varivel referencida. Isso permite ao Delphi gerenciar o tempo de vida da string, liberando a memria quando a string no mais utilizada.

WideStringWideString foi o ltimo tipo de string adicionado famla Delphi, mais precisamente, introduzido na verso 6. No entanto, somente a partir da verso 2009 que se tornou o tipo padro de string.A finalidade do tipo WideString o suporte a caracteres Unicode e a diferena reside no fato de que cada caracter WideString representado por 2 bytes e no 1, como em AnsiString.var s: WideString;

O tipo String

No Delphi, o tipostringno nada mais do que um alias para ShortString, PChar, AnsiString ou WideString, dependendo da verso do Delphi que for utilizado.Por exemplo, no Delphi 7 o tipostring equivalente a AnsiString; j no Delphi 2009 e, mas recentemente, no Delphi 2010,string equivalente a WideString.O uso do tipo string deve ser aplicado com um pouco de cautela, principalmente se retro-compatibilidade ou portabilidade for uma das necessidades do cdigo fonte produzido.Tomamos por exemplo o lanamento do Delphi 2009, que trouxe aos usurios o desafio da migrao do cdigo fonte de manipulao de strings para o formato wide.

Semntica e comportamentoPara o correto uso das string necessrio entender sua semntica e seu comportamento.

a) Exemplo 1Lembre-se, o tipo ShortString pre-aloca 256 bytes e operaes de atribuio resultaro em cpia do contedo fonte.var s: ShortString; b: ShortString;begin ... s :='Teste'; b := s;// O contedo de 's' copiado para 'b' s[1] := 'X'// A varivel 'b' ainda contm 'Teste'b) Exemplo 2O tipo PChar est frequentemente associado a alocao dinnimica e atribuies somente copiam o ponteiro do destino, no o contedo.var s: PChar; b: PChar;begin ... s :='Teste'; b := s;// A varivel 'b' aponta para 's' s[0] := 'X'// Tanto 's' quanto 'b' contm 'Xeste'

c) Exemplo 3Tanto o tipo AnsiString quanto WideString so semelhantes ao tipo PChar quando uma atribuio direta feita, ou seja, somente copiado o ponteiro do destino. Alm disso, AnsiString e WideString possuem um campoRefCount(usado para saber quando a string no mais utilizada) que, na atribuio direta, incrementado..Observe o exemplo baixo.var s: AnsiString; b: AnsiString;begin ... s :='Teste'; b := s;// Ambas apontam para a mesma rea de memria e //o campo "RefCount' incrementado.

d) Exemplo 4Vamos tomar o mesmo exemplo anterior, mas com uma linha a mais de cdigo.var s: AnsiString; b: AnsiString;begin ... s :='Teste'; b := s;// b recebe o ponteiro de 's'e o RefCount incrementado. s[1] :='X';// Faz uma cpia de 'b' com o primeiro byte modificado e //decrementa o RefCount de 'b'.

Vamosdetalhar o trabalho realizado pelo Delphi em cada uma das linhas ilustrando as estruturas em memria.

Step 1) s :='Teste';

feito a alocao de memria para armazena a string. O campo "Lenght" setado para5(que o nmero de caracteres da string) e o campo "RefCount" setado para1.A varivel 's' passa a apontar para o incio da string.

Step 2) b := s;

Observe que a linha de cdigo acima simplesmente copiou o ponteiro da varivel 's' para a varivel 'b' e incrementou o campo 'RefCount', ou seja, abas as variveis esto apontando para a mesma rea de memria.

Step 3) s[1] :='X';

Aqui a alterao de um simples byte resultou e vrias instrues.Pelo fato do "RefCount" ser superior a 1, ou seja, por haver mais do que uma referncia, foirealizadaa alocao de uma nova string; copiado o contedo da string original; setado 1 para o "RefCount"; alterado o primeiro byte para 'X';atribudoo ponteiro da nova string para a varivel 's'.Por ltimo, o 'RefCount' da string original decrementado.

Otimizaes no uso de stringsUso de parmetros do tipo constA plavrava reservada "const" um modificador usado para definir algo como esttico, que no muda. E quando associadas com parmetros do tipo string, h um incremento no desempenho que pode ser perceptvel, dependendo da intensidade de uso.Vamos tomar como base uma funo simples.function GetStrSize(s: string): Integer;beginResult := Length(s);end;;

Aqui o parmetro "s" ir incondicionalmente incrementar o campo "RefCount" da string a qual ele est referenciando antes de iniciar a execuo da primeira linha de cdigo da funo.Pode parecer um tarefa simples e pouco honerosa para o sistema, mas totalmente desnecessria porque o parmetro somente utilizado para leitura e nunca para escrita.Uma cdigo fonte mais eficiente, nesse caso, facilmente obtido apenas especificando o parmetro como "constante".function GetStrSize2(consts: string): Integer;beginResult := Length(s);end;

A boa notcia que as otimizaes implementada nos compiladores mais recentes j detectam esse tipo de situao e geram um cdigo final mais eficiente.

Evitando retorno do tipo stringQuando voc for codificar uma funo na qual pretendido retornar uma string modificada da que passada por parmetro, aconselhado que seja feito atravs do prprio parmetro. A menos, claro, que voc necessite do valor original.function AddChar(const s: string): string;beginResult := s + '*';end;

O exemplo acima, como pode ser notado, apenas acrescenta um caracter no final da string passa por parmetro.J a funo abaixo faz a mesma coisa, mas retorna a nova string atravs do prprio parmetro.procedure AddChar2(var s: string);begins := s + '*';end;Tanto o modificador "const" da primeira funo quanto o modificador "var" da segunda funo fazem com que no seja necessrio o incremento do "RefCount". Entretanto a segunda funo mais eficiente porque no requer que a string do parmetro seja duplicada, porque a concatenao feita diretamente na string original.

Postergando condicionais de comparao de stringUmasituao bem corriqueira no dia-a-dia, principalmente quando se est trabalhando com algum tipo de parser, codificar testes condicionais com mais do que uma validao onde h comparaes string.varlFailed: Boolean;lText: string;begin...if (lText ='') and (not lFailed) then...end;

No exemplo acima o primeiro teste do "if" faz uma comparao entre string e o segundo uma comparao que envolve uma varivel booleana.Sabendo que uma comparao entre string uma tarefa "onerosa" para o processador, o mais sensato alterarmos a ordem das condies de forma que os testes mais simples sejam atendidos primeiros. Isso evita, nesse nosso exemplo, uma comparao de string desnecessria quando a varivellFailedforFalse.Esse um tipo de Boas Prticas de Programao pode ser extentido para casos de forma geral que exigam um processamento extra.

Redundncia de chamadasPor mais otimizado que a biblioteca do Delphi esteja, a chamada a uma funo de manipulao de string sempre impem alguma penalidade se comparado com operaes simples, e evitar chamadas desnecessrias ou redundantes certamente um cuidado bem vindo.

O cdigo abaixo um pequeno trecho retirado de uma mtodo de validao de email. Podemos notar o uso repetido e redundante da funo "Pos".function IsValidEmail(const EMail: string): Boolean;begin ...if (Pos('@', EMail) 0) and (Pos('.', EMail) 0) thenbegin if (Pos('@', EMail) = 1) or (Pos('@', EMail) = Length(EMail)) or (Pos('.', EMail) = 1) or (Pos('.', EMail) = Length(EMail)) or (Pos(' ', EMail) = 0) then Result := False...

Esse um exemplo simples que utiliza funes especficas para tratamento de strings, mas o problema no exclusivo eestende-se para qualquer outro tipo de redundncia.function IsValidEmail(const EMail: string): Boolean;varlPos_Dot: Integer;lPos_Arroba: Integer;begin...lPos_Dot := Pos('.', EMail);lPos_Arroba := Pos('@', EMail);

if (lPos_Arroba 0) and (lPos_Dot 0) thenbegin if (lPos_Arroba = 1) or (lPos_Arroba = Length(EMail)) or (lPos_Dot = 1) or (lPos_Dot = Length(EMail)) or (Pos(' ', EMail) = 0) then Result := False...

A simples adio de duas variveis locais evitou quatro chamadas desnecessrias funo "Pos".

Verificando se uma string est vaziaExistem muitas maneiras de conferir se uma string ou no vazia, mas voc sabe qual delas a mais eficiente?Abaixo eu apresento trs das formas maisfreqentementeempregadas.varlMyStr: string;beginlMyStr := Caption;if lMyStr = '' then lMyStr :='Maneira mais eficiente';if Length(lMyStr) = 0 then lMyStr :='Maneira menos eficiente';if lMyStr[1] = '' then lMyStr :='Forma desaconselhada';

Para entender melhor a verdadeira razo do primeiroifconter a forma mais adequada de verificar se um string est ou no vazia, vou postar o cdigo assemble gerado pelo compilador. Mesmo voc no estando familiarizado com a linguagem assembly, tenho certeza que ser fcil o entendimento.

O cdigo gerado pelo compilador apresentado logo abaixo da linha do cdigo fonte respectiva e cada um dos ifacima est destacado em uma cor diferente.fuStrTest.pas.127:if lMyStr = '' then07992023 837DFC00 cmp dword ptr [ebp-$04],$0007992027 750D jnz $07992036fuStrTest.pas.128: lMyStr := 'Maneira mais eficiente';07992029 8D45FC lea eax,[ebp-$04]0799202C BADC209907 mov edx,$079920dc07992031 E842F0FFFF call $07991078fuStrTest.pas.129:if Length(lMyStr) = 0 then07992036 8D45FC lea eax,[ebp-$04]07992039 E822F0FFFF call $079910600799203E E83DF0FFFF call $0799108007992043 85C0 test eax,eax07992045 750D jnz $07992054fuStrTest.pas.130: lMyStr := 'Maneira menos eficiente';07992047 8D45FC lea eax,[ebp-$04]0799204A BA18219907 mov edx,$079921180799204F E824F0FFFF call $07991078fuStrTest.pas.131:if lMyStr[1] = '' then07992054 8B45FC mov eax,[ebp-$04]07992057 66833800 cmp word ptr [eax],$000799205B 750D jnz $0799206afuStrTest.pas.132: lMyStr := 'Forma desaconselhada';0799205D 8D45FC lea eax,[ebp-$04]07992060 BA54219907 mov edx,$0799215407992065 E80EF0FFFF call $07991078

Somente observando o nmero de instrues necessrias para cada uma das trs situaes j suficiente para comprovar que o primeiroif o mais eficiente. Entretanto o segundoif, que utiliza a funoLength, merece alguns comentrios.Observe que h duas instruescall(call $07991060 ecall $07991080) que so, na verdade, chamadas para as funesEnsureUnicodeString eUStrLen, respectivamente. Essas funes so compostas por diversas instrues e, do ponto de vista de micro otimizaes, impem uma penalidade de performance bastante grande.Mesmo no tento realizados testes estatsticos mais precisos, posso afirmar que um simplesif lMyStr = '' then incontveis vezes mais rpido que o uso da funoLength.Novamente ressaltando que isso do ponto de vista de micro otimizaes e para a maioria dos desenvolvedores o ganho de desempenho seria imperceptvel.