Domanda

Ho un'idea di come posso migliorare le prestazioni con la generazione di codice dinamico, ma non sono sicuro che è il modo migliore per affrontare questo problema.

Supponiamo che ho una classe


class Calculator
{
  int Value1;
  int Value2;
  //.......... 
  int ValueN;

  void DoCalc()
  {
    if (Value1 > 0)
    {
      DoValue1RelatedStuff();    
    }
    if (Value2 > 0)
    {
      DoValue2RelatedStuff();    
    }
    //....
    //....
    //....
    if (ValueN > 0)
    {
      DoValueNRelatedStuff();    
    }
  }
}

Il metodo DoCalc è al livello più basso e si chiama più volte durante il calcolo. Un altro aspetto importante è che ValueN vengono impostati solo all'inizio e non cambiano durante il calcolo. Così molte delle IFS nel metodo DoCalc sono inutili, come molti di ValueN sono 0. Quindi speravo che la generazione di codice dinamico potrebbe contribuire a migliorare le prestazioni.

Per esempio, se creo un metodo


  void DoCalc_Specific()
  {
    const Value1 = 0;
    const Value2 = 0;
    const ValueN = 1;

    if (Value1 > 0)
    {
      DoValue1RelatedStuff();    
    }
    if (Value2 > 0)
    {
      DoValue2RelatedStuff();    
    }
    ....
    ....
    ....
    if (ValueN > 0)
    {
      DoValueNRelatedStuff();    
    }
  }

e compilarlo con le ottimizzazioni acceso il compilatore C # è abbastanza intelligente da solo a mantenere il materiale necessario. Quindi vorrei creare tale metodo in fase di esecuzione in base ai valori di ValueN e utilizzare il metodo generato durante i calcoli.

Credo che potrei usare alberi di espressione per questo, ma gli alberi di espressione funziona solo con semplici funzioni lambda, quindi non posso usare le cose come se, mentre ecc all'interno del corpo della funzione. Quindi, in questo caso ho bisogno di cambiare questo metodo in modo appropriato.

Un'altra possibilità è quella di creare il codice necessario come stringa e compilare dinamicamente. Ma sarebbe molto meglio per me se potevo prendere il metodo esistente e modificarlo di conseguenza.

C'è anche Reflection.Emit, ma io non voglio attaccare con esso in quanto sarebbe molto difficile da mantenere.

A proposito. Non sto limitato alle C #. Quindi sono aperto a suggerimenti dei linguaggi di programmazione che sono più adatti per questo tipo di problema. Fatta eccezione per LISP per un paio di motivi.

Una precisazione importante. DoValue1RelatedStuff () non è una chiamata di metodo nel mio algoritmo. E 'solo un po' di calcoli formula a base ed è abbastanza veloce. Ho dovuto scrivere in questo modo


if (Value1 > 0)
{
  // Do Value1 Related Stuff
}

Ho eseguito alcuni test di performance e posso vedere che con due IFS quando si è disattivato il metodo ottimizzato è di circa 2 volte più veloce rispetto alla ridondante se.

Ecco il codice che ho usato per il test:


    public class Program
    {
        static void Main(string[] args)
        {
            int x = 0, y = 2;

            var if_st = DateTime.Now.Ticks;
            for (var i = 0; i  < 10000000; i++)
            {
                WithIf(x, y);
            }
            var if_et = DateTime.Now.Ticks - if_st;
            Console.WriteLine(if_et.ToString());

            var noif_st = DateTime.Now.Ticks;
            for (var i = 0; i  < 10000000; i++)
            {
                Without(x, y);
            }
            var noif_et = DateTime.Now.Ticks - noif_st;
            Console.WriteLine(noif_et.ToString());

            Console.ReadLine();

        }

        static double WithIf(int x, int y)
        {
            var result = 0.0;
            for (var i = 0; i  < 100; i++)
            {
                if (x > 0)
                {
                    result += x * 0.01;
                }
                if (y > 0)
                {
                    result += y * 0.01;
                }
            }
            return result;
        }

        static double Without(int x, int y)
        {
            var result = 0.0;
            for (var i = 0; i < 100; i++)
            {
                result += y * 0.01;
            }
            return result;
        }
    }
È stato utile?

Soluzione

Io di solito nemmeno pensare a una tale ottimizzazione. Quanto lavoro DoValueXRelatedStuff() fare? Più di 10 a 50 cicli del processore? Sì? Ciò significa che si sta andando a costruire un sistema molto complesso di risparmiare meno del 10% i tempi di esecuzione (e questo sembra abbastanza ottimista per me). Questo può facilmente scendere a meno di 1%.

Non c'è spazio per altre ottimizzazioni? Meglio algoritmi? Un Avete veramente bisogno di eliminare singoli rami di prendere solo un singolo ciclo del processore (se il branch prediction è corretto)? Sì? Non dovresti pensare di scrivere il codice in assembler o qualcos'altro più specifica macchina invece di utilizzare .NET?

Potrebbe dare l'ordine di N, la complessità di un metodo tipico, e il rapporto tra le espressioni di solito valutare a true?

Altri suggerimenti

E mi sorprenderebbe per trovare uno scenario in cui il sovraccarico di valutare il se affermazioni è vale la pena di emettere dinamicamente il codice.

branch prediction e ramo predicato , che rende l'overhead di filiali in piccoli segmenti di approccio codice zero.

Hai provato a riferimento due versioni codificate a mano del codice, quella che ha tutte le se-dichiarazioni in atto, ma fornisce i valori zero per la maggior parte, e uno che rimuove tutti coloro stessi se i rami?

Se siete veramente in ottimizzazione del codice - prima di fare qualsiasi cosa - eseguire il profiler! Essa vi mostrerà dove il collo di bottiglia è e quali aree sono la pena di ottimizzazione.

Anche - se la scelta della lingua non è limitata (ad eccezione di LISP) allora niente batterà assembler in termini di prestazioni;)

Mi ricordo ottenere qualche magia prestazioni riscrivendo alcune funzioni interne (come quello che avete) utilizzando assembler.

Prima di fare qualsiasi cosa, si fa effettivamente hanno un problema

vale a dire. non correre abbastanza a lungo per disturbarla?

Se è così, scoprire che cosa sta realmente prendendo tempo, non ciò che si indovina . Questa è il rapido , il metodo di sporco, e molto efficace lo uso per vedere dove passare del tempo.

Ora, si sta parlando di interpretare contro la compilazione. codice interpretato è in genere di 1-2 ordini di grandezza più lento di codice compilato. La ragione è che gli interpreti sono continuamente cercando di capire cosa fare dopo, e poi dimenticare , mentre il codice compilato solo sa .

Se siete in questa situazione, allora può dare un senso a pagare il prezzo di tradurre in modo da ottenere la velocità di codice compilato.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top