É incrível como é fácil escrever aplicações Web usando ASP.NET. Devido a essa simplicidade, muitos desenvolvedores não perdem tempo estruturando seus aplicativos para obter melhor desempenho. Neste artigo, abordarei 10 dicas para escrever aplicativos Web de alto desempenho. Mas eu não limitaria essas sugestões a aplicativos ASP.NET, já que esses aplicativos são apenas parte de um aplicativo Web. Este artigo não tem a intenção de ser um guia definitivo para ajuste de desempenho de aplicações web — um livro inteiro não cobriria facilmente esse assunto. Considere este artigo um excelente ponto de partida.
Antes de me tornar um workaholic, eu gostava de escalar. Antes de qualquer grande subida começo por olhar atentamente os percursos dos guias e ler as recomendações dos visitantes anteriores. Mas não importa quão bom seja o guia, você precisa de uma experiência real de escalada antes de tentar uma escalada particularmente desafiadora. Da mesma forma, você só aprenderá a escrever aplicativos Web de alto desempenho quando tiver que resolver problemas de desempenho ou executar um site de alto rendimento.
Minha experiência pessoal vem de trabalhar como Gerente de Programas de Infraestrutura na divisão ASP.NET da Microsoft, onde administrei e gerenciei o www.ASP.NET e ajudei a arquitetar os servidores da comunidade, um dos vários servidores de comunidade bem conhecidos.
Aplicativos ASP.NET (Fóruns ASP.NET, .Text e nGallery combinados em uma plataforma). Tenho certeza que algumas das dicas que me ajudaram irão ajudar você também.
Você deve considerar dividir seu aplicativo em diversas camadas lógicas. Você deve ter ouvido o termo arquitetura física de 3 camadas (ou n camadas). Essas são abordagens arquitetônicas normalmente prescritas que separam fisicamente a funcionalidade entre processos e/ou hardware. Quando o sistema precisa ser expandido, mais hardware pode ser facilmente adicionado. No entanto, há um impacto no desempenho associado a saltos de processo e de máquina e deve ser evitado. Portanto, se possível, tente executar páginas ASP.NET e seus componentes relacionados juntos no mesmo aplicativo.
Devido à separação de código e aos limites entre as camadas, o uso de serviços Web ou a comunicação remota degradará o desempenho em 20% ou mais.
A camada de dados é um pouco diferente porque normalmente é melhor ter hardware dedicado ao banco de dados. No entanto, o custo do salto do processo para o banco de dados ainda é alto, portanto, o desempenho da camada de dados é a primeira questão que você deve considerar ao otimizar o código.
Antes de mergulhar nas correções de desempenho do seu aplicativo, primeiro certifique-se de criar um perfil do seu aplicativo para identificar os problemas específicos. Os principais contadores de desempenho, como aqueles que representam a porcentagem de tempo necessária para executar a coleta de lixo, também são úteis para descobrir onde um aplicativo gasta seu tempo principal. No entanto, onde o tempo é gasto é muitas vezes pouco intuitivo.
Este artigo descreve dois tipos de melhorias de desempenho: grandes otimizações (como usar o cache do ASP.NET) e pequenas otimizações que se repetem. Estas pequenas otimizações são por vezes particularmente interessantes. Você faz uma pequena alteração em seu código e ganha muito, muito tempo. Com grandes otimizações, você poderá observar um grande salto no desempenho geral. Com pequenas otimizações, você pode economizar apenas alguns milissegundos para uma solicitação específica, mas somando todas as solicitações todos os dias, pode ser uma grande melhoria.
Desempenho da camada de dados
Quando se trata de ajuste de desempenho de aplicativos, há um teste que você pode usar para priorizar seu trabalho: O código acessa o banco de dados? Se sim, com que frequência? Observe que esse mesmo teste também pode ser aplicado ao código que usa serviços Web ou comunicação remota, mas este artigo não aborda isso.
Se uma solicitação de banco de dados for necessária em um caminho de código específico e você achar que precisa otimizar outras áreas primeiro (como manipulação de string), pare e execute este teste de vareta. Se os seus problemas de desempenho não forem graves, é uma boa ideia gastar algum tempo otimizando o tempo gasto em relação ao banco de dados, a quantidade de dados retornados e a frequência das viagens de ida e volta para o banco de dados.
Com essas informações gerais em mente, vejamos dez dicas que podem ajudar a melhorar o desempenho do aplicativo. Primeiro, falarei sobre as mudanças que podem fazer a maior diferença.
Dica 1 — Retorne vários conjuntos de resultados
Observe atentamente o código do seu banco de dados para ver se há vários caminhos de solicitação no banco de dados. Cada viagem de ida e volta reduz o número de solicitações por segundo que o aplicativo pode atender. Ao retornar vários conjuntos de resultados em uma solicitação de banco de dados, você pode economizar o tempo total necessário para se comunicar com o banco de dados. Ao mesmo tempo, também torna o sistema mais escalável, reduzindo o trabalho do servidor de banco de dados no gerenciamento de solicitações.
Embora seja possível usar SQL dinâmico para retornar vários conjuntos de resultados, meu método preferido é usar procedimentos armazenados. Há algum debate sobre se a lógica de negócios deve residir no procedimento armazenado, mas acho que seria melhor se a lógica do procedimento armazenado pudesse restringir os dados retornados (reduzindo o tamanho do conjunto de dados, reduzindo o tempo gasto em a rede, não tendo que peneirar os dados da camada lógica), isso deve ser favorecido.
Ao preencher uma classe de negócios fortemente tipada usando uma instância SqlCommand e seu método ExecuteReader, você pode mover o ponteiro do conjunto de resultados para frente chamando NextResult. A Figura 1 mostra um exemplo de sessão usando classes de tipo para preencher vários ArrayLists. Retornar apenas os dados necessários do banco de dados reduzirá ainda mais a alocação de memória no servidor.
Figura 1 Extraindo vários conjuntos de resultados de um DataReader
// lê o primeiro conjunto de resultados
leitor = command.ExecuteReader();
// lê os dados desse conjunto de resultados
while (leitor.Leitura()) {
fornecedores.Add(PopulateSupplierFromIDataReader(leitor));
}
// lê o próximo conjunto de resultados
reader.NextResult();
// lê os dados desse segundo conjunto de resultados
while (leitor.Leitura()) {
produtos.Add(PopulateProductFromIDataReader(leitor));
}
Dica 2 - Acesso a dados paginados
O ASP.NET DataGrid possui um recurso interessante: suporte à paginação de dados. Quando a paginação está habilitada no DataGrid, um número fixo de registros é exibido por vez. Além disso, uma UI de paginação é exibida na parte inferior do DataGrid para facilitar a navegação entre os registros. A UI de paginação permite navegar para frente e para trás pelos dados exibidos e exibe um número fixo de registros por vez.
Há também uma pequena reviravolta. A paginação usando o DataGrid requer que todos os dados sejam vinculados à grade. Por exemplo, se a sua camada de dados precisar retornar todos os dados, o DataGrid filtrará todos os registros exibidos com base na página atual. Se 100.000 registros forem retornados durante a paginação no DataGrid, 99.975 registros serão descartados para cada solicitação (assumindo um tamanho de página de 25 registros). Quando o número de registros aumenta, o desempenho do aplicativo é prejudicado porque cada vez mais dados devem ser enviados a cada solicitação.
Uma excelente maneira de escrever código de paginação com melhor desempenho é usar procedimentos armazenados. A Figura 2 mostra um exemplo de procedimento armazenado para paginar a tabela Pedidos no banco de dados Northwind. Resumindo, tudo o que você precisa fazer neste momento é passar o índice e o tamanho da página. O conjunto de resultados apropriado é então calculado e retornado.
Figura 2 Paginação na tabela de pedidos
CRIAR PROCEDIMENTO northwind_OrdersPaged
(
@PageIndexint,
@PageSizeint
)
COMO
COMEÇAR
DECLARE @PageLowerBoundint
DECLARE @PageUpperBound int
DECLARE @RowsToReturn int
– Primeiro defina o número de linhas
DEFINIR @RowsToReturn = @PageSize * (@PageIndex + 1)
SET ROWCOUNT @RowsToReturn
– Define os limites da página
SET @PageLowerBound = @PageSize * @PageIndex
SET @PageUpperBound = @PageLowerBound + @PageSize + 1
– Crie uma tabela temporária para armazenar os resultados selecionados
CRIAR TABELA #PageIndex
(
IndexId int IDENTIDADE (1, 1) NÃO NULO,
ID do pedido interno
)
- Inserir na tabela temporária
INSERIR EM #PageIndex (ID do pedido)
SELECIONAR
ID do pedido
DE
Pedidos
ENCOMENDAR POR
OrderID DESC
– Contagem total de retorno
SELECT COUNT(OrderID) FROM Orders
-- Retornar resultados paginados
SELECIONAR
Ó.*
DE
Ordena Ó,
#PageIndex PáginaIndex
ONDE
O.OrderID = PageIndex.OrderID E
PageIndex.IndexID > @PageLowerBound E
PageIndex.IndexID <@PageUpperBound
ENCOMENDAR POR
PageIndex.IndexID
END
No servidor da comunidade, escrevemos um controle de servidor de paginação para completar toda a paginação de dados. Como você verá, estou usando o conceito discutido na Dica 1 para retornar dois conjuntos de resultados de um procedimento armazenado: o número total de registros e os dados solicitados.
O número total de registros retornados pode variar dependendo da consulta executada. Por exemplo, uma cláusula WHERE pode ser usada para restringir os dados retornados. Para calcular o número total de páginas exibidas na UI paginada, você deve saber o número total de registros a serem retornados. Por exemplo, se houver 1.000.000 de registros no total e você quiser usar uma cláusula WHERE para filtrá-los até 1.000 registros, a lógica de paginação precisará saber o número total de registros para renderizar a UI de paginação corretamente.
Dica 3 – Pool de conexões
Configurar uma conexão TCP entre um aplicativo Web e o SQL Server pode ser uma operação que consome muitos recursos. Os desenvolvedores da Microsoft já podem usar o pool de conexões há algum tempo, o que lhes permite reutilizar conexões de banco de dados. Em vez de configurar uma nova conexão TCP para cada solicitação, eles apenas configuram uma nova conexão quando não há conexões no pool de conexões. Quando a conexão é fechada, ela retorna ao pool de conexões onde mantém a conexão com o banco de dados, em vez de destruir totalmente a conexão TCP.
Claro, você precisa ter cuidado se desenvolver conexões com vazamentos. Quando terminar de usar suas conexões, certifique-se de fechá-las. Repetindo: não importa o que alguém diga sobre a coleta de lixo no Microsoft® .NET Framework, certifique-se de chamar explicitamente Close ou Dispose em uma conexão quando terminar de usá-la. Não confie no Common Language Runtime (CLR) para limpar e fechar conexões para você em horários predeterminados. Embora o CLR acabe destruindo a classe e forçando o fechamento da conexão, não há garantia de quando a coleta de lixo do objeto realmente ocorrerá.
Para usar o pool de conexões de maneira ideal, existem algumas regras que precisam ser seguidas. Primeiro abra a conexão, execute a operação e depois feche a conexão. Se necessário, abra e feche a conexão várias vezes por solicitação (de preferência aplicando a dica 1), mas não mantenha a conexão aberta o tempo todo e passe-a para dentro e para fora usando vários métodos diferentes. Segundo, use a mesma cadeia de conexão (e o mesmo ID de thread se estiver usando autenticação integrada). Se você não usar a mesma cadeia de conexão, por exemplo, personalizando a cadeia de conexão com base no usuário conectado, não obterá o mesmo valor de otimização que o pool de conexões fornece. Se você usar autenticação integrada e também representar um grande número de usuários, a eficiência do pool de conexões também diminuirá significativamente. Os contadores de desempenho de dados .NET CLR podem ser úteis ao tentar rastrear quaisquer problemas de desempenho relacionados ao pool de conexões.
Sempre que seu aplicativo se conectar a um recurso, como um banco de dados em execução em outro processo, você deverá otimizar concentrando-se no tempo necessário para se conectar a esse recurso, no tempo necessário para enviar ou recuperar dados e no número de viagens de ida e volta. Otimizar qualquer tipo de salto de processo em sua aplicação é o primeiro ponto para obter melhor desempenho.
A camada de aplicação contém a lógica para conectar-se à camada de dados e transformar dados em instâncias de classe e processos de negócios significativos. Por exemplo, um servidor de comunidade, onde você deseja preencher a coleção de Fóruns ou Threads, aplicar regras de negócios (como permissões e, o mais importante, executar a lógica de cache);
Dica 4 – API de cache ASP.NET
Antes de escrever uma linha de código de aplicativo, uma das primeiras coisas a fazer é estruturar a camada de aplicativo para aproveitar ao máximo os recursos de cache do ASP.NET.
Se o seu componente for executado em um aplicativo ASP.NET, você só precisará incluir uma referência a System.Web.dll no projeto do aplicativo. Quando precisar acessar o cache, utilize a propriedade HttpRuntime.Cache (este objeto também é acessível através de Page.Cache e HttpContext.Cache).
Existem várias regras para armazenar dados em cache. Primeiro, se for provável que os dados sejam usados diversas vezes, esta é uma boa alternativa ao uso do cache. Em segundo lugar, se os dados forem genéricos e não específicos de uma solicitação ou usuário específico, esse também será um bom candidato para armazenamento em cache. Se os dados forem específicos do usuário ou da solicitação, mas tiverem uma vida longa, eles ainda poderão ser armazenados em cache, mas isso poderá não ser usado com muita frequência. Terceiro, uma regra frequentemente esquecida é que às vezes você pode armazenar muito em cache. Normalmente, em um computador x86, para reduzir a chance de erros de falta de memória, você desejará executar processos com no máximo 800 MB de bytes privados. Portanto o cache deve ter um limite. Em outras palavras, você pode reutilizar o resultado de um cálculo, mas se esse cálculo usar 10 parâmetros, você pode estar tentando armazenar em cache 10 permutações, o que pode causar problemas. Uma das solicitações mais comuns de suporte do ASP.NET são erros de falta de memória causados por cache excessivo, especialmente para grandes conjuntos de dados.
O cache tem vários recursos excelentes que você precisa conhecer. Primeiro, o cache implementa um algoritmo usado menos recentemente, permitindo que o ASP.NET force a limpeza do cache - removendo automaticamente itens não utilizados do cache - quando a memória está sendo executada com menos eficiência. Segundo, o cache suporta dependências expiradas que podem ser forçadas a expirar. Essas dependências incluem tempo, chaves e arquivos. O tempo é frequentemente usado, mas com o ASP.NET 2.0, um novo e mais poderoso tipo de invalidação é introduzido: invalidação de cache de banco de dados. Refere-se à exclusão automática de itens do cache quando os dados do banco de dados são alterados. Para obter mais informações sobre invalidação de cache de banco de dados, consulte a coluna Dino Esposito Cutting Edge na MSDN Magazine de julho de 2004. Para entender a arquitetura do cache, veja o diagrama abaixo.
Dica 6 – Processamento em segundo plano
O caminho para o código deve ser o mais rápido possível, certo? Pode haver momentos em que você sinta que uma tarefa executada em cada solicitação ou uma vez a cada n solicitações requer muitos recursos. Enviar e-mails ou analisar e validar dados recebidos são alguns exemplos.
Ao dissecar os Fóruns ASP.NET 1.0 e reprojetar o conteúdo que compõe o servidor da comunidade, descobrimos que a adição de novos caminhos de código de postagem era muito lenta. Cada vez que uma nova postagem é adicionada, o aplicativo primeiro precisa garantir que não há postagens duplicadas, depois deve analisar a postagem usando um filtro de "palavrões", analisar o emoticon do personagem postado, marcar e indexar a postagem e adicionar o postar quando solicitado Vá para a fila apropriada, valide o anexo e, após a postagem final, envie uma notificação por e-mail para todos os assinantes imediatamente. Claramente, há muita coisa envolvida.
Após pesquisa, constatou-se que a maior parte do tempo era gasto na indexação da lógica e no envio de e-mails. A indexação de postagens é uma operação muito demorada e foi descoberto que a funcionalidade System.Web.Mail integrada se conecta a um servidor SMYP e envia e-mails continuamente. À medida que o número de assinantes de uma determinada postagem ou área de tópico aumenta, a função AddPost leva cada vez mais tempo para ser executada.
A indexação de email não é necessária para todas as solicitações. Idealmente, gostaríamos de agrupar essa operação, indexando 25 postagens por vez ou enviando todos os emails a cada cinco minutos. Decidimos usar o código que havíamos usado anteriormente para criar o protótipo da invalidação do cache de dados que foi usada para o que acabou no Visual Studio® 2005.
A classe Timer no namespace System.Threading é muito útil, mas não muito conhecida no .NET Framework, pelo menos entre os desenvolvedores da Web. Uma vez criada, esta classe Timer chamará o retorno de chamada especificado em um intervalo configurável para um thread no ThreadPool. Isso significa que você pode configurar seu código para ser executado sem solicitações recebidas no aplicativo ASP.NET, o que é ideal para processamento em segundo plano. Você também pode realizar operações como indexação ou envio de e-mails neste processo em segundo plano.
No entanto, existem vários problemas com esta tecnologia. Se o domínio do aplicativo for desinstalado, esta instância de timer irá parar de disparar seus eventos. Além disso, como o CLR tem um padrão rígido para o número de threads por processo, pode haver situações em que o servidor esteja muito carregado e o cronômetro possa não ter threads para concluir, o que causará atrasos. O ASP.NET tenta minimizar a chance de isso acontecer, mantendo um certo número de threads disponíveis no processo e usando apenas uma parte do total de threads para processamento de solicitações. No entanto, isso pode ser um problema se você tiver muitas operações assíncronas.
Não há espaço suficiente aqui para este código, mas você pode baixar um exemplo legível em www.rob-howard.net. Confira os slides e demonstrações da apresentação Blackbelt TechEd 2004.
Dica 7 – Cache de saída de página e servidores proxy
ASP.NET é sua camada de apresentação (ou deveria ser sua camada de apresentação); consiste em páginas, controles de usuário, controles de servidor (HttpHandlers e HttpModules) e o conteúdo que eles geram. Se você tem uma página ASP.NET que gera saída (HTML, XML, imagens ou qualquer outro dado) e quando você executa esse código em cada solicitação ele gera a mesma saída, então você tem uma ferramenta que pode ser usada para ASP.NET ótima alternativa para cache de saída de página.
Adicione esta linha de conteúdo ao topo da página <%@ Page OutputCache VaryByParams="none" Duration="60" %>
Você pode gerar com eficiência a saída para esta página uma vez e reutilizá-la várias vezes por até 60 segundos, momento em que a página será executada novamente e a saída será adicionada ao cache do ASP.NET novamente. Esse comportamento também pode ser realizado usando algumas APIs programáticas de baixo nível. Existem várias configurações configuráveis para cache de saída, como a propriedade VaryByParams que acabamos de mencionar. VaryByParams é apenas solicitado, mas também permite especificar parâmetros HTTP GET ou HTTP POST para alterar entradas de cache. Por exemplo, basta definir VaryByParam="Report" para armazenar em cache a saída para default.aspx?Report=1 ou default.aspx?Report=2. Parâmetros adicionais podem ser especificados especificando uma lista separada por ponto e vírgula.
Muitas pessoas não sabem que quando o cache de saída é usado, as páginas ASP.NET também geram alguns cabeçalhos HTTP localizados abaixo do servidor de cache, como aqueles usados pelo Microsoft Internet Security and Acceleration Server ou Akamai. Depois de definir o cabeçalho do cache HTTP, os documentos podem ser armazenados em cache nesses recursos de rede e as solicitações do cliente podem ser atendidas sem retornar ao servidor de origem.
Portanto, usar o cache de saída de página não tornará seu aplicativo mais eficiente, mas poderá reduzir a carga no servidor porque as tecnologias de cache downstream armazenam em cache o documento. É claro que isso pode ser apenas conteúdo anônimo; uma vez downstream, você nunca mais verá essas solicitações e não poderá mais realizar autenticação para impedir o acesso a ele.
Dica 8 — Execute o IIS 6.0 (basta usá-lo para cache do kernel)
Se você não estiver executando o IIS 6.0 (Windows Server? 2003), estará perdendo alguns ótimos aprimoramentos de desempenho nos servidores Web da Microsoft. Na Dica 7, discuti o cache de saída. No IIS 5.0, as solicitações passam pelo IIS e depois pelo ASP.NET. Quando se trata de cache, o HttpModule no ASP.NET recebe a solicitação e retorna o conteúdo do cache.
Se você estiver usando o IIS 6.0, encontrará um pequeno recurso interessante chamado cache do kernel que não requer nenhuma alteração de código no ASP.NET. Quando é feita uma solicitação de cache de saída pelo ASP.NET, o cache do kernel do IIS recebe uma cópia dos dados armazenados em cache. Quando uma solicitação vem de um driver de rede, o driver em nível de kernel (sem mudança de contexto para o modo de usuário) recebe a solicitação, libera os dados armazenados em cache para a resposta, se estiver em cache, e então conclui a execução. Isso significa que ao usar o cache no modo kernel com cache de saída IIS e ASP.NET, você verá resultados de desempenho incríveis. Durante o desenvolvimento do ASP.NET no Visual Studio 2005, fui o gerente de programa responsável pelo desempenho do ASP.NET. Os desenvolvedores fazem o trabalho específico, mas posso ver todos os relatórios que acontecem todos os dias. Os resultados do cache do modo kernel são sempre os mais interessantes. A característica mais comum é que a rede está inundada com solicitações/respostas, enquanto o IIS está rodando com apenas cerca de 5% de uso da CPU. Isso é chocante! É claro que existem outros motivos para usar o IIS 6.0, mas o cache no modo kernel é o mais óbvio.
Dica 9 – Use compactação Gzip
Embora o uso do gzip não seja necessariamente um truque de desempenho do servidor (já que você pode observar um aumento no uso da CPU), o uso da compactação gzip pode reduzir o número de bytes enviados pelo servidor. Isso resulta em um aumento percebido na velocidade da página e redução no uso da largura de banda. Dependendo dos dados que estão sendo enviados, do quanto eles podem ser compactados e se o navegador do cliente os suporta (o IIS só enviará conteúdo compactado com gzip para clientes que suportam compactação gzip, como Internet Explorer 6.0 e Firefox), seu servidor pode servir Mais pedidos. Na verdade, quase sempre que você reduz a quantidade de dados retornados, você aumenta o número de solicitações por segundo.
A compactação Gzip está integrada ao IIS 6.0 e seu desempenho é muito melhor do que a compactação gzip usada no IIS 5.0, o que é uma boa notícia. Infelizmente, ao tentar ativar a compactação gzip no IIS 6.0, talvez você não consiga encontrar a configuração na caixa de diálogo Propriedades do IIS. A equipe do IIS incorporou uma excelente funcionalidade gzip no servidor, mas esqueceu de incluir uma UI administrativa para habilitá-la. Para ativar a compactação gzip, você precisa se aprofundar nas definições de configuração XML do IIS 6.0 (para não ficar com o coração fraco). Aliás, o crédito vai para Scott Forsyth da OrcsWeb por me ajudar a levantar esta questão com o servidor www.asp.net hospedado na OrcsWeb.
Este artigo não descreverá as etapas. Leia o artigo de Brad Wilson em IIS6 Compression. Há também um artigo da base de conhecimento sobre como habilitar a compactação para ASPX em Habilitar compactação ASPX no IIS. Você deve observar, entretanto, que devido a alguns detalhes de implementação, a compactação dinâmica e o cache do kernel não podem existir simultaneamente no IIS 6.0.
Dica 10 – Estado de visualização do controle do servidor
View state é um nome interessante para ASP.NET que armazena alguns dados de estado em campos de saída ocultos da página gerada. Quando a página é postada de volta no servidor, o servidor pode analisar, validar e aplicar esses dados de estado de visualização de volta à árvore de controle da página. Visualizar estado é um recurso muito poderoso porque permite que o estado seja persistido com o cliente e não requer cookies ou memória do servidor para salvar esse estado. Muitos controles de servidor ASP.NET usam o estado de exibição para persistir configurações criadas durante interações com elementos de página, como salvar a página atual exibida durante a paginação de dados.
No entanto, usar o estado de visualização também tem algumas desvantagens. Primeiro, aumenta a carga geral da página sempre que ela é veiculada ou solicitada. A sobrecarga adicional também ocorre ao serializar ou desserializar dados de estado de visualização postados de volta no servidor. Finalmente, o estado de visualização aumenta a alocação de memória no servidor.
Vários controles de servidor tendem a usar excessivamente o estado de visualização, mesmo quando não é necessário, sendo o mais famoso o DataGrid. O comportamento padrão da propriedade ViewState está ativado, mas você pode desativá-lo no nível do controle ou da página se não precisar dele. Dentro do controle, basta definir a propriedade EnableViewState como false ou defini-la globalmente na página usando a seguinte configuração:
<%@ Página EnableViewState="false" %>
Se você não postar de volta a página ou sempre gerar novamente os controles na página em cada solicitação, você deverá desabilitar o estado de visualização no nível da página.
Dei algumas dicas que considero úteis ao escrever aplicativos ASP.NET de alto desempenho. Como mencionei anteriormente neste artigo, este é um guia preliminar e não a palavra final sobre o desempenho do ASP.NET. (Para obter informações sobre como melhorar o desempenho de aplicativos ASP.NET, consulte Melhorando o desempenho do ASP.NET.) A melhor maneira de resolver um problema de desempenho específico só pode ser encontrada por meio de sua própria experiência. No entanto, essas dicas devem fornecer uma boa orientação em sua jornada. No desenvolvimento de software, existem poucos valores absolutos; cada aplicativo é único.