Tag: desempenho

Melhorando o tempo de carregamento em grandes aplicações web na nuvem [vídeo]

Meu trabalho na Atlassian envolve primariamente o desempenho do JIRA na nuvem. Descrevi parte do meu trabalho no artigo Hello, JIRA, mas existem muitos detalhes complexos que acabam ficando de fora.

Neste artigo vou descrever brevemente um importante aspecto sobre desempenho de grandes aplicações web e indicar um vídeo que explica o assunto do ponto de vista do Facebook em maiores detalhes.

Leia mais

Entendendo as Configurações de Memória da Máquina Virtual Java (JVM)

java

Talvez você já tenha ouvido falar que em Java não é preciso preocupar-se com gerenciamento de memória, ou ainda que em Java a memória nunca acaba por causa do Garbage Collector (GC).

Bem, estou aqui para lhe dizer que tudo isso é besteira. Depois de erros banais como NullPointerException e SqlException, os problemas mais sérios e comuns que tenho visto ao longo dos anos são justamente relacionados à memória, isto é: OutOfMemoryError.

Seja por falta de configuração adequada ou implementação equivocada. Código ruim pode alocar objetos indeterminadamente sem remover as referências a eles, de forma que nem o GC dá um jeito. Porém, neste artigo vamos focar na parte de configuração.

O erro OutOfMemoryError manifesta-se em duas formas principais: Java Heap Space e PermGen Space. Ambos são, respectivamente, relacionados a trechos de memória dinâmico e permanente do Java.

A maioria das aplicações sérias passa, em algum momento, por ajustes na quantidade de memória disponível tanto para a seção dinâmica quanto para a seção permanente.

Vamos ver o que é tudo isso e como efetuar a configuração.

Heap Space (Memória dinâmica)

O Heap Space é o local onde o Java armazena suas variáveis e instâncias de objetos. Este espaço de memória pode aumentar ou diminuir ao longo do tempo, dinamicamente, de acordo com a quantidade de objetos usados no programa.

Para definir quanta memória pode ser usada pelo Java, é preciso informar parâmetros ao executar a JVM. Caso você não saiba, executar o java cria uma nova instância da JVM, portanto qualquer parâmetro passado a este programa é uma configuração para essa JVM.

Já dei uma breve introdução sobre isso no artigo Instalando, Configurando e Usando o Eclipse Kepler, porém cada programa pode ter seu modo de configurar. Se você está usando um programa qualquer ou um servidor de aplicação (JBoss, Websphere, Weblogic, Glassfish, Tomcat, etc.), procure na documentação do mesmo onde fica a configuração dos parâmetros de memória.

Neste artigo, vou exemplificar uma chamada diretamente ao comando java via linha de comando.

Quantidade máxima de memória dinâmica

O parâmetro Xmx define a quantidade máxima de memória dinâmica que a Máquina Virtual Java pode alocar para armazenar esses objetos e variáveis.

É importante definir uma quantidade máxima de memória razoavelmente maior do que a média usada na aplicação para evitar não só OutOfMemoryError como também escassez de memória.

Trabalhar no limite da memória disponível faz o Garbage Collector executar muitas vezes para coletar objetos não usados e isso resulta em pausas indesejadas no programa durante a varredura dos objetos.

Quantidade inicial de memória dinâmica

O Xms define a quantidade inicial de memória dinâmica alocada no início da JVM.

É importante verificar quanto sua aplicação usa em média e definir um valor próximo disso. Dessa forma, não ocorrerão muitas pausas para alocação de memória, resultando em um desempenho maior de inicialização até o ponto em que a aplicação está executando num patamar estável.

PermGen Space (Memória permanente)

O Java também possui a PermGen Space, outra parte da memória chamada de “estática” ou “permanente”, utilizada para carregar suas classes (arquivos .class), internalizar Strings (string pool), entre outras coisas.

Como regra geral, a memória permanente não pode ser desalocada. Isso implica que, por exemplo, se sua aplicação tem muitos Jars e carrega muita classes, em algum momento poderá ocorrer um erro de PermGen Space. Isso é comum com quem abusa de frameworks com modelos de classes “pesados”, sendo um cenário comum a turminha JSF, PrimeFaces, Hibernate e JasperReports.

