Dev (Back & Front)ARTIGO

.Net Framework Inside: Otimizações do Compilador

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 diferenças 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 acontecesse 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. Logo, 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 fórmulas 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.

é Bacharel em Ciências da Computação, Desenvolvedor .NET há 6 anos, MCAD, Autor de Livros e Artigos, Engenheiro de Segurança de Internet Banking e membro da Kontrolu. WebSite: http://www.kontrolu.com.br

Ver perfil