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

Jefferson de Almeida Costa

Jefferson de Almeida Costa

Jefferson de Almeida Costa, formado em Ciência da Computação, com pós-graduação em MBA em Engenharia de Software Orientada a Serviços – SOA pela FIAP (Faculdade de Informática e Administração Paulista) e pós-graduando em MBA em Gestão de Projetos pela USP/Esalq. É desenvolvedor PL/SQL, com foco em Tuning/Performance e grandes volumes de dados, com certificações Oracle Certified Associate (OCA) e Oracle Certified Professional (OCP) em PL/SQL e Oracle Certified Expert (OCE) em SQL e SQL Tuning, bem como Agile Scrum Master (ASM) e ITIL Foundation (ITFL) pela Exin. Site: https://www.jeffersonacosta.com/

Comentário(s) da Comunidade

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