O erro ocorre porque não é possível ao Java carregar novas classes quando não há espaço e não é mais possível aumentar a memória permanente, pois não dá para descartar classes já carregadas para dar lugar a novas.

Da mesma forma que na memória Heap, é possível informar à JVM a quantidade de memória permanente máxima que pode ser alocada e a quantidade de memória permanente inicialmente alocada.

O parâmetro XX:MaxPermSize define a quantidade máxima de memória permanente que a JVM pode utilizar e o parâmetro XX:PermSize define o tamanho inicial alocado.

Os mesmos princípios mencionados anteriormente para definição de quanta memória Heap deve ser reservada podem ser aplicados aqui.

Gerações de Objetos

A seção dinâmica da memória, Heap Space, é ainda dividida entre “nova” e “velha” geração.

A seção da “nova geração” (Young Generation) é reservada para a criação de objetos. É o berçário do Java. Estatisticamente, os objetos recém-criados são mais suscetíveis a serem coletados.

Após ter algum tempo de vida, o GC pode promover os objetos que não forem coletados para a seção da “velha geração” (Old Generation), onde ficam os objetos com mais tempo de vida e com menos chances de serem coletados.

Os parâmetros XX:MaxNewSize e XX:NewSize definem, respectivamente, a quantidade máxima de memória reservada para os objetos da “nova geração” e a quantidade inicial de memória para tais objetos.

Note que a memória da “nova geração” fica dentro do espaço do Heap Space, portanto cuidado com os valores definidos.

Nunca tive a necessidade de modificar esses parâmetros e creio que na grande maioria dos casos você também não vai precisar. Use-os somente em caso de última necessidade.

Ilustração da divisão de memória da JVM

A imagem a seguir ilustra os conceitos apresentados no artigo:

java-memory

A memória total alocada pelo Java é a soma do Heap Space e PermGen Space. O espaço para a Young Generation fica dentro do Heap.

Exemplo

Cada programa Java tem sua forma de configuração, porém ao executar diretamente o comando java, você pode simplesmente passar os parâmetros da seguinte forma:

java -Xmx2g -Xms1024m -XX:MaxPermSize=1g -XX:PermSize=512m

No exemplo acima, definimos:

  1. -Xmx2g: Quantidade máxima de memória dinâmica de 2 Gigabytes
  2. -Xms1024m: Quantidade inicial de memória dinâmica de 1024 Megabytes ou 1 Gigabyte
  3. -XX:MaxPermSize=1g: Quantidade máxima de memória permanente de 1 Gigabyte
  4. -XX:PermSize=512m: Quantidade inicial de memória permanente de 512 Megabytes

Considerações

As configuração de memória apresentadas neste artigo resolvem 90% ou mais dos problemas cotidianos relacionados à memória com a JVM. Entretanto, este é apenas o tipo de ajuste mais básico que existe.

Você pode encontrar informações bem mais detalhadas, incluindo parâmetros de configurações específicas, em artigos voltados para otimização da JVM. Por exemplo:

Por fim, é importante lembrar que não existe uma fórmula mágica para determinar valores de memória para um sistema. Antes, deve ser levado em conta o tipo de uso, recursos utilizados, quantidade de usuários e capacidade do hardware.

Não creio que isso possa ser determinado objetivamente por alguma fórmula, mas através de um processo dedutivo conduzido por um profissional experiente.


Este artigo foi baseado em minha resposta no Stack Overflow em Português.

Strings em Java: há mais detalhes do que você imagina

Quem estudou um pouco sobre Java sabe que Strings possuem algumas peculiaridades. Provavelmente o leitor já sabe que elas são imutáveis, já ouviu falar do pool de Strings e que deve-se usar o método equals() ao invés do operador == para comparar o conteúdo de variáveis.

Neste artigo quero ir um pouco mais além, entendendo como isso funciona internamente.

Brincando com o == e com o pool de Strings

O Java utiliza um mecanismo chamado String interning, colocando as Strings num pool para tentar armazenar apenas uma cópia de cada sequência de caracteres em memória. Em tese, o programa usaria mesmo memória e seria mais eficiente em decorrência dessa otimização.

Quando o Java encontra literais String no código, ele retorna sempre uma mesma instância de String, que aponta para uma entrada no pool interno da JVM. Sendo assim, é bem possível usar o operador == para comparar duas variáveis que recebem literais String:

String literal = "str";
String outraLiteral = "str";

