ASP.NET Tunning

Boa noite pessoal, este post é um apanhado de dicas de como aumentar a performance de suas aplicações ASP.NET. Todas foram testadas na prática por mim no projeto do Busca Acelerada, que de tão rápido que é, o pessoal nunca acredita que é feito em ASP.NET, hehehe. Muitas das dicas são para quem programa com WebForms, pois eu não utilizo ASP.NET MVC em meus projetos. É muito provável também que você não poderá utilizar todas essas dicas devido à natureza de sua aplicação, eu uso todas, mas o Busca Acelerada é um projeto bem fora do comum mesmo, então não é parâmetro. Ou seja, nem tudo que eu disser aqui vai se aplicar ao seu caso. Sugiro usar este post em conjunto com os outros dois que tem aqui no blog sobre como aumentar o desempenho de websites.

E por último, pretendo atualizar este post periodicamente conforme for encontrado mais dicas por aí ou for descobrindo coisas novas por minha conta mesmo.

Session

Todo mundo já deve ter ouvido falar mal do uso de sessões em ASP.NET. A sessão é uma área de memória no servidor alocada para cada usuário utilizando seu sistema. Além de um tempo de expiração definido na aplicação e/ou no servidor, a sessão é “apagada” quando o usuário fecha o navegador. Como qualquer coisa salva na memória do servidor, algo extremamente concorrido e limitado, deve ser usado com parcimônia. Ou melhor, não use.

No Busca Acelerada adotamos a política de não utilizar sessão. Em casos raros utilizamos cookies na máquina do cliente ou simplesmente passamos as informações via QueryString. Para deixar de utilizar sessão, não basta não salvar dados na Session, mas sim deve marcar suas páginas com o atributo EnableSessionState=”False”, na diretiva Page. Se você tem a mania de usar sessão apenas para passar um dado de uma página para outra (um ID para editar um cliente, por exemplo), considere passar esta informação via querystring e poupe a memória do seu servidor.

View-State

A menina dos olhos do pessoal que gosta de WebForms e o terror dos webdesigners. O ViewState garante que entre um PostBack e outro, os seus dados ainda estarão persistidos, salvando e criptografando todos em um hidden field no seu HTML. Quanto mais server controls com runat=”server” você possuir em seu ASPX, maior será o tamanho do seu view-state e consequentemente o tempo de download da página. Mais que isso, processar grandes viewstates consome tempo de processamento do servidor, principalmente se você parar pra pensar que este dado é criptografado.

Aqui temos dois ajustes a serem considerados: tamanho do arquivo (e consequente tempo de download) e processamento do servidor. Eu abracei os dois e não utilizamos view-state no Busca Acelerada. Caso tenha que utilizar view-state no seu projeto, mas os campos do seu formulário não possuem dados sensíveis (i.e. sigilosos) ou sua aplicação está sobre SSL, desabilite a criptografia do Viewstate na diretiva page do seu ASPX com o atributo ViewStateEncryptionMode=”Never”. Isso irá diminuir o processamento no servidor.

Caso possa abrir mão completamente do view-state, adicione o atributo ViewStateMode=”Disabled” na diretiva Page, o que vai reduzir o tamanho do HTML gerado. Caso não possa fazer isso pois precisa manter o estado entre os Postbacks, tente reduzir o tamanho do seu ViewState com as seguintes dicas:

  • se o texto jamais é alterado via C#, jamais use uma asp:Label para exibi-lo. Prefira a tag label do HTML ou apenas escreva literalmente na página.
  • use o menor número possível de ASP Controls no seu formulário e elementos com runat=”server”
  • desabilite o viewstate para controles que você não precisa manter estado entre postbacks, como botões e repeaters por exemplo. Use a propriedade ViewStateMode do controle para desativá-lo.

Event Validation

O ASP.NET por padrão é um dos frameworks web mais seguros da atualidade. Por padrão ele já protege contra injeções de script em formulários, por exemplo. Caso você já faça a sua própria validação no lado do servidor e/ou possua outros mecanismos de proteção para isso, considere desabilitar a validação de eventos na diretiva page de suas páginas com o atributo EnableEventValidation=”False”. Note que EventValidation é completamente inútil em páginas que não possuam campos de entrada de dados.

Web.Config

Muitas são as dicas de otimização de performance que podem ser feitas diretamente no web.config. Além de várias das dicas anteriores, que podem ser feitas a nível de página ou de aplicação, tem algumas que são exclusivas deste arquivo. A saber:

  • se sua aplicação não usa nenhum tipo de autenticação, declare o authentication mode como none. Remover a tag não adianta, pois o ASP.NET assumirá o padrão como Windows, que consumirá processamento do servidor durante as requisições de página.
  • em produção, declare  para economizar processamento e memória no servidor, desabilitando o recurso do ASP.NET de registrar todos os “movimentos” dos usuários para uma melhor depuração, visto que em produção não é lugar para depuração!
  • em produção, use compilation em debug=”false”. Você notará que por padrão o web.config já possui uma tag compilation. Substitua ela por essa quando em produção, para diminuir o consumo de memória e o tempo de compilação da aplicação, definindo que não deverá manter informações para debug em memória e já dizendo exatamente a linguagem e framework de sua aplicação.
  • use sessionState em mode=”off” caso queira desabilitar a sessão para toda sua aplicação, reduzindo o consumo de memória no servidor ou  para definir várias configurações de página ao mesmo tempo.
  • use customErrors sempre como On ou RemoteOnly em produção, e aqui o motivo não é performance, mas sim segurança.

