Resposta ao Desafio da Semana #2 [Performance – Visual Basic 6]



 


Por: Roberto A. Farah


 


CENÁRIO


 


Eis o link para o Desafio: http://blogs.technet.com/latam/archive/2006/04/21/426009.aspx


O problema causando o sintoma de baixa performance é colocado abaixo junto com uma solução.


 


PROBLEMA


 


A concatenação de strings é efetuada com o operador ‘+’ que, assim como ‘&’ alocações e desalocações internamente, que, para código nativo é algo custoso. Note que usar ‘+’ e ‘&’ não são sinônimos conforme explicado abaixo: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vbcon98/html/vbstartpage.asp


http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vbcon98/html/vbstartpage.asp


http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vbcon98/html/vbconadvancedvarianttopics.asp


 


Entretanto, usar qualquer desses operadores causa o mesmo impacto. Veja isso:


 


Option Explicit


 


Private Sub Original(ByVal strText As String)


    Dim strOutput As String


    Dim i As Integer


   


    strOutput = vbNullString


   


    ‘Usando operador +


    For i = 1 To 10


        strOutput = strOutput + strText


    Next


   


    ‘Usando operador &


    strOutput = vbNullString


    For i = 1 To 10


        strOutput = strOutput & strText


    Next


End Sub   


 


 


Agora observe o código disassemblado:


 


 


Ok, eis o endereço das variáveis locais no início da rotina. Como uma curiosiade note que o VB usa Unicode internamente (wchar_t):


 


local  0012f49c @ebp+0x08 void * Me = 0x0014a550


local  0012f4a0 @ebp+0x0c wchar_t * strText = 0x001518a4 “String para ser concatenada!!!”


local  0012f478 @ebp-0x1c wchar_t * strOutput = 0x00000000 “”


local  0012f47c @ebp-0x18 wchar_t * strText = 0x00153b7c “String para ser concatenada!!!”


local  0012f480 @ebp-0x14 short i = -23216


 


Rotina disassemblada:


 