System.out.println(literal == outraLiteral); //exibe true

Inclusive, como o Java trata literais String como instâncias é possível comparar um literal diretamente, assim:

System.out.println(literal == "str"); //também exibe true

Por outro lado, não podemos confiar no operador de comparação quando não sabemos como a String foi criada, já que é possível criar outras instâncias de várias formas. Exemplo:

String novaInstancia = new String("str");
System.out.println("str" == novaInstancia); //exibe false

O código acima cria uma nova instância de String, que não é a mesma retornada pela JVM para o literal "str".

Mas, contudo, entretanto, isso não quer dizer que temos duas entradas de "str" no pool do Java. Como podemos verificar isso? Usando o método String.intern(), que retorna uma referência para a String que está no pool. Exemplo:

String novaInstancia = new String("str");
System.out.println("str" == novaInstancia.intern()); //exibe true

Outro exemplo:

String str1 = "teste";
String str2 = "outro teste".substring(6);
System.out.println(str1 == str2.intern()); //exibe true

Tudo muito interessante. Mas, e se criássemos uma String de uma forma mirabolante?

StringBuilder sb = new StringBuilder();
sb.append('s');
sb.append('t');
sb.append('r');
System.out.println("str" == sb.toString().intern()); //continua sendo true

Até aqui aprendemos que uma instância da classe String não representa diretamente o seu conteúdo, isto é, o conjunto de caracteres. Várias instâncias de String podem coexistir com o mesmo texto. A questão é que todas apontam para a mesma entrada no pool.

Continue lendo, pois ainda não esgotamos este assunto!

Mas então pare que serve o equals()?

Com as informações do tópico anterior poderíamos chegar precipitadamente à conclusão de que é sempre melhor comparar duas Strings usando o operador == e o método intern().

O método equals() da classe String compara todos os caracteres de duas Strings para verificar a igualdade, enquanto o == apenas verifica se as duas Strings apontam para a mesma entrada do pool, uma comparação numérica infinitamente mais eficiente do ponto de vista computacional.

Já que a comparação com == é muito mais rápida do que com o método equals(), devemos abandonar o equals() e usar o intern() em todo lugar? A resposta é não.

A verdade é que nem todas as Strings são internalizadas no pool imediatamente. Quando chamamos o método intern(), se ela não estiver lá, então o Java irá acrescentá-la.

O problema é que, uma vez no pool, a String vai para a memória permanente e não será mais coletada pelo garbage collector.

Quando se quer velocidade e o conjunto de valores é relativamente pequeno, usar o método intern() pode ser vantajoso. Mas se usarmos este recurso, por exemplo, para processamento de arquivos texto, XML, bancos de dados, logo esbarraremos num OutOfMemoryError.

Além disso, adicionar uma Strings no pool também pode ser uma operação “cara”. Além de ser necessário verificar se a String já existe lá (envolve o método hashCode() e modificação de um mapa), o Java provavelmente terá que tratar acessos concorrentes (mais de uma thread pode inserir elementos no pool).

Finalmente, uma grande desvantagem é o código ficar mais propenso a bugs (error prone), já que é preciso que o desenvolvedor sempre coloque o intern() quando necessário.

Concluindo, o conhecimento sobre o pool ajuda em casos específicos para otimização “fina” do código, mas o uso deve ser moderado.

Outras formas de comparação

Indo um pouco além da comparação exata de Strings, temos outras formas interessantes de comparação:

Case insensitive (sem considerar maiúsculas e minúsculas)

System.out.println("STR".equalsIgnoreCase("str")); //retorna true

Uma string contida em outra

System.out.println("###STR###".contains("STR")); //retorna true

Qual string é “maior” que a outra?

System.out.println("str1".compareTo("str2")); //retorna -1, pois "str1" é menor que "str2"

Ou:

System.out.println("str1".compareToIgnoreCase("STR2")); //retorna -1, ignorando a capitalização

O método compareTo retorna:

  • 1 se a primeira String for maior que a segunda
  • 0 se forem iguais
  • -1 se a primeira String for menor que a segunda

Começa com…

System.out.println("str1".startsWith("str")); //returna true, pois "str1" começa com "str"

Termina com…

System.out.println("str1".endsWith("r1")); //return true, pois "str1" termina com "r1"

Expressão regular

