Desenvolvimento - Visual Basic .NET

.Net Framework Inside : Otimizações do Compilador

Veja as otimizações que o compilador do .NET realiza e como tirar proveito delas.

por Guilherme Bacellar Moralez



Os compiladores do .NET Framework implementam uma série de otimizações que são executadas no momento da compilação.

Isso corre para que o programador possa ter mais “clareza” ao ler um fonte e não perder performance durante a execução do mesmo.

Vamos abordar algumas destas otimizações que sendo de nosso conhecimento pode oferecer uma nova abordagem a problemas antigos e facilitar nosso dia a dia como programadores.

Para motivos didáticos os códigos compilados em C# serão obtidos dos códigos fontes em C# e os códigos compilados em VB.Net serão obtidos dos códigos fontes em VB.Net, desta forma poderemos observar se existem diferencias de otimizações entre os compiladores C#.Net e VB.Net.

Constantes

Se algo é uma constante, então, ele “é o que é” e “será sempre o que é”, imutável em sua própria natureza e preso a sua própria e única existência.

Neste espírito o compilador troca todas as chamadas a uma constante pelo próprio valor da constante, evitando assim chamadas desnecessárias a posições de memória que poderiam (caso não acontece-se a troca) existir com o valor.

Fonte Original em C#

const string csvSeparator = ";";

const string endOfLineSeparator = "\n";

string nome = "Guilherme Bacellar Moralez";

int idade = 25;

string sexo = "Masculino";

string dadoEmCSV = nome + csvSeparator + idade.ToString() + csvSeparator + sexo + endOfLineSeparator;

Fonte Original em VB.Net

Sub Main()

Const csvSeparator As String = ";"

Const endOfLineSeparator As String = vbCrLf

Dim nome As String = "Guilherme Bacellar Moralez"

Dim idade As Integer = 25

Dim sexo As String = "Masculino"

Dim dadoEmCSV As String = nome + csvSeparator + idade.ToString() + csvSeparator + sexo + endOfLineSeparator

End Sub

Resultado Compilado em C#

private static void Main(string[] args)

{

string nome = "Guilherme Bacellar Moralez";

int idade = 0x19;

string sexo = "Masculino";

string dadoEmCSV = nome + ";" + idade.ToString() + ";" + sexo + "\n";

}

Resultado Compilado em VB.Net

Public Shared Sub Main()

Dim nome As String = "Guilherme Bacellar Moralez"

Dim idade As Integer = &H19

Dim sexo As String = "Masculino"

Dim dadoEmCSV As String = String.Concat(New String() {nome, ";", idade.ToString, ";", sexo, ChrW(13) & ChrW(10)})

End Sub

Podemos observar que as variáveis que originalmente eram consideras constantes foram eliminadas do código fonte resultante e suas referências foram trocadas pelos próprios valores atribuídos originalmente às constantes.

Remoção de Itens não Usados

Para falar deste item, deixe-me introduzir uma pergunta: Você manteria o seu lixo em casa?

Acredito veementemente que a resposta um sonoro NÃO!!!!!!!

