Por que os meus executáveis Delphi são tão grandes?
Escrito por Carlos B. Feitoza Filho | |
Categoria: Artigos | |
Categoria Pai: Addicted 2 Delphi! | |
Acessos: 14406 |
Houve um tempo em que memória RAM e de armazenamento eram muito caras e os programadores precisavam se desdobrar para que seus programas se mantivessem dentro de limites rígidos, impostos pelo hardware da época.
Naquele tempo não havia muita coisa a se fazer para manter os programas menores do que eles já eram. Os compiladores normalmente geravam executáveis que eram capazes de rodar nas limitadas máquinas das décadas de 1980 e 1990. Caso alguma máquina tivesse limites ainda mais rígidos, a saída era trocar de linguagem, no caso, normalmente o Assembly era a solução, pois ele era (a ainda é) a última fronteira entre um ser humano e o hardware.
Ao programar em Assembly o tamanho do executável gerado e seus requisitos de memória são, por assim dizer, dependentes exclusivamente da técnica que o programador possui. Naquele tempo existiam até mesmo competições entre grupos de demoscenes. Nestas competições os programadores extraíam toda a capacidade dos computadores da época usando Assembly puro para desenvolver executáveis minúsculos com capacidades extraordinárias que, normalmente, só se conseguem utilizando dezenas de frameworks e bibliotecas.
Um dos grupos que mais se destacou nesta época foi o Razor 1911. Vários de seus assim chamados "demos", podem ser vistos no site especializado em demos, Pouet. O site oficial deste grupo é este. E o meu demo favorito se chama "Pirates of the 777 Seas", o qual pode ser baixado no site do grupo. Este demo é um executável com mízeros 80KB. Execute-o e descubra a desproporcionalidade entre seu tamanho e o que ele faz. Eis o link direto.
O que há com os Delphis de hoje em dia?
Hoje em dia não temos mais limitações nos computadores e a capacidade dos mesmos cresce a cada dia que passa, tornando a necessidade de programação absurdamente enxuta desnecessária, no entanto, se você é um programador atento e já programa no Delphi há algum tempo, deve ter notado que o tamanho dos executáveis gerados está muito maior do que deveria ser.
Eu não teria percebido o aumento drástico de tamanho dos executáveis se eu não estivesse codificando um programa multidelphi. Comecei por desenvolver algo no Delphi XE e em seguida abri o projeto no Delphi 2006, mundos completamente diferentes. Notei que o executável gerado pelo Delphi 2006 tinha cerca de 2MB e que o executável gerado no XE tinha 15MB, sendo que ambos estavam compilando rigorosamente o mesmo código!
Achei isso muito absurdo e resolvi fazer uma investigação que não surtiu qualquer efeito até que eu fiz o caminho oposto: criei um projeto no Delphi 2006 e compilei ele no Delphi XE e minha surpresa foi que, desta vez, ambos tinham praticamente o mesmo tamanho (2MB). O que aconteceu foi que, ao abrir o projeto do Delphi 2006 no Delphi XE, este criou um arquivo de projeto novo (.dproj) baseado no arquivo de projeto antigo (.bdsproj) o qual não tem todas as opções disponíveis no arquivo de projeto novo.
Depois dessa constatação comecei a desconfiar das opções do projeto e resolvi bolar um meio de descobrir facilmente qual configuração inchava os binários:
- Criei um projeto novo (uma aplicação de console) no Delphi XE e comprovei que o executável gerado vazio, ficava com cerca de 1MB. Ao se criar um projeto novo no Delphi XE, o mesmo deve criar o arquivo de projeto com opções padrão, e dentre elas estava incluída a opção que torna os binários gigantescos;
- Criei um projeto novo (uma aplicação de console) no Delphi 2006 e comprovei que o executável gerado vazio ficava com cerca de 130KB!;
- Abri no Delphi XE o projeto do Delphi 2006 e após a compilação constatei que o executável permanecia com cerca de 130KB;
Após os 3 passos acima, eu estava de posse de um projeto de Delphi XE que gerava o executável de 1MB e outro projeto de Delphi XE que gerava o mesmo executável com 130KB. Comparei os dois arquivos e finalmente descobri a configuração, a qual eu apresento na figura abaixo:
Ao desabilitar esta opção, não serão incluídas informações de depurações pelo linker e isso tornará os binários gerados até 10x menores em Delphis que possuem esta opção. Ao contrário do que possa parecer, esta ação não impossibilita a depuração local de um programa, o qual continua "debugável" normalmente!
Falando em debugável, ao se configurar o alvo da construção do executável como RELEASE, esta opção é automaticamente desabilitada e o executável se torna "de tamanho normal", no entanto, até o presente momento ao menos pra mim, mesmo em modo DEBUG esta opção é inútil
Afinal, para que serve esta configuração?
Após algumas pesquisas a respeito da utilidade da opção Debug Information, nas configurações do Linker não consegui achar explicações práticas a respeito da utilidade da mesma. Foi então que, conversando com Régys Borges (https://regys.com.br/sobre-mim/), o mesmo desconfiou que esta opção serviria para a realização de depurações remotas, recurso muito útil, mas pouco utilizado pela maioria de nós, programadores Delphi. Régys realizou testes rápidos tomando como base a teoria a respeito da depuração remota e chegou aos seguintes resultados empiricamente:
A opção Debug Information existente na seção Compiling das opções de um projeto, atua na depuração local de um programa, isto é, quando você está no uso normal do Delphi, depurando seus programas da forma mais simples que se conhece, é esta opção que precisa estar habilitada, pois localmente temos acesso direto a todos os arquivos intermediários que são gerados no momento da compilação (.dcu). De fato, ao habilitar a opção Debug Information existente na seção Compiling, todos os arquivos .dcu do projeto ficam ligeiramente maiores, pois estes contém informação necessária para a realização da depuração.
Por outro lado, ao se realizar uma depuração remota, o executável não dispõe das units compiladas (arquivos .dcu) e por isso ele precisa embutir em si mesmo todas as informações de depuração necessárias. Ao habilitar a opção Debug Information da seção Linking, estas informações são incluídas e a depuração remota se torna possível.
Devo parabenizar aqui o Régys, pois ele desvendou algo que nem mesmo a ajuda do Delphi explicava. Imagino que esta opção existia há tanto tempo que a Embarcadero "supôs" que não precisasse mais deixar claro seu uso. Só quem já a usou desde o começo sabia de sua utilidade.
Pouco tempo antes do Régys realizar os testes que eu mencionei, outro escovador de bits havia levantando outra hipótese a respeito do uso desta opção. Ele ficou de, também, realizar alguns testes a fim de comprovar sua tese e, pouco tempo depois de eu receber o resultado do Régys ele me trouxe aquilo que eu precisava: detalhes! O outro herói se chama Victor Hugo Gonzales que atende pelo codinome de Pandaaa.
Além de possibilitar a depuração remota, esta configuração também tem um outro uso, bem mais comum do que a depuração, mas ainda assim pouco usado por grande parte de nós programadores. Trata-se de algo que justifica o nome da propriedade (Debug Information): incluir no binário informações completas a respeito do mesmo. Símbolos globais do programa, arquivos-fonte e suas linhas são as informações salvas, as quais permitem, por exemplo, que quando houver uma exceção na aplicação, seja possível localizar o método, o nome do arquivo-fonte e a linha no mesmo!
Existem algumas bibliotecas que tiram proveito desta informação e são capazes de exibí-las, de maneira muito elegante, ao usuário final, o qual poderá copiar ou tirar um screenshot com o intuito de enviá-lo ao desenvolvedor. Vou citar duas das bibliotecas mais conhecidas. Uma delas chama-se Eureka Log (paga) e a outra chamas MadExcept (gratuita).
Quando se desabilita a opção Debug Information, na seção Linking não haverá meios de exibir as informações detalhadas a respeito de uma exceção, veja:
Como se pode observar, o Eureka Log não informa nada além de um call stack genérico com apenas os endereços de memória que pouco são úteis. Ao habilitar a opção entretanto, o detalhamento é completo:
Quando o Pandaaa me falou o resultado dos testes a primeira coisa que fiz foi questionar a respeito do porquê com Delphis antigos eu conseguia usar o Eureka Log da mesma forma, mas sem que o executável ficasse inchado. Por exemplo, no Delphi 2006 não existe a opção Debug Information, na seção Linking e mesmo assim eu já usei o Eureka Log para trazer as mesmas informações detalhadas.
Afinal, o que houve com o Delphi? A Embarcadero cagou o bom trabalho da Borland? Na verdade não, ela de fato melhorou o uso de um recurso essencial para que as informações detalhadas de uma exceção sejam exibidas: os arquivos .map! Estes arquivos guardam toda informação necessária que é utilizada pelas bibliotecas de exibição de call stack e exceções detalhadas e precisam estar disponíveis, normalmente no mesmo local onde o executável está, por exemplo, se eu tenho meuexe.exe, eu deveria ter meuexe.map no mesmo diretório, para que o Eureka Log funcione corretamente.
O que acontece atualmente é que o conteúdo do arquivo .map pode estar ou não embutido no binário (executável). E foi assim que chegamos a mais um veredicto para a utilidade da opção Debug Information da seção Linking:
Ao habilitar a opção Debug Information da seção Linking, todas as informações que estariam contidas no arquivo .map são embutidas no executável, dispensando a criação de um arquivo .map separado.
Ao desabilitar esta opção, e consequentemente diminuindo o tamanho do executável, caso queiramos exibir informações de exceção detalhadas, precisaremos criar um arquivo .map obrigatoriamente o qual deve estar no mesmo diretório da aplicação compilada.
Parabéns Victor Hugo Gonzales (Pandaaa). Você e o Régys ajudaram a tornar este artigo muito mais explicativo do que eu esperava!
Conclusão
Sabemos que atualmente temos praticamente espaço de armazenamento ilimitado e memória RAM relativamente barata, no entanto, como se pôde observar, os 90% a mais de tamanho quando esta configuração está ativa, são absolutamente inúteis para a maioria dos programadores.
É evidente que esta configuração tem suas utilidades, mas tenho certeza que pouca gente a usa, portanto, por que não gerar os nossos executáveis com um tamanho aceitável mesmo em modo DEBUG? Não é um hack, não é nada mirabolante, é apenas uma única configuração e o melhor: é de graça!