System.out.println("str2".matches("\\w{3}\\d")); //return true, pois corresponde à expressão regular

Está vazia?

String str1 = "";
System.out.println(str1.isEmpty());
System.out.println(str1.length() == 0);
System.out.println(str1.equals(""));

Particularmente eu prefiro o primeiro método para Java >= 6 e o segundo para as versões anteriores.


Este artigo foi baseado na minha resposta no StackOverflow em Português!

JavaScript: substituição em Strings

Substituir texto em Strings é algo muito comum em qualquer linguagem. Neste artigo, quero analisar qual seria a forma mais eficiente de fazer isso em JavaScript.

Dada a seguinte frase numa String:

var frase = "O céu está azul hoje!";

Qual é a melhor forma de trocarmos a cor do céu na frase acima para “verde”?

“O céu está verde hoje!”

Métodos de substituição

Após algumas pesquisas cheguei a quatro variações das técnicas para substituição de Strings. Elas fazem substituição de todas as ocorrências da String a ser substituída, caso haja mais de uma.

Criando uma expressão regular com RegExp

var regex = new RegExp("azul", "g");
var resultado = frase.replace(regex, "verde");

O RegExp é um tipo de objeto que recebe como parâmetro em seu construtor uma expressão regular e um modificador. A expressão usada foi a mais simples possível (“azul”). O modificador "g" significa global, isto é, a expressão irá afetar todas as ocorrências na frase, caso contrário, somente a primeira seria localizada.

Após criar a expressão regular, usei-a no método replace() da String. O primeiro argumento é a expressão usada para localizar partes do texto, as quais serão substituídas pelo conteúdo do segundo argumento (“verde”).

Criando uma expressão regular “nativa”

var regex  = /azul/g;
var resultado = frase.replace(regex, "verde");

Este código faz o mesmo que o anterior, mas escrevendo a expressão regular diretamente no código.

Usando as funções split e join

var resultado = frase.split('azul').join('verde');

Explicando o código acima, a função split() da String divide nossa frase em duas partes: uma com o conteúdo anterior da palavra “azul” e outra com o conteúdo posterior. O resultado é o vetor ["O céu está ", " hoje!"]. Depois disso, a função join() do array une os itens do vetor separando-os pela palavra “verde”.

Usando a função indexOf e substring

var pos = frase.indexOf('azul');
var ultpos = 0;
var resultado = '';
while (pos >= 0) {
    resultado += frase.substring(ultpos, pos) + 'verde';
    ultpos = pos + 4;
    pos = frase.indexOf('azul', pos + 4);
}
if (ultpos < frase.length) {
    resultado += frase.substring(ultpos, frase.length);
}

O código acima procura pela ocorrência da palavra “azul” na nossa frase e, enquanto houver alguma, vai montando uma outra String com a palavra substituída.

Fazendo o teste de desempenho

O primeiro passo para analisar a eficiência das soluções foi utilizar Jsperf. Este site permite a criação de casos de teste comparativos, com quantas variações forem necessárias. Eles podem ser executados por qualquer usuário, em qualquer navegador.

Clique aqui para ver ou executar os testes no jsperf!

Alguns testes foram realizados e o gráfico na data em que escrevo o artigo é o seguinte:

string-replace-performance

Nota: o item “regex2” se refere à expressão regular “nativa” mencionada anteriormente.

Cada novo usuário que executar os testes irá contribuir para a análise, então é provável que logo o gráfico no site Jsperf esteja diferente.

Análise dos resultados

Não temos uma conclusão! Não existe um consenso sobre o método mais rápido!

Em resumo:

  • O método com regex foi o melhor nas versões 7 e 10 do Internet Explorer
  • O método com split e join ganha no Google Chrome
  • E o método com indexOf e substring venceu no Firefox e no Ópera

Minha sugestão, baseada numa análise geral, é usar expressões regulares “nativas”, isto é, sem o RegExp. Note como a barra laranja está sempre em primeiro ou segundo lugar.


Este artigo foi baseado na minha resposta no StackOverflow em Português!

HTML e Javascript: trocando dois elementos de posição (swap)

Você tem dois elementos quaisquer na estrutura do documento da sua página (DOM). Como invertê-los, isto é, colocar um no lugar do outro?

Abordagens para solução