Bom, nem os compiladores do .NET (Atenção!!!!!, estamos falando neste artigo dos compiladores de C# e VB.NET) fazem isso.

Dentro do ciclo de processamento existe a capacidade de remover variáveis e blocos de código que nunca serão utilizados no programa, então, nada mais natural do que remover estes blocos.

Vamos pegar o exemplo acima e fazer uma leve mudança no código.

Fonte Original em C#

private static void Main(string[] args)

{

const string csvSeparator = ";";

const string endOfLineSeparator = "\n";

string nome = "Guilherme Bacellar Moralez";

int idade = 25;

string sexo = "Masculino";

string dadoEmCSV = nome + csvSeparator + idade.ToString() + csvSeparator + sexo;

if (dadoEmCSV.Equals("Guilherme Bacellar Moralez;25;Masculino"))

{

return;

}

dadoEmCSV += endOfLineSeparator;

}

Fonte Original em VB.Net

Sub Main()

Const csvSeparator As String = ";"

Const endOfLineSeparator As String = vbCrLf

Dim nome As String = "Guilherme Bacellar Moralez"

Dim idade As Integer = 25

Dim sexo As String = "Masculino"

Dim dadoEmCSV As String = nome + csvSeparator + idade.ToString() + csvSeparator + sexo

If (dadoEmCSV.Equals("Guilherme Bacellar Moralez;25;Masculino")) Then

Exit Sub

End If

dadoEmCSV = dadoEmCSV + endOfLineSeparator

End Sub

Resultado Compilado em C#

private static void Main(string[] args)

{

string nome = "Guilherme Bacellar Moralez";

int idade = 0x19;

string sexo = "Masculino";

string dadoEmCSV = nome + ";" + idade.ToString() + ";" + sexo;

if (!dadoEmCSV.Equals("Guilherme Bacellar Moralez;25;Masculino"))

{

dadoEmCSV = dadoEmCSV + "\n";

}

}

Resultado Compilado em VB.Net

Public Shared Sub Main()

Dim nome As String = "Guilherme Bacellar Moralez"

Dim idade As Integer = &H19

Dim sexo As String = "Masculino"

Dim dadoEmCSV As String = String.Concat(New String() {nome, ";", idade.ToString, ";", sexo})

If Not dadoEmCSV.Equals("Guilherme Bacellar Moralez;25;Masculino") Then

dadoEmCSV = (dadoEmCSV & ChrW(13) & ChrW(10))

End If

End Sub

Uma vez detectado que, existe uma linha de código que nunca será utilizada no código fonte, o compilador remove esta linha do Build final. De quebra, foi removida a linha que declarava a constante “endOfLineSeparator”, afinal, o único ponto em que ela seria utilizada nunca será chamado pelo código fonte.

Otimizações Aritméticas

1 + 1 = 2

2 + 2 = 4

((1 + 2) * 3) * (99 / (3 * 4)) = 74,25

Um computador é (no fundo) uma máquina de calcular extremamente sofisticada, então, o que ele sabe fazer de melhor é realizar operações matemáticas.

Da mesma forma os compiladores conseguem realizar otimizações de formulas e cálculos de nosso código para evitar que sejam executadas operações de forma desnecessária.

Fonte Original em C#

private static void Main(string[] args)

{

int idadeInicial = 25;

double modificadorIPrevidencia = 0.69 + 1;

double modificadorIIPrevidencia = 9.9999 - 0.001 ;

double modificadorIIIPrevidencia = modificadorIPrevidencia*modificadorIIPrevidencia;

double valorCotaPrevidencia = 0.2236977;

double totalValorInvestido = 1000000;

double totalCotas;

totalCotas = (totalValorInvestido/valorCotaPrevidencia)* ((modificadorIIPrevidencia/(modificadorIPrevidencia/idadeInicial)))*modificadorIIIPrevidencia;

}

Fonte Original em VB.Net

Sub Main()

Dim idadeInicial As Integer = 25

Dim modificadorIPrevidencia As Double = 0.69 + 1

Dim modificadorIIPrevidencia As Double = 9.9999 - 0.001

Dim modificadorIIIPrevidencia As Double = modificadorIPrevidencia * modificadorIIPrevidencia

Dim valorCotaPrevidencia As Double = 0.2236977

Dim totalValorInvestido As Double = 1000000

Dim totalCotas As Double

totalCotas = (totalValorInvestido / valorCotaPrevidencia) * ((modificadorIIPrevidencia / (modificadorIPrevidencia / idadeInicial))) * modificadorIIIPrevidencia

End Sub

Resultado Compilado em C#

private static void Main(string[] args)

{

int idadeInicial = 0x19;

double modificadorIPrevidencia = 1.69;

double modificadorIIPrevidencia = 9.9989;

double modificadorIIIPrevidencia = modificadorIPrevidencia * modificadorIIPrevidencia;

double valorCotaPrevidencia = 0.2236977;

double totalValorInvestido = 1000000.0;

double totalCotas = ((totalValorInvestido / valorCotaPrevidencia) * (modificadorIIPrevidencia / (modificadorIPrevidencia / ((double)idadeInicial)))) * modificadorIIIPrevidencia;

}

Resultado Compilado em VB.Net

Public Shared Sub Main()

Dim idadeInicial As Integer = &H19

Dim modificadorIPrevidencia As Double = 1.69

Dim modificadorIIPrevidencia As Double = 9.9989

Dim modificadorIIIPrevidencia As Double = (modificadorIPrevidencia * modificadorIIPrevidencia)

Dim valorCotaPrevidencia As Double = 0.2236977

Dim totalValorInvestido As Double = 1000000

Dim totalCotas As Double = (((totalValorInvestido / valorCotaPrevidencia) * (modificadorIIPrevidencia / (modificadorIPrevidencia / CDbl(idadeInicial)))) * modificadorIIIPrevidencia)

End Sub

Observemos que ocorreram algumas otimizações:

· A variável que é inteira foi convertida para notação em Hexadecimal (0x19) representa o número 25.

· As variáveis “modificadorIPrevidencia” e “modificadorIIPrevidencia” tiveram seus valores somados automaticamente pelo compilador e o resultado foi atribuído á variável. De certa forma, esse calculo resulta em um “constante”.

· Observe que a formula matemática foi “levemente” alterada, mas, a alteração visa apenas performance não alterando o resultado final.

Otimizações de Declaração de Variáveis

Código fonte mais “legível” não significa necessariamente código fonte mais lento.

Em diversas ocasiões trabalhei com equipes que me diziam que declarar uma variável e depois atribuir o valor a ela (em linhas diferentes) era mais lento do que declarar a variável e atribuir o valor no momento da declaração.

Nada melhor do que decompilar para aprender, então...

Fonte Original em C#

private static void Main(string[] args)

{

// Declara as Constantes

const string csvSeparator = ";";

const string endOfLineSeparator = "\n";

// Declara as Variáveis

string nome;

int idade;

string sexo;

string dadoEmCSV = "";

string dadoLoop;

// Preenche as Variáveis

sexo = "Masculino";

nome = "Guilherme Bacellar Moralez";

idade = 25;

for (int i = 0; i <= idade; i++)

{

dadoLoop = nome;

dadoLoop += csvSeparator;

dadoLoop += idade.ToString();

dadoLoop += csvSeparator;

dadoLoop += sexo;

dadoLoop += endOfLineSeparator;

dadoEmCSV += dadoLoop;

}

}

Fonte Original em VB.Net

Sub Main()

" Declara as Constantes

Const csvSeparator As String = ";"

Const endOfLineSeparator As String = "\n"

" Declara as Variáveis

Dim nome As String

Dim idade As Integer

Dim sexo As String

Dim dadoEmCSV As String = ""

Dim dadoLoop As String

" Preenche as Variáveis

sexo = "Masculino"

nome = "Guilherme Bacellar Moralez"

idade = 25

For i As Integer = 0 To idade

dadoLoop = nome

dadoLoop += csvSeparator

dadoLoop += idade.ToString()

dadoLoop += csvSeparator

dadoLoop += sexo

dadoLoop += endOfLineSeparator

dadoEmCSV += dadoLoop

Next

End Sub

Resultado Compilado em C#

private static void Main(string[] args)

{

string dadoEmCSV = "";

string sexo = "Masculino";

string nome = "Guilherme Bacellar Moralez";

int idade = 0x19;

for (int i = 0; i <= idade; i++)

{

string dadoLoop = nome;

dadoLoop = ((dadoLoop + ";") + idade.ToString() + ";") + sexo + "\n";

dadoEmCSV = dadoEmCSV + dadoLoop;

}

}

Resultado Compilado em VB.Net

Public Shared Sub Main()

Dim dadoEmCSV As String = ""

Dim sexo As String = "Masculino"

Dim nome As String = "Guilherme Bacellar Moralez"

Dim idade As Integer = &H19

Dim VB$t_i4$L0 As Integer = idade

Dim i As Integer = 0

Do While (i <= VB$t_i4$L0)

Dim dadoLoop As String = nome

dadoLoop = (((dadoLoop & ";") & idade.ToString & ";") & sexo & "\n")

dadoEmCSV = (dadoEmCSV & dadoLoop)

i += 1

Loop

End Sub

Vamos observar atentamente as mudanças realizadas

  • As variáveis que foram declaradas e atribuídas posteriormente foram agrupadas;
  • A variável “dadoLoop” que foi declarada fora do loop foi transferida pelo compilador para dentro do loop;
  • As diversas linhas de concatenação do loop foram substituídas por apenas 1 linha;

Conclusões

A que conclusões podemos chegar:

Bom, eu cheguei a 2 delas:

1-) Existem diversas formas de se obter o mesmo resultado. O compilador otimiza para o melhor resultado.

2-) Código mais “legível” não significa código mais lento.

Guilherme Bacellar Moralez

Guilherme Bacellar Moralez - Bacharel em Ciências da Computação, Desenvolvedor .NET há 4 anos, MCAD, Autor de Livros e Artigos, Arquiteto de Software do Projeto D.NET (Framework de Desenvolvimento .NET).
Blog:
http://dotnetmax.bacellar.org/