Question

J'ai une idée de la façon dont je peux améliorer les performances avec la génération de code dynamique, mais je ne suis pas sûr de la meilleure façon d'aborder ce problème.

Supposons que j'ai une classe


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

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

La méthode DoCalc est au plus bas niveau et il est appelé à plusieurs reprises au cours de calcul. Un autre aspect important est que ValeurN ne sont mis au début et ne changent pas pendant le calcul. Donc, la plupart des ifs dans la méthode DoCalc ne sont pas nécessaires, comme beaucoup d'entre ValeurN sont 0. J'espérais que la génération de code dynamique pourrait aider à améliorer les performances.

Par exemple, si je crée une méthode


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

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

et le compiler avec des optimisations du compilateur commutées C # est assez intelligent pour ne garder que les choses nécessaires. Donc, je voudrais créer cette méthode lors de l'exécution sur la base des valeurs de ValeurN et utiliser la méthode générée lors de calculs.

Je suppose que je pourrais utiliser des arbres d'expression pour qui fonctionne, mais les arbres d'expression uniquement avec des fonctions simples lambda, je ne peux pas utiliser des choses comme si, tout en etc. à l'intérieur du corps de la fonction. Donc, dans ce cas, je dois changer cette méthode de manière appropriée.

Une autre possibilité est de créer le code nécessaire comme une chaîne et le compiler dynamiquement. Mais ce serait beaucoup mieux pour moi si je pouvais prendre la méthode existante et la modifier en conséquence.

Il y a aussi Reflection.Emit, mais je ne veux pas rester avec elle, car il serait très difficile à maintenir.

BTW. Je ne suis pas limitée aux C #. Je suis ouvert aux suggestions des langages de programmation qui sont les mieux adaptés à ce genre de problème. À l'exception de LISP pour deux raisons.

Une précision importante. DoValue1RelatedStuff () n'est pas un appel de méthode dans mon algorithme. Il est juste un calcul basé sur une formule et il est assez rapide. Je l'ai écrit comme ceci


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

J'ai quelques tests de performance et je peux voir que deux ifs quand on est désactivé la méthode optimisée est d'environ 2 fois plus rapide que la redondance si.

Voici le code que j'utilisé pour le 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;
        }
    }
Était-ce utile?

La solution

Je pense même généralement pas une telle optimisation. Combien de travail ne DoValueXRelatedStuff() faire? Plus de 10 à 50 cycles de processeur? Oui? Cela signifie que vous allez construire un système complexe tout à fait d'économiser moins de 10% du temps d'exécution (et cela me semble tout à fait optimiste). Cela peut facilement descendre à moins de 1%.

Yat-il pas de place pour d'autres optimisations? De meilleurs algorithmes? Un avez-vous vraiment besoin d'éliminer les branches individuelles prenant un seul cycle de traitement (si la prédiction de branchement est correcte)? Oui? Ne devriez-vous penser à écrire votre code en assembleur ou quelque chose d'autre machine plus spécifique au lieu d'utiliser .NET?

Pourriez-vous donner l'ordre de N, la complexité d'une méthode typique, et le rapport des expressions d'évaluation généralement vrai?

Autres conseils

Il me surprendrait de trouver un scénario où les frais généraux d'évaluation de la si déclarations vaut la peine d'émettre dynamiquement le code.

et branche predication, ce qui en fait les frais généraux pour les branches en petits segments d'approche de code zéro.

Avez-vous essayé de référence deux versions codées manuellement du code, celui qui a toutes les instructions if en place, mais fournit des valeurs nulles pour la plupart, et qui supprime tous ces mêmes si les branches?

Si vous êtes vraiment dans l'optimisation de code - avant de faire quoi que ce soit - exécuter le profileur! Il vous montrera le goulot d'étranglement est où et quels domaines méritent d'optimisation.

De plus - si le choix de la langue ne se limite pas (sauf pour LISP) alors rien ne battra assembleur en termes de performance;)

Je me souviens atteindre la magie de la performance en réécrivant certaines fonctions internes (comme celui que vous avez) en utilisant assembleur.

Avant de faire quoi que ce soit, avez-vous réellement un problème

i.e.. il tourne assez longtemps pour vous déranger?

Si oui, savoir ce qui se prend réellement le temps, pas ce que vous devinez . Cette est rapide , méthode sale, et très efficace, j'utiliser pour voir où le temps passe.

Maintenant, vous parlez d'interprétation par rapport à la compilation. Code Interprété est généralement 1-2 ordres de grandeur plus lent que le code compilé. La raison en est que les interprètes essaient de trouver sans cesse ce qu'il faut faire ensuite, puis oublier , alors que le code compilé juste sait .

Si vous êtes dans cette situation, il peut être judicieux de payer le prix de la traduction pour obtenir la vitesse du code compilé.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top