Web Services

Ao usar Webservices em sua aplicação ASP.NET, prefira pelos padrões mais modernos como o WCF e sempre que possível, faça-os sobre o protocolo REST, ao invés de SOAP. O intuito do post não é ensinar como se faz webservices de alto desempenho, mas vale lembrar que SOAP utiliza XML, o que cria arquivos maiores e processamento desnecessário nos casos em que não se precisa manter estado ou verificações de integridade. O REST é um padrão mais simples, leve e seguro se bem utilizado, como sobre SSL, por exemplo. Pense nisso, irá diminuir o tempo de comunicação com o webservice.

ASP.NET Ajax

Evite sempre que possível de utilizar o ASP.NET Ajax. Ao invés disso, prefira o uso de JQuery e suas funções load, post, getJSON, entre outras, que fazem muito bem o seu papel e removem overheads desnecessários na sua aplicação, como aumento de viewstate, por exemplo. Nem vou citar o famigerado Ajax Control Toolkit, simplesmente não use ele.

Javascript

Use Javascript sempre que possível para solucionar problemas que não envolvem segurança. Isso irá diminuir seus postbacks e aumentará a velocidade das páginas, melhorando a experiência dos usuários. Muitos desenvolvedores ASP.NET odeiam Javascript, e eu já fui um deles. Coloque na sua cabeça que é muito importante aprendê-lo principalmente para distribuir o processamento entre os usuários ao invés de acumular tudo no seu servidor, sobrecarregando-o. Frameworks como JQuery e Bootstrap ajudam bastante a romper esse estigma do Javascript e são muito produtivos.

Generic Handler

Sempre que você precisar fazer uma comunicação de um código Javascript com o banco de dados ou com algum código C# dentro da mesma aplicação, prefira o uso de Generic Handler (.ashx) a qualquer outro método. O Generic Handler permite tratar requisições feitas de qualquer fonte (JS por exemplo) e permite retornar qualquer tipo de dado sem toda a pilha de execução de uma página ASP.NET tradicional. Quer carregar uma tabela do banco via JS? Chama um generic handler retornando uma lista de JSON. Quer salvar um dado JS no banco ou na sessão? Chama um Generic handler. Quer fazer uma página que a partir de um ID da URL baixa um arquivo? Chama um Generic handler, e por aí vai.

Para retorno de objetos a partir de um Generic Handler, sugiro o uso de JSON e somente em último caso XML, por ser mais pesado.

Postbacks

Evite postbacks sempre que possível. Postbacks geram processamento no servidor, sem contar o tempo dispendido trafegando dados pela rede. Quando postbacks são algo inevitável (botão de salvar formulário, por exemplo), tenha certeza de que no evento Page_Load da sua página existe uma verificação “Page.IsPostback == false” ou similar, para garantir que a página inteira não será carregada novamente para logo em seguida ter a ação do botão executada. Lembre-se que no ciclo de vida de uma página ASP.NET, o Page_Load acontece antes do que qualquer evento Click.

Output-Cache

Essa dica eu não uso, mas sei de vários casos em que foi utilizado e deu grandes melhorias. Eu não uso pelo simples fato de que memória é um recurso valioso para mim e processamento eu tenho sobrando no servidor, ou seja, a principal vantagem do Output-Cache, que é reduzir o processamento das páginas ASPX em páginas HTML tradicionais para serem exibidas no browser, não me serve pra muita coisa, além do fato de que o Busca Acelerada é atualizado em real-time, o que tornaria as páginas armazenadas em cache obsoletas rapidamente.

Se você tem memória sobrando no servidor ou possui páginas que são lentas para processar e quer melhorar seu desempenho, dê uma estudada sobre Output-Cache, que armazena as páginas HTML geradas pela sua aplicação na memória do servidor, evitando retrabalho.

Cache

Caso deseje armazenar dados na memória do servidor de uma forma mais gerenciável que na Session, aumentando o desempenho de sua aplicação, Cache é a solução. Não ensinarei aqui macetes de como usar cache, mas basta saber que é semelhante ao uso de Session mas é possível definir prazos de expiração para seu conteúdo baseados em tempo, em falta de uso por parte dos usuários ou até mesmo vincular a vida de um objeto em cache ao estado de uma arquivo ou pasta de arquivos.

Repeaters, ListViews, etc

Sempre pagine. Óbvio não?! Sempre que for trazer mais do que poucas dezenas de registros, pagine. E a paginação deve ser feita a nível de servidor, e não no lado do cliente. Um erro comum é os desenvolvedores quererem usar fantásticos paginadores escritos como plugins JQuery que puxam todos os dados do banco de uma vez só e depois apenas exibem de forma paginada, sendo que todos os dados tiveram de ser retornados e foram escritos no HTML. Isso é ruim do ponto de vista de performance e use apenas se for com poucos dados.

