Escrito em September 17th, 2008 as 7:13 pm por Guilherme Bacellar

1 Comentário

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 (0×19) 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:

  • Existem diversas formas de se obter o mesmo resultado. O compilador otimiza para o melhor resultado.
  • Código mais “legível” não significa código mais lento.
  • Posts Relacionados:

    1. Reflection – Parte 4 (Propriedades)
    2. Por Dentro das Propriedades Automáticas
    3. .Net Framework Inside : Enumeradores (Enums)
    4. .Net Framework Inside : Strings (System.String)
    5. .Net Framework Inside : On Error Resume Next (Anjo ou Demónio)?
    6. Tipos Nulos (Nullable Value Types)
    7. Reflection – Parte 3 (Fields)
    , , , ,

    1 Reposta to “.Net Framework Inside : Otimizações do Compilador”

    1 Trackbacks For This Post

    1. Fique por dentro Compilador » Blog Archive » .Net Framework Inside : Otimizações do Compilador Says:

      [...] ao ler um fonte e não perder performance durante a execução … fique por dentro clique aqui. Fonte: [...]

    Deixa uma Resposta

    znjdb32s6g