O problema não é tão simples quanto pode parecer a princípio, pois ao movermos um dos elementos, perdemos sua posição original e não é tão fácil armazenar a localização de um elemento em Javascript.

Vamos analisar algumas abordagens nos tópicos abaixo.

Variável auxiliar

Podemos usar o princípio básico da troca de valores entre variáveis. Por exemplo, dadas as variáveis A e B, podemos trocar os valores usando uma variável auxiliar AUX:

AUX = A
A = B
B = AUX

Simples, não? Mas estamos aqui falando da posição de elementos na estrutura HTML. O que seria nossa variável auxiliar? A resposta é: um elemento criado dinamicamente.

Colocando isso numa solução reutilizável em forma de um plugin para jQuery:

(function ($) {
    $.fn.swap = function(anotherElement) {
        var a = $(this).get(0);
        var b = $(anotherElement).get(0);
        var swap = document.createElement('span');
        a.parentNode.insertBefore(swap, a);
        b.parentNode.insertBefore(a, b);
        swap.parentNode.insertBefore(b, swap);
        swap.remove();
    }
}(jQuery));

Demo no jsfiddle

A utilização é muito simples:

$(seletorElemento).swap(seletorOutroElemento);

Puff! Os elementos foram trocados.

Armazenando a posição do elemento

Outra abordagem, que não envolve a criação de um elemento adicional, é armazenar a posição de pelo menos um dos elementos. Na verdade, só precisamos armazenar a posição do elemento que será movido primeiro.

O problema está em como representar a posição de um elemento, pois a sua inserção será feita relativamente a algum outro. Se usarmos o anterior como referência, teremos problemas se ele for o primeiro. Se usarmos o posterior, teremos problemas se ele for o último. Pior, os dois elementos a serem trocados podem estar próximos um do outro, em sequência!

Não vou descrever minuciosamente as nuances do algoritmo que desenvolvi porque não creio ser interessante, mas considere a seguinte implementação:

(function ($) {

    $.fn.swap = function(anotherElement) {

        var sameParentStrategy = function(one, another) {
            var oneIndex = one.index();
            var anotherIndex = another.index();
            var swapFunction = function(first, second, firstIndex, secondIndex) {
                if (firstIndex == secondIndex - 1) {
                    first.insertAfter(second);
                } else {
                    var secondPrevious = second.prev();
                    second.insertAfter(first);
                    first.insertAfter(secondPrevious);
                }
            }
            if (oneIndex < anotherIndex) {
                swapFunction(one, another, oneIndex, anotherIndex);
            } else {
                swapFunction(another, one, anotherIndex, oneIndex);
            }
        };

        var differentParentsStrategy = function(one, another) {
            var positionStrategy = function(e) {
                var previous = e.prev();
                var next = e.next();
                var parent = e.parent();
                if (previous.length > 0) {
                    return function(e) {
                        e.insertAfter(previous);
                    };
                } else if (next.length > 0) {
                    return function(e) {
                        e.insertBefore(next);
                    };
                } else {
                    return function(e) {
                        parent.append(e);
                    };
                }
            }
            var oneStrategy = positionStrategy(one);
            var anotherStrategy = positionStrategy(another);
            oneStrategy(another);
            anotherStrategy(one);
            return this;
        };

        //check better strategy
        var one = $(this);
        var another = $(anotherElement);
        if (one.parent().get(0) == another.parent().get(0)) {
            sameParentStrategy(one, another);
        } else {
            differentParentsStrategy(one, another);
        }

    };

}(jQuery));

Note as diferentes estratégias de troca, dependendo dos elementos serem ou não filhos de um mesmo “pai”.

Demo no jsfiddle.

Clonagem dos elementos

Uma terceira alternativa, um tanto simplista na verdade, é clonar os dois elementos e substituir os originais um pelo outro. Vejamos o código:

(function ($) {
    $.fn.swap = function(anotherElement) {
        // cache elements
        var $div1 = $(this),
            $div2 = $(anotherElement);
        // clone elements and their contents
        var $div1Clone = $div1.clone(),
            $div2Clone = $div2.clone();
        // switch places
        $div1.replaceWith($div2Clone);
        $div2.replaceWith($div1Clone);
    }
}(jQuery));

Análise das soluções