Project1!Form1::Original [C:\Development\My Tools\BLOG Articles\Article #6\Form1.frm @ 92]:


00402220 55              push    ebp


00402221 8bec            mov     ebp,esp


00402223 83ec08          sub     esp,8


00402226 6816114000      push    offset Project1!__vbaExceptHandler (00401116)


0040222b 64a100000000    mov     eax,dword ptr fs:[00000000h]


00402231 50              push    eax


00402232 64892500000000  mov     dword ptr fs:[0],esp


00402239 83ec20          sub     esp,20h


0040223c 53              push    ebx


0040223d 56              push    esi


0040223e 57              push    edi


0040223f 8965f8          mov     dword ptr [ebp-8],esp


00402242 c745fcf0104000  mov     dword ptr [ebp-4],offset Project1!_imp____vbaFreeObj+0x4c (004010f0)


00402249 8b550c          mov     edx,dword ptr [ebp+0Ch]


0040224c 8b3d78104000    mov     edi,dword ptr [Project1!_imp___vbaStrCopy (00401078)]


00402252 33c0            xor     eax,eax


00402254 8d4de8          lea     ecx,[ebp-18h]


00402257 8945e8          mov     dword ptr [ebp-18h],eax


0040225a 8945e4          mov     dword ptr [ebp-1Ch],eax


0040225d ffd7            call    edi


 


CÓDIGO USANDO OPERADOR +


 


0040225f 33d2            xor     edx,edx


00402261 8d4de4          lea     ecx,[ebp-1Ch]    <– Carrega strOutput.


00402264 ffd7            call    edi              <– Chama __vbaStrCopy do VBRuntime.


00402266 bb01000000      mov     ebx,1


0040226b 8bf3            mov     esi,ebx


0040226d b80a000000      mov     eax,0Ah          <– Prepara o contador do loop.


00402272 663bf0          cmp     si,ax


00402275 7f25            jg      Project1!Form1::Original+0x7c (0040229c)


00402277 8b45e4          mov     eax,dword ptr [ebp-1Ch]    <– strOutput.


0040227a 8b4de8          mov     ecx,dword ptr [ebp-18h]    <– strText


0040227d 50              push    eax                        <– Apos colocar em registradores salva na pilha,


0040227e 51              push    ecx                        <– pois sao os parametros de _vbaStrCat.


0040227f ff1518104000    call    dword ptr [Project1!_imp___vbaStrCat (00401018)]


00402285 8bd0            mov     edx,eax


00402287 8d4de4          lea     ecx,[ebp-1Ch]


0040228a ff158c104000    call    dword ptr [Project1!_imp____vbaStrMove (0040108c)]  <– Move os ponteiros dos caracteres fonte e destino.


00402290 668bd3          mov     dx,bx


00402293 6603d6          add     dx,si


00402296 706c            jo      Project1!Form1::Original+0xe4 (00402304)


00402298 8bf2            mov     esi,edx


0040229a ebd1            jmp     Project1!Form1::Original+0x4d (0040226d)


 


CÓDIGO USANDO OPERADOR &


 


0040229c 33d2            xor     edx,edx                                 <– Aqui inicia a parte usando &.


0040229e 8d4de4          lea     ecx,[ebp-1Ch]


004022a1 ffd7            call    edi                                     <– __vbaStrCopy.


004022a3 bf01000000      mov     edi,1


004022a8 bb0a000000      mov     ebx,0Ah                                 <– Exatamente o mesmo padrao se repete.


004022ad 8bf7            mov     esi,edi


004022af 663bf3          cmp     si,bx


004022b2 7f25            jg      Project1!Form1::Original+0xb9 (004022d9)


004022b4 8b45e4          mov     eax,dword ptr [ebp-1Ch]


004022b7 8b4de8          mov     ecx,dword ptr [ebp-18h]


004022ba 50              push    eax


004022bb 51              push    ecx


004022bc ff1518104000    call    dword ptr [Project1!_imp___vbaStrCat (00401018)]


004022c2 8bd0            mov     edx,eax


004022c4 8d4de4          lea     ecx,[ebp-1Ch]


004022c7 ff158c104000    call    dword ptr [Project1!_imp____vbaStrMove (0040108c)]


004022cd 668bd7          mov     dx,di


004022d0 6603d6          add     dx,si


004022d3 702f            jo      Project1!Form1::Original+0xe4 (00402304)


004022d5 8bf2            mov     esi,edx


004022d7 ebd6            jmp     Project1!Form1::Original+0x8f (004022af)


 


004022d9 68ef224000      push    offset Project1!Form1::Original+0xcf (004022ef)


004022de 8b35a0104000    mov     esi,dword ptr [Project1!_imp____vbaFreeStr (004010a0)]


004022e4 8d4de8          lea     ecx,[ebp-18h]


004022e7 ffd6            call    esi


004022e9 8d4de4          lea     ecx,[ebp-1Ch]


004022ec ffd6            call    esi


004022ee c3              ret


004022ef 8b4df0          mov     ecx,dword ptr [ebp-10h]


004022f2 5f              pop     edi


004022f3 5e              pop     esi


004022f4 33c0            xor     eax,eax


004022f6 64890d00000000  mov     dword ptr fs:[0],ecx


004022fd 5b              pop     ebx


004022fe 8be5            mov     esp,ebp


00402300 5d              pop     ebp


00402301 c20800          ret     8


00402304 ff156c104000    call    dword ptr [Project1!_imp____vbaErrorOverflow (0040106c)]


0040230a 90              nop


0040230b 90              nop


0040230c 90              nop


0040230d 90              nop


0040230e 90              nop


0040230f 90              nop


 


Pois bem, as chamadas do VBRuntime, iniciadas por _imp___ (rotinas importadas do VBRutime) chamam, dentro delas, outras APIs que constantemente alocam e desalocam memória. Para se ter uma idéia, olhe as chamadas feitas pelo VBRuntime para cada interação de qualquer um dos loops disassemblados acima:


 


  352 13277 [  0] Project1!Form1::Original


    7     0 [  1]   MSVBVM60!__vbaFreeStr


   10     0 [  2]     OLEAUT32!SysFreeString


   11     0 [  3]       kernel32!TlsGetValue


   19    11 [  2]     OLEAUT32!SysFreeString


   16     0 [  3]       OLEAUT32!APP_DATA::FreeCachedMem


    9     0 [  4]         ole32!CRetailMalloc_GetSize


   15     0 [  5]           ntdll!RtlSizeHeap


    3     0 [  6]             ntdll!RtlDebugSizeHeap


   19     0 [  7]               ntdll!_SEH_prolog


   17    19 [  6]             ntdll!RtlDebugSizeHeap


   14     0 [  7]               ntdll!RtlpCheckHeapSignature


   26    33 [  6]             ntdll!RtlDebugSizeHeap


   19     0 [  7]               ntdll!RtlEnterCriticalSection


   31    52 [  6]             ntdll!RtlDebugSizeHeap


   15     0 [  7]               ntdll!RtlpValidateHeap


   10     0 [  8]                 ntdll!RtlpValidateHeapHeaders


   27    10 [  7]               ntdll!RtlpValidateHeap


   38    89 [  6]             ntdll!RtlDebugSizeHeap


   35     0 [  7]               ntdll!RtlpValidateHeapEntry


   18     0 [  8]                 ntdll!RtlpCheckBusyBlockTail


   18     0 [  9]                   ntdll!RtlCompareMemory


   27    18 [  8]                 ntdll!RtlpCheckBusyBlockTail


   58    45 [  7]               ntdll!RtlpValidateHeapEntry


   44   192 [  6]             ntdll!RtlDebugSizeHeap


   34     0 [  7]               ntdll!RtlSizeHeap


   49   226 [  6]             ntdll!RtlDebugSizeHeap


    5     0 [  7]               ntdll!RtlDebugSizeHeap


    8     0 [  8]                 ntdll!RtlLeaveCriticalSection


    6     8 [  7]               ntdll!RtlDebugSizeHeap


   51   240 [  6]             ntdll!RtlDebugSizeHeap


    9     0 [  7]               ntdll!_SEH_epilog


   52   249 [  6]             ntdll!RtlDebugSizeHeap


   19   301 [  5]           ntdll!RtlSizeHeap


   11   320 [  4]         ole32!CRetailMalloc_GetSize


   95   331 [  3]       OLEAUT32!APP_DATA::FreeCachedMem


   22   437 [  2]     OLEAUT32!SysFreeString


   10   459 [  1]   MSVBVM60!__vbaFreeStr


  353 13746 [  0] Project1!Form1::Original


 


 


Agora observe o total de uma completa execução do método Original():


 


14108 instructions were executed in 14107 events (0 from other threads)


 


Function Name                               Invocations MinInst MaxInst AvgInst


MSVBVM60!__vbaFreeStr                              2      10      10      10


MSVBVM60!__vbaStrCat                              20      12      12      12


MSVBVM60!__vbaStrCopy                              3      14      19      16


MSVBVM60!__vbaStrMove                            20      12      14      13


OLEAUT32!APP_DATA::AllocCachedMem         21      20      52      30


OLEAUT32!APP_DATA::FreeCachedMem         21      35      95      55


OLEAUT32!BstrLenA                                   40       8       9       8


OLEAUT32!GetAppData                               21      17      17      17


OLEAUT32!SysAllocStringByteLen                 21      33      58      34


OLEAUT32!SysFreeString                             21      22      22      22


OLEAUT32!VarBstrCat                                20      58     117     102


Project1!Form1::Original                              1     362     362     362


kernel32!TlsGetValue                                 42      11      11      11


ntdll!RtlCompareMemory                             21      18      18      18


ntdll!RtlDebugSizeHeap                              42       6      52      29


ntdll!RtlEnterCriticalSection                         21      19      19      19


ntdll!RtlLeaveCriticalSection                        21       8       8       8


ntdll!RtlSizeHeap                                       42      19      34      26


ntdll!RtlpCheckBusyBlockTail                       21      27      27      27


ntdll!RtlpCheckHeapSignature                      21      14      14      14


ntdll!RtlpValidateHeap                               21      27      27      27


ntdll!RtlpValidateHeapEntry                        21      58      58      58


ntdll!RtlpValidateHeapHeaders                    21      10      10      10


ntdll!_SEH_epilog                                      21       9       9       9


ntdll!_SEH_prolog                                      21      19      19      19


ole32!CRetailMalloc_GetSize                       21      11      11      11


 


 


Quando eu dúvida verifique o código disassemblado: o Assembly nunca mente! J


 


Pois bem, uma solução otimizada deve eliminar ou reduzir drasticamente as constantes alocações e desalocações de memória.


 


 


SOLUÇÃO


 


A solução se baseia em evitar constante alocação/desalocação de memória.


 


Eis a rotina otimizada:


 


‘ Fazer 10 concatenacoes de string.


Private Sub Optimized(ByVal strText As String)


    ‘PARA CONCATENAR STRINGS NUNCA USE O OPERADOR +, PREFIRA O & QUE É MAIS RÁPIDO.


    ‘MAS PREFIRA PREALOCAR UMA VARIÁVEL E USAR MID$ QUE É MUITO MAIS RÁPIDO, POIS EVITA CONSTANTES


    ‘REALOCAÇÕES DE STRING CAUSADAS POR &.


    ‘Nao e’ usado um loop ou uma chamada de rotina propositalmente para poupar ciclos de CPU.


    ‘E’ uma tecnica conhecida como unroll the loop.


    Dim lIndex As Long


    Dim strBuffer As String


    Dim strSubStr As String


    Dim lLen As Long


   


    ‘Aloca buffer para armazenar a string.


    ‘ATENÇÃO! Colocar um valor suficientemente grande para a tarefa.


    strBuffer = Space$(Len(strText) * 10)


    lIndex = 1


   


    ‘1- Inicializa a string a ser colocada no buffer.


       Poderiamos usar strText no exemplo e poupar uma variavel e alguns ciclos de processamento,


       mas usando strSubStr fica mais facil para voce reusar esse codigo! 🙂


    strSubStr = strText


 


    ‘2- Obtemos tamanho da substring inicial.


    lLen = Len(strText)


   


    ‘3- Passamos a substring para o buffer.


    Mid$(strBuffer, lIndex, lLen) = strSubStr


   


    ‘4- Atualiza o novo indice para adicionar as proximas strings.


    lIndex = lIndex + lLen


   


    ‘5- Repetimos os passos. Entretanto, como estamos concatenando uma string de tamanho fixo que


       nao mudou, podemos ignorar as chamadas dos passos 1 e 2 acima.


    Mid$(strBuffer, lIndex, lLen) = strSubStr


    lIndex = lIndex + lLen


   


    Mid$(strBuffer, lIndex, lLen) = strSubStr


    lIndex = lIndex + lLen


   


    Mid$(strBuffer, lIndex, lLen) = strSubStr


    lIndex = lIndex + lLen


   


    Mid$(strBuffer, lIndex, lLen) = strSubStr


    lIndex = lIndex + lLen


   


    Mid$(strBuffer, lIndex, lLen) = strSubStr


    lIndex = lIndex + lLen


   


    Mid$(strBuffer, lIndex, lLen) = strSubStr


    lIndex = lIndex + lLen


   


    Mid$(strBuffer, lIndex, lLen) = strSubStr


    lIndex = lIndex + lLen


   


    Mid$(strBuffer, lIndex, lLen) = strSubStr


    lIndex = lIndex + lLen


   


    Mid$(strBuffer, lIndex, lLen) = strSubStr


  


   


End Sub


 


Recomendo que você teste ela contra a implementação original para medir o ganho de performance.


 


Preferi ater minha resposta unicamente com comandos Visual Basic para ter uma resposta em VB “puro” J, mas outra abordagem seria utilizar chamadas de API, que deveria proporcionar uma solução um pouco mais rápida, usando por exemplo:


 


Declare Sub CopyMemory Lib “kernel32” Alias “RtlMoveMemory” (lpDest As Any, lpSrc As Any, ByVal Length As Long)


 


How To Call Windows API Functions with Special Requirements from Visual Basic


http://support.microsoft.com/kb/202179/en-us


 


How To Retrieve Individual Bytes from a Multi-Byte Type in VB


http://support.microsoft.com/kb/171652/en-us


 


 


Voltando a solução apresentada acima, observe o que representa essa otimização em comparação com o código original:


 


  147  5319 [  0] Project1!Form1::Optimized


    7     0 [  1]   MSVBVM60!__vbaFreeStr


   10     0 [  2]     OLEAUT32!SysFreeString


   11     0 [  3]       kernel32!TlsGetValue


   19    11 [  2]     OLEAUT32!SysFreeString


   16     0 [  3]       OLEAUT32!APP_DATA::FreeCachedMem


    9     0 [  4]         ole32!CRetailMalloc_GetSize


   15     0 [  5]           ntdll!RtlSizeHeap


    3     0 [  6]             ntdll!RtlDebugSizeHeap


   19     0 [  7]               ntdll!_SEH_prolog


   17    19 [  6]             ntdll!RtlDebugSizeHeap


   14     0 [  7]               ntdll!RtlpCheckHeapSignature


   26    33 [  6]             ntdll!RtlDebugSizeHeap


   19     0 [  7]               ntdll!RtlEnterCriticalSection


   31    52 [  6]             ntdll!RtlDebugSizeHeap


   15     0 [  7]               ntdll!RtlpValidateHeap


   10     0 [  8]                 ntdll!RtlpValidateHeapHeaders


   27    10 [  7]               ntdll!RtlpValidateHeap


   38    89 [  6]             ntdll!RtlDebugSizeHeap


   35     0 [  7]               ntdll!RtlpValidateHeapEntry


   18     0 [  8]                 ntdll!RtlpCheckBusyBlockTail


   18     0 [  9]                   ntdll!RtlCompareMemory


   27    18 [  8]                 ntdll!RtlpCheckBusyBlockTail


   58    45 [  7]               ntdll!RtlpValidateHeapEntry


   44   192 [  6]             ntdll!RtlDebugSizeHeap


   34     0 [  7]               ntdll!RtlSizeHeap


   49   226 [  6]             ntdll!RtlDebugSizeHeap


    5     0 [  7]               ntdll!RtlDebugSizeHeap


    8     0 [  8]                 ntdll!RtlLeaveCriticalSection


    6     8 [  7]               ntdll!RtlDebugSizeHeap


   51   240 [  6]             ntdll!RtlDebugSizeHeap


    9     0 [  7]               ntdll!_SEH_epilog


   52   249 [  6]             ntdll!RtlDebugSizeHeap


   19   301 [  5]           ntdll!RtlSizeHeap


   11   320 [  4]         ole32!CRetailMalloc_GetSize


   37   331 [  3]       OLEAUT32!APP_DATA::FreeCachedMem


   22   379 [  2]     OLEAUT32!SysFreeString


   10   401 [  1]   MSVBVM60!__vbaFreeStr


  148  5730 [  0] Project1!Form1::Optimized


 


Compare a coluna Invocations abaixo contra a mesma coluna da solução não otimizada! Veja a diferença que se traduz em tempo de execução bem menor!


 


Nota: Os valores da tabela acima devem ser divididos por 2 (como regra geral) porque usei dois loops com os dois operadores nesse teste.


 


5887 instructions were executed in 5886 events (0 from other threads)


 


Function Name                               Invocations MinInst MaxInst AvgInst


MSVBVM60!__vbaFreeStr                            3      10      10      10


MSVBVM60!__vbaLenBstr                           2       6       6       6


MSVBVM60!__vbaMidStmtBstr                   10      16      16      16


MSVBVM60!__vbaMidStmtBstrB                 10      73      73      73


MSVBVM60!__vbaStrCopy                           2      19      19      19


MSVBVM60!__vbaStrMove                           1      12      12      12


MSVBVM60!omemset                                 1      75      75      75


MSVBVM60!rtcSpaceBstr                             1      22      22      22


OLEAUT32!APP_DATA::AllocCachedMem       3      58      60      58


OLEAUT32!APP_DATA::FreeCachedMem        3      35      46      39


OLEAUT32!GetAppData                              3      17      17      17


OLEAUT32!SysAllocStringByteLen                2      58      58      58


OLEAUT32!SysAllocStringLen                      1      31      31      31


OLEAUT32!SysFreeString                            3      22      22      22


Project1!Form1::Optimized                         1     157     157     157


kernel32!TlsGetValue                                6      11      11      11


ntdll!RtlAllocateHeap                                3      25      25      25


ntdll!RtlAllocateHeapSlowly                       9       4     339     118


ntdll!RtlCompareMemory                           3      18      18      18


ntdll!RtlCompareMemoryUlong                   3      66     250     180


ntdll!RtlDebugAllocateHeap                       6       6      85      45


ntdll!RtlDebugSizeHeap                            6       6      52      29


ntdll!RtlEnterCriticalSection                       6      19      19      19


ntdll!RtlFillMemoryUlong                            6      27      71      49


ntdll!RtlGetNtGlobalFlags                          6       4       4       4


ntdll!RtlLeaveCriticalSection                      6       8       8       8


ntdll!RtlSizeHeap                                     6      19      34      26


ntdll!RtlpCheckBusyBlockTail                      3      27      27      27


ntdll!RtlpCheckHeapSignature                    6      14      14      14


ntdll!RtlpGetExtraStuffPointer                    6      10      10      10


ntdll!RtlpUpdateIndexInsertBlock               3      12      12      12


ntdll!RtlpUpdateIndexRemoveBlock             3      14      14      14


ntdll!RtlpValidateHeap                              6      27      27      27


ntdll!RtlpValidateHeapEntry                       3      58      58      58


ntdll!RtlpValidateHeapHeaders                   9      10      10      10


ntdll!_SEH_epilog                                   15       9       9       9


ntdll!_SEH_prolog                                   15      19      19      19


ole32!CRetailMalloc_Alloc                          3       9       9       9


ole32!CRetailMalloc_GetSize                      3      11      11      11


 


 


Agora a melhor parte! Dicas quentes de otimização de código Visual Basic 6.


 


 


DICAS DE OTIMIZAÇÃO DE CÓDIGO EM VISUAL BASIC 6


 


1-   Coloque Refências de Objetos em variáveis auxiliares, chamadas cache.


 


*      Exemplo NÃO OTIMIZADO:


*      Set rst = New ADODB.Recordset


*      Set rst.ActiveConnection = CurrentProject.Connection


*      rst.Source = “tblTests”


*      rst.Open


 


*      For i = 1 to lRepeats


*            strName = rst.Fields(0).Name


*      Next


 


*      Exemplo OTIMIZADO:


*      Set rst = New ADODB.Recordset


*      Set rst.ActiveConnection = CurrentProject.Connection


*      rst.Source = “tblTests”


*      rst.Open


*      Dim fld as ADODB.Field


*      Set fld = rst.Fields(0)    ‘Criada como as Field.


 


*      For i = 1 to lRepeats


    strName = fld.Name


*      Next


 


*      Nota: O mesmo não se aplica para Visual Basic .NET que consegue otimizar o código quando identifica a versão convencional gerando um código melhor do que se o mesmo fosse otimizado.


 


2-   Use LenB para testar strings.


 


*      Exemplo NÃO OTIMIZADO:


*      If strValue = “” then


 


*      Exemplo OTIMIZADO:


*      If LenB(strValue) = 0 then


 


*      Nesse caso ao se usar LenB o VB consulta o tamanho da string já armazenado no início da mesma, num DWORD (4 bytes), e ao se usar a comparação com “” o VB cria uma string vazia para comparar com a string atual. Notem que o tamanho da string está em bytes, portanto, Len() teria que fazer a conversão que LenB() não faz.


 


3- Use vbNullString ao invés de inicializar uma string com “”


 


*      Exemplo NÃO OTIMIZADO:


*      strItem = “”


 


*      Exemplo OTIMIZADO:


*      strItem = vbNullString


 


*      No exemplo não otimizado o VB cria uma string nova e copia na variável, no exemplo otimizado o VB usa seu próprio ponteiro interno para uma string vazia, economizando tempo ao se inicializar uma string.


 


 


4- Evite concatenações desnecessárias.


 


*      Exemplo NÃO OTIMIZADO:


*      strTest = “A” & “B” & “C” & “D” & “E”


 


*      Exemplo OTIMIZADO:


*      strTest = “ABCDE”


 


É preferível criar uma longa string do que criar string menores e concatená-las pois a concatenação implica em realocar memória para o novo conteúdo, que é uma operação custosa.


 


5- Prefira usar Mid$ do que concatenar com &


 


*      Exemplo NÃO OTIMIZADO:


*       strAux = strAux & “AAA “


*       strAux = strAux & “BBB “


*       strAux = strAux & “CCC “


*       strAux = strAux & “DDD “


*       strAux = strAux & “EEE”


 


*      Exemplo OTIMIZADO:


*      Dim lIndex As Long


*      Dim strBuffer As String


*      Dim strSubStr As String


*      Dim lLen As Long


*          


*      ‘Aloca buffer para armazenar a string.


*      strBuffer = Space$(100)


*      lIndex = 1


*          


*      strSubStr = “AAA”


 


*      lLen = Len(strSubStr)


*      Mid$(strBuffer, lIndex, lLen) = strSubStr


*      lIndex = lIndex + lLen


*          


*      strSubStr = “BBB”


*      lLen = Len(strSubStr)


*      Mid$(strBuffer, lIndex, lLen) = strSubStr


*      lIndex = lIndex + lLen


*          


*      E assim sucessivamente. Isso evita a realocação de memória constante do exemplo não otimizado.


 


 


6- Ao comparar strings use StrComp ao invés de UCase


 


*      Exemplo NÃO OTIMIZADO:


*      If UCase(strValue)  = UCase(strValue2) then


 


*      Exemplo OTIMIZADO:


*      If StrComp(strValue, strValue2, vbTextCompare) = 0 then


 


*      Essa técnica é vantajosa para strings não muito grandes uma vez que em comparações de strings grandes não há muita diferença de performance.


 


 


7- Use o operador LIKE ao invés de comparar caracteres individuais.


 


*      Exemplo NÃO OTIMIZADO:


*      bMatch = True


*      For j = 1 to 5


*          intCh = AscW(Mid$(strTest, j, 1))


    Select Case j


                        Case 1, 3, 4, 5


                                if(IsCharAlpha(intCh) = 0) then


                                        bMatch = false


                                end if


                        Case 2


                                if(IsCharAlpha(intCh) <> 0) then


                                        bMatch = false


                                endif


    End Select


        if not bMatch then


          Exit For


     end if


*      Next


 


*      Exemplo OTIMIZADO:


*      bMatch = strTest Like “[A-Z]#[A-Z][A-Z][A-Z]”


 


*      Há grande diferença de performance quando usando a abordagem otimizada.


 


8- Use funções com terminação em $ sempre que possível.


 


*      Exemplo NÃO OTIMIZADO:


*      For i = 1 To 5


*          strValue = Left(strValue, 3)


*      Next


 


*      Exemplo OTIMIZADO:


*      For i = 1 To 5


*          strValue = Left$(strValue, 3)


*      Next


 


*      Ao utilizar as funções com terminação em $ não há conversões de tipos, ou seja, o retorno é sempre string. Ao usar as funções sem a terminação $ o retorno é sempre um tipo Variant que é convertido para String. Conversões implícitas de dados são prejudiciais em qualquer linguagem de programação, incluindo Visual Basic 6 tanto por razões de performance quanto por aumentar a probabilidade de se cometer bugs.


 


9- Use atribuições lógicas ao invés de IF’s


 


*      Exemplo NÃO OTIMIZADO:


*      If x = 5 Then


*          y = true


*      Else


*          y = false


*      End If


 


*      Exemplo OTIMIZADO:


*      y = (x = 5)


 


Nota: Essa otimização é válida para Access e Visual Basic 6.0 uma vez que em .NET e Visual C++ o compilador otimiza a chamada não otimizada.


 


 


10- For…Next é mais rápido que Do…Loop


 


*      Exemplo NÃO OTIMIZADO:


*      i = 1


*      Do Until i > nLimit


*          j = i


*          ‘Faz algo.


*          i = i + 1


*      Loop


 


*      Exemplo OTIMIZADO:


*      For i = 1 To nLimit


*          j = i


*          ‘Faz algo.


*      Next


 


*      A versão com Do…Loop é mais lenta porque é necessário se incrementar ou decrementar uma variável, quando o For…Loop faz isso automaticamente.


 


 


11- Use IF/ELSE/END IF ao invés de IIF()


 


*      Exemplo NÃO OTIMIZADO:


*      strValue = IIf(i Mod 2 = 0, “Mesmo”, “Diferente”)


 


*      Exemplo OTIMIZADO:


*      If i Mod 2 = 0 Then


*          strValue = “Mesmo”


*      Else


*          strValue = “Diferente”


*      End If


 


 


12- Em Arrays o For…Next é mais rápido que For Each…Next


 


*      Exemplo NÃO OTIMIZADO:


*      For Each Index In Array


*          j = Index


*      Next


 


*      Exemplo OTIMIZADO:


*      For i = LBound(Array) To UBound(Array)


*          j = Array(i)


*      Next


 


*      Atenção! Para Collections a regra oposta é a que se aplica.


 


 


13- Sempre use Early Binding.


 


*      Exemplo NÃO OTIMIZADO:


*      Dim rst as ADODB.Recordset


*      Dim strName as String


*      Dim fld as Object   ‘Late Binding, prejuízo de performance.


 


*      Set rst = New ADODB.Recordset


*      Set rst.ActiveConnection = CurrentProject.Connection


*      rst.Source = “tblTests”


*      rst.Open


 


*      Set fld = rst.Fields(0)


 


*      Exemplo OTIMIZADO:


*      Dim rst as ADODB.Recordset


*      Dim strName as String


*      Dim fld as ADODB.Field    ‘Early Binding, ganho de performance


 


*      Set rst = New ADODB.Recordset


*      Set rst.ActiveConnection = CurrentProject.Connection


*      rst.Source = “tblTests”


*      rst.Open


 


*      Set fld = rst.Fields(0)


 


 


14- Simplifique e otimize expressões com AND, OR e XOR


 


*      Exemplo NÃO OTIMIZADO:


*      If (x < 0 And y < 0) Or (x >= 0 And y >= 0) Then


 


*      Exemplo OTIMIZADO:


*      If (x Xor y) >= 0 then


 


 


15- Use a técnica de “curto-circuito” em expressões.


 


*      Exemplo NÃO OTIMIZADO:


*      If x > 0 And y <= 0 And z = 0 Then


 


*      Exemplo OTIMIZADO:


*      Select Case False


*          Case x > 0, y <= 0, z = 0


*          Case Else


*               ‘Faça isso.


*      End Select


 


*      O Visual Basic 6.0 quando avalia uma expressão como no exemplo não otimizado, valida cada condição desnecessariamente enquanto os compiladores C++ analisam apenas o suficiente para concluir a expressão. Entretanto, ao utilizar o artifício do Select Case o VB analisa a expressão fazendo “short-circuit” como no Visual C++.


 


16- Procure atribuir o resultado de um recordset em um array quando possível.


 


*      Exemplo NÃO OTIMIZADO:


*      Dim rs As New ADODB.Recordset


*      Dim fldName as ADODB.Field, fldName as ADODB.Field


*      rs.Open “SELECT au_lname, au_fname FROM authors”, “DSN=pubs”, , ,


*      Set fldLName = rs.Fields(“au_lname”)


*      Set fldFName = rs.Fields(au_fName”)


*      Do Until rs.EOF


*          List1.AddItem fldLName & “, “ & fldFName


*      Loop


*      rs.Close


 


*      Exemplo OTIMIZADO:


*      Dim rs As New ADODB.Recordset


*      Dim varArray() as Variant


*      Dim i As Long


 


*      rs.Open “SELECT au_lname, au_fname FROM authors”, “DSN=pubs”,


*      varArray() = rs.GetRows()


 


*      For i = 0 to UBound(varArray, 2)


*          List1.AddItem varArray(0, 1)


*      Next


 


*      Nota: Válido para quando for necessário colocar valores em um controle como num ListBox.


 


 


17 – Use AscW e ChrW$.


 


*      Exemplo não otimizado:


*      value = Right$(Asc(strSomeChar))


 


*      Exemplo otimizado:


*      value = Right$(AscW(strSomeChar))


 


*      VB usa Unicode internamente, o qual usa 2 bytes por caracter. Usando as funções Unicode o VB não tem que fazer a conversão de ASCII para Unicode implicitamente.


 


18 – Cheque existência de uma substring dentro de uma string usando InStrB


 


*      Como usar:


*      If InStrB(Text$, SearchFor$) <> 0 then


 


*      A versão byte de InStrB é a mais rápida e serve para checar se uma string existe dentro de outra desde que a localização não seja relevante, uma vez que o retorno é em bytes.


 


 


Espero que tenham gostado desse Desafio.


Agora vou publicar o terceiro Desafio da Semana!


 


 


 

Comments (0)