Outra dica ao usar estes controles é usar o mínimo possível de botões. Se você colocou um botão que leva o usuário para uma tela de edição com um ID como CommandArgument, porque não fazer simplesmente uma âncora (tag “a”) que leva para a tela de edição passando o ID como querystring? Muito mais leve e 100% funcional, evitando um postback desnecessário.

Evite o uso de DataBinder.Eval, pois ele usa Reflection para escrever os dados na tela. Ao invés disso, prefira fazer casting explícito sobre o Container.DataItem e chamar a propriedade que deseja.

Além disso, muito cuidado com os eventos ItemDataBound, que são executados uma vez para cada elemento que será apresentado na tela. Ele pode se tornar um imenso gargalo, principalmente se realizar consultas no banco de dados. O ItemDataBound é uma boa pedida se o seu controle de repetição possui muitas colunas, neste caso, pode ser mais eficiente montar uma string no ItemDataBound e escrever na tela com Response.Write, pois o eval do objeto será feito apenas uma vez dessa forma.

Redirecionamentos HTTP

Sempre que possível, use Server.Transfer para redirecionamentos dentro de uma mesma aplicação. Deixe o Response.Redirect somente para os casos de redirecionar para uma URL externa. E sob o ponto de vista de SEO, use o RedirectPermanent para avisar quando esse redirecionamento é para sempre.

Global.asax

Em aplicações que possuem tal arquivo, tome muito cuidado com o que você faz no mesmo, pois cada requisição feita à sua aplicação (o carregamento de uma imagem, um postback, um acesso pelo browser, etc) invocará este arquivo. Então se pretende colocar alguma lógica de aplicação neste arquivo, faça os testes corretos para garantir que você não efetuará sua lógica sobre requisições inofensivas, como imagens, JS e CSS, por exemplo. Libere tais requisições verificando a extensão de arquivo da requisição, quando houver.

Páginas Grandes e Complexas

Evite criar páginas ASPX muito grandes, especialmente com MultiViews cheias de Views ou páginas cheias de User Controls. Mesmo que a renderização do HTML seja dinâmica e alguns conteúdos não serão exibidos, o servidor dispenderá tempo processando esta página para gerar o HTML correto. prefira criar páginas separadas e menores para aumento de performance. Também evite uma hierarquia muito grande controles ou páginas, como múltiplas MasterPages ou User Controls dentro de User Controls.

Publicação

Aqui seguem algumas dicas de publicação do seu sistema em produção para que aumente a performance e reduza o consumo de memória:

  • compile como Release. Desta forma, suas DLLs ficarão menores, com até 15% de ganho em DLLs grandes. isso reduzirá o tempo de publicação (FTP) e o tempo de compilação da mesma no servidor, fora outros ganhos menores no próprio uso da aplicação.
  • compile para a plataforma de destino. Por padrão o ASP.NET mantém compatibilidade com servidores x32 e x64 sem nenhum ajuste. Entretanto, está cada vez mais comuns servidores com Windows Server 2008 R2, que por padrão, não existe em evrsão 32-bit. Dessa forma, porque manter uma compatibilidade que gera um overhead na compilação sem necessidade? Ajuste a arquitetura de compilação de seu projeto para x64 nas propriedades do mesmo, mas somente na configuração de Build para Release, para evitar problemas com sua máquina local. isso lhe trará ganhos de performance.

Manutenção

Mesmo que sua aplicação ASP.NET esteja pronta e publicada com sucesso, existem algumas dicas para melhorar a performance dela no servidor. Uma delas é reiniciar o seu application pool no IIS com certa frequencia, podendo inclusive deixar isso configurado no próprio pool. Reiniciar o pool limpa completamente a memória da aplicação, eliminando qualquer recurso indesejado que pode ter ficado pra trás e iniciando um novo processo em perfeitas condições de uso. Eu costumo fazer isso no máximo a cada 2 dias.

Conclusões

É muito provável que você faça essas alterações, mande rodar sua aplicação e…não veja diferença. Isso é normal. Cada uma dessas melhorias lhe dará milisegundos de ganho. A vantagem somente se dará quando atingir um volume de requisições, com vários usuários utilizando sua aplicação ao mesmo tempo, pois vários milisegundos virarão segundos, depois minutos e por aí vai. Um sistema ASP.NET bem construído pode ser tão veloz ou até mais do que a maioria das aplicações feitas com frameworks PHP, por exemplo, basta se estudar para isso.

Nos próximos posts sobre Tunning irei abordar otimizações de performance para sistemas escritos em C# e otimizações para modelagem e uso de bancos MS SQL Server. Ou seja, ao final desta série de posts (que começou com os posts sobre melhorias de performance em websites comuns) você terá em mãos muitas das técnicas que utilizo para que o Busca Acelerada consiga honrar o seu nome.

E você, conhece alguma melhoria de ASP.NET que já tenha utilizado e que não listei aqui?

Publicado por

Luiz Duarte

Pós-graduado em computação, professor, empreendedor, autor, Agile Coach e programador nas horas vagas.