Cada abordagem parece ter seu ponto fraco. Criar um elemento auxiliar deixa qualquer um com um pé atrás por causa da criação e inserção de um elemento “inútil” ao DOM. Porém, a complexidade do algoritmo que calcula a posição dos elementos e as várias funções usadas da API do jQuery chega a dar calafrios na espinha. E quanto a clonar os elementos? Isso não pode ser algo do bem!

Note que a primeira abordagem, por ser simples e direta, pode facilmente ser implementada sem usar a API do jQuery. Usei propositalmente a API nativa do Javascript para gerar um ganho de desempenho, pois, como veremos a seguir, isso faz muita diferença. Já as demais abordagens exigiriam alterações nada triviais para depender apenas da API nativa.

Ao invés conjecturar, vamos logo ao teste de desempenho. Preparei oito cenários com os algoritmos de troca descritos acima. Para cada algoritmo, o desempenho para troca de nós que estão no mesmo nível e o desempenho para troca de nós que estão em locais diferentes do HTML foram medidos separadamente. Além disso, o algoritmo que usa a “variável auxiliar” foi desmembrado em dois: o primeiro usando a API do jQuery e o segundo, como descrito no respectivo tópico, usando a API nativa.

Considere o seguinte gráfico:

Desempenho de algoritmos de swap

A legenda para relacionar cor e algoritmo:

  • Azul escuro: nós no mesmo nível armazenando a posição do elemento.
  • Vermelho: nós em níveis diferentes armazenando a posição do elemento.
  • Amarelo: nós no mesmo nível com variável auxiliar e a API do jQuery.
  • Verde escuro: nós em níveis diferentes com variável auxiliar e a API do jQuery.
  • Roxo: nós no mesmo nível com variável auxiliar e API nativa, sem jQuery.
  • Azul claro: nós em níveis diferentes com variável auxiliar e API nativa, sem jQuery.
  • Pink: nós no mesmo nível usando clonagem.
  • Verde claro: nós de níveis diferentes usando clonagem.

Teste no jsperf

Conclusões

Os resultados confirmaram algumas das suspeitas:

  • A clonagem é muito lenta.
  • Usando a API do jQuery, armazenar o local da variável é mais rápido do que criar e inserir um elemento auxiliar.
  • Porém, usar a API nativa do Javascript gerou um ganho em torno de 10x no desempenho. Mesmo com a alteração no DOM gerada pera inserção da variável auxiliar, o desempenho desta solução superou todas as demais.

Não pretendo aqui tirar nenhum mérito de uma excelente biblioteca como o jQuery, pois em muitos casos ela nos permite obter um desempenho muito superior a uma “solução caseira”. Porém, não devemos tomar qualquer biblioteca por uma solução definitiva para todos os problemas.

Muitos cenários exigirão código específico e usar uma API nativa, de modo a remover camadas de abstração, muitas vezes é o melhor caminho.

Nós, desenvolvedores, devemos ser flexíveis o suficiente para trabalhar em diferentes níveis de abstração. Devemos saber quando “baixar” o nível… 😉

Este artigo foi baseado na minha resposta no StackOverflow em Português e nas respostas de outros excelentes desenvolvedores como Marcelo Gibson e Zuul!

Inserção em massa no SQL Server

Como inserir vários registros em uma tabela com valores sequenciais? Por exemplo, como podemos obter uma série de datas?

A resposta mais simples seria utilizar um cursor ou laço (loop), mas a execução de ambos é demasiadamente lenta para muitos registros. O desempenho máximo somente será obtido se a inserção for feita de uma só vez em um comando INSERT. Para isso, podemos usar uma tabela auxiliar com valores sequenciais de forma que possamos usar esses valores para derivar aqueles que necessitamos. Mas como obter tal tabela?

Uma das formas de unir velocidade e praticidade é criar uma tabela com um campo IDENTITY. Entretanto, não gosto da ideia de criar uma tabela física para auxiliar em operações completamente ortogonais às funcionalidades do sistema.

Opção fácil para poucos registros

A tabela MASTER..SPT_VALUES possui números sequenciais de 0 (zero) a 2047 e é nativa do SQL Server. O exemplo abaixo insere 2000 linhas com datas sequenciais de uma só vez:

INSERT INTO DATAS (DATA, DIADASEMANA)
SELECT DATEADD(d, NUMBER, @DATAINICIAL), DATEPART(DW,DATEADD(d, NUMBER, @DATAINICIAL))
FROM MASTER..SPT_VALUES
WHERE TYPE='P' AND NUMBER BETWEEN 0 AND 1999

Portanto, com essa tabela é possível fazer uma inserção em massa utilizando a coluna NUMBER e alguma função que calcule o valor do campo a partir do valor sequencial. No exemplo, a função DATEADD gera datas a partir da @DATAINICIAL, até 2000 dias à frente, e também armazena o dia da semana de cada data.

Porém, há uma questão sobre o uso de uma tabela do usuário MASTER, pois ela pode não ser acessível para você. E se não houver possibilidade de obter a devida permissão?

Utilizando uma tabela auxiliar em memória

Um outro método muito eficiente, mas que exige um pouco de Transact-SQL, é usar uma Variável do tipo TABLE. Confira o seguinte exemplo:

DECLARE @T TABLE (NUMBER INT)
DECLARE @I INT, @LIMITE INT

SET @I = 0
SET @LIMITE = 25000
WHILE @I < @LIMITE
BEGIN
    INSERT INTO @T (NUMBER) VALUES (@I)
    SET @I = @I + 1
END

Este código leva menos de 1 segundo para executar e cria uma tabela na memória com números de 0 a 24.999. Usando ainda o exemplo de geração de datas, podemos inserir 25 mil linhas de uma só vez, como mostra o novo exemplo abaixo:

DECLARE @DATAINICIO DATETIME
SET @DATAINICIO = '2001-01-01'
SELECT DATA, DATEPART(DW, DATA)
FROM (SELECT @DATAINICIO + NUMBER DATA FROM @T) T

Conclusão

Já usei várias vezes esse recurso para melhorar o desempenho de cálculos financeiros que envolvem parcelas, justo e vencimentos em feriados. Certa vez consegui diminuir o tempo de execução de uma procedure de cálculos, que levava 20 minutos para inserir 23 mil registros, para cerca de 20 segundos, gerando 150 mil registros.

Embora para alguns os exemplos acima possam soar como algum tipo de gambiarra, o conhecimento e a correta aplicação de tais recursos podem ser essenciaus quando as restrições de desempenho são prioridade.

Evite reiniciar o Tomcat 6 e deixe a inicialização mais rápida

Otimizações geralmente não caem bem em ambientes de desenvolvimento.

Imagine um desenvolvedor tendo que reiniciar o Tomcat a cada alteração em arquivos estáticos ou JSPs durante o desenvolvimento de uma determinada tela do sistema.
Para evitar isso, altere o arquivo context da sua aplicação da seguinte forma, acrescentando ou modificando os parâmetros antiJARLocking e antiResourceLocking para false.
Exemplo:
<Context (...)
    antiJARLocking="false" antiResourceLocking="false">

Agora o Tomcat 6 vai iniciar mais rapidamente e reconhecer alterações nos arquivos do sistema, ficando apenas limitado a cache de frameworks, como JSF, por exemplo.

Explicação

O antiResourceLocking faz com que o Tomcat crie uma pasta TEMP e copie todos os arquivos da aplicação para lá. Então ele ignora o que está na pasta original da aplicação.

Algo semelhante aplica-se ao antiJarLocking, mas com os jars da lib.

Além de desperdiçar espaço em disco, isso atrapalha o desenvolvimento de telas. Ativar esses recursos é indicado para ambientes de produção.

Com isso, o Tomcat também vai iniciar mais rapidamente, porque ele não vai mais ficar sincronizando todos os arquivos da aplicação para a pasta TEMP.

Tomcat lento no Eclipse com o plugin Sysdeo

Numa empresa que usa Tomcat como web container, alguns desenvolvedores relataram uma lentidão na inicialização e na depuração dos sistemas.

Um colega disse que a única solução encontrada até o momento era criar um novo workspace e importar os projetos do antigo.

Depois de pesquisar um pouco, encontrei em num fórum pelo mundo afora (não vou lembrar onde) algo relacionado a breakpoints. Bingo!

Se você está enfrentando um problema semelhante, ou seja Tomcat + Eclipse + Sysdeo + Lentidão, apague todos os breakpoints e há uma grande chance do seu workspace voltar ao normal.

Creative Commons O blog State of the Art de Luiz Ricardo é licenciado sob uma Licença Creative Commons. Copie, compartihe e modifique, apenas cite a fonte.