Pular para o conteúdo

Short Circuit: Uma boa prática

Short Circuit: Uma boa prática

Em diversas linguagens de programação – para as boas práticas em condicionantes, estrutura de repetições ou em outras validações – recomenda-se testar primeiro as opções que possuem mais chance de ocorrerem e que são menos custosas em termo de processamento. Isto evita que outras validações sejam efetuadas sem necessidade, fornecendo uma melhor performance para o código. Em PL/SQL não é diferente e para o processamento de uma grande massa de dados a adoção desta prática promove um ganho bastante significativo.

Agora devem está imaginando: “Isto é óbvio”. De fato. Contudo, nota-se que em grande parte das vezes esta boa prática não é implementada. Eu mesmo não me preocupo com isto em uma primeira implementação. Recentemente, uma importação de uma grande quantidade de dados diários estava fora do tempo que consideravam aceitável. Ao analisar o código foi percebido que existiam diversos IF’s e ELSE’s alinhados. A primeira ação foi verificar o comportamento dos inserts e updates. Quais ocorriam mais? Foi constatado que uma condição acontecia 76% das vezes e esta era testada apenas depois de 7 outras serem avaliadas. A ação foi reclassificar o alinhamento dos IFs e ELSEs em ordem de maior ocorrência, o que já promoveu um ganho no tempo de resposta. Esta boa prática, foi somado a utilização de Forall, Bulk Collect e Insert First o que tornou a execução ainda mais performática.

Agora vamos aos exemplos com intuito de comparar os tempos de execução. Para a estrutura abaixo foi feito um loop para ocorrer 5 milhões de vezes e alinhado vários IF e Elses. A busca que desejamos é uma string com conteúdo TESTE 1. Para o bloco com boas práticas a busca será satisfeita já no primeiro IF, logo não será necessário efetuar os demais testes poupando processamento. Já no segundo bloco, agora sem a boa prática, a busca será satisfeita apenas no último IF, obrigando a passagem por todas as outras condições.

SET serveroutput ON

DECLARE
  v_time_i_one NUMBER;
  v_time_i_two NUMBER;
  v_time_f_one NUMBER;
  v_time_f_two NUMBER;
  v_qtd_loop   NUMBER := 5000000;
  v_my_value   VARCHAR2(10) := 'TESTE 1';

BEGIN
  ---- BOA PRÁTICA

  v_time_i_one := dbms_utility.Get_time();
  FOR nx IN 1 .. v_qtd_loop
  LOOP
    IF v_my_value = 'TESTE 1' THEN
      NULL;
    ELSIF v_my_value = 'TESTE 2' THEN
      NULL;
    ELSIF v_my_value = 'TESTE 3' THEN
      NULL;
    ELSIF v_my_value = 'TESTE 4' THEN
      NULL;
    ELSIF v_my_value = 'TESTE 5' THEN
      NULL;
    ELSIF v_my_value = 'TESTE 6' THEN
      NULL;
    ELSIF v_my_value = 'TESTE 7' THEN
      NULL;
    ELSIF v_my_value = 'TESTE 8' THEN
      NULL;
    ELSIF v_my_value = 'TESTE 9' THEN
      NULL;
    ELSIF v_my_value = 'TESTE 10' THEN
      NULL;

    END IF;

  END LOOP;

  v_time_f_one := (dbms_utility.get_time - v_time_i_one)/100;

  dbms_output.Put_line('Com boa pratica: '
  || v_time_f_one
  || ' segundos');

  –-- SEM BOA PRATICA

  v_time_i_two := dbms_utility.get_time();

  FOR nx IN 1 .. v_qtd_loop
  LOOP
    IF v_my_value = 'TESTE 10' THEN
      NULL;
    ELSIF v_my_value = 'TESTE 9' THEN
      NULL;
    ELSIF v_my_value = 'TESTE 8' THEN
      NULL;
    ELSIF v_my_value = 'TESTE 7' THEN
      NULL;
    ELSIF v_my_value = 'TESTE 6' THEN
      NULL;
    ELSIF v_my_value = 'TESTE 5' THEN
      NULL;
    ELSIF v_my_value = 'TESTE 6' THEN
      NULL;
    ELSIF v_my_value = 'TESTE 7' THEN
      NULL;
    ELSIF v_my_value = 'TESTE 8' THEN
      NULL;
    ELSIF v_my_value = 'TESTE 9' THEN
      NULL;
    END IF;

  END LOOP;

  v_time_f_two := (dbms_utility.get_time - v_time_i_two)/ 100;

  dbms_output.Put_line('Sem boa pratica: '
  || v_time_f_two
  || ' segundos');
  dbms_output.Put_line('Ou seja, '
  || Round((v_time_f_two*100)/v_time_f_one,2)
  || '% mais rapido.');

END;
Short Circuit

A melhoria da performance entre as duas estruturas é de mais de 800%, já que para a primeira situação o número de testes feitos é bem inferior que a segunda, economizando processamento e passagens desnecessárias.

Para o segundo exemplo, simula-se o que se chama de Short-Circuit. O curto circuito ocorre quando se consegue determinar o resultado de uma validação apenas analisando parte da expressão, sem a necessidade de passagens para os próximos operadores. Conforme, tabela abaixo, quando se quer um resultado TRUE para uma consulta entre TRUE OR FALSE, é necessário verificar a veracidade apenas do lado esquerdo, já que se ele for verdadeiro, não importa o que virá a sua direita, pois a expressão como um todo sempre será verdadeira.

LVbkzbm9DRq8HbLCgin mJmW872jkdNYVwbpVCKkKE6 TkByZ9oQlo7pgPcTzLOJ pPCebVP0qNAq4c8 VgVJ8pf20 nxtW7FXF1jk basDrrYe0cLGPL9BINFL qhEEeK7UnMVe7cDPylG8ZA

Supondo que temos duas FUNCTION’s onde o processamento de uma leva 0.5 segundos, enquanto a segunda 1 segundo. Na primeira condicional colocaremos ao lado esquerdo a chamada da função que executa em 0.5 segundos e para a parte, sem boa prática, inverteremos a ordem e executaremos antes a função que tem um maior custo e tempo de processamento. Desta maneira, com dissera anteriormente, o simples fato do lado esquerdo ser verdadeiro a validação por completa não precisará ser feito, economizando tempo e processamento.

SET serveroutput ON

DECLARE
  v_time_i_one NUMBER;
  v_time_i_two NUMBER;
  v_time_f_one NUMBER;
  v_time_f_two NUMBER;
  v_qtd_loop   NUMBER := 15;
  v_my_value   VARCHAR2(10) := 'TESTE 1';

  FUNCTION Dorme (p_segundos NUMBER)
    RETURN BOOLEAN AS
  BEGIN
    dbms_lock.Sleep(p_segundos);

    RETURN TRUE;

  END;

  BEGIN
    –-- COM BOA PRATICA

    v_time_i_one := dbms_utility.get_time();

    FOR nx IN 1 .. v_qtd_loop
    LOOP
      IF Dorme(0.5)
        OR
        Dorme(1) THEN
        NULL;

      END IF;

    END LOOP;

    v_time_f_one := (dbms_utility.get_time - v_time_i_one)/100;

    dbms_output.Put_line('Com boa pratica: '
    || v_time_f_one
    || ' segundos');

    –-- SEM BOA PRATICA

    v_time_i_two := dbms_utility.get_time();

    FOR nx IN 1 .. v_qtd_loop
    LOOP
      IF Dorme(1)
        OR
        Dorme(0.5) THEN
        NULL;

      END IF;

    END LOOP;

    v_time_f_two := (dbms_utility.get_time - v_time_i_two)/ 100;

    dbms_output.Put_line('Sem boa pratica: '
    || v_time_f_two
    || ' segundos');

    dbms_output.Put_line('Ou seja, '
    || Round((v_time_f_two*100)/v_time_f_one,2)
    || '% mais rapido.');

  END;
OP53o

Nota-se que com a boa prática as validações são executadas na metade do tempo, tenho um ganho de performance considerável.

Através dos exemplos, entende-se que as ordens dos fatores – para estes casos – altera o tempo de resposta. Ainda, fica claro que Tuning PL/SQL nem sempre é utilizar packages, functions ou estruturas “desconhecidas”. As boas práticas são também cruciais para melhor performance.

Referências

Quão útil foi este post ?

Clique em uma estrela para classificar o post

nota média 5 / 5. Contagem de votos: 29

Sem votos ! Seja o primeiro a classificar !

1 comentário em “Short Circuit: Uma boa prática”

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *

plugins premium WordPress