संकलित सी # लैम्ब्डा एक्सप्रेशन प्रदर्शन


91

एक संग्रह पर निम्नलिखित सरल हेरफेर पर विचार करें:

static List<int> x = new List<int>() { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
var result = x.Where(i => i % 2 == 0).Where(i => i > 5);

अब हम भाव का उपयोग करते हैं। निम्नलिखित कोड लगभग बराबर है:

static void UsingLambda() {
    Func<IEnumerable<int>, IEnumerable<int>> lambda = l => l.Where(i => i % 2 == 0).Where(i => i > 5);
    var t0 = DateTime.Now.Ticks;
    for (int j = 1; j < MAX; j++) 
        var sss = lambda(x).ToList();

    var tn = DateTime.Now.Ticks;
    Console.WriteLine("Using lambda: {0}", tn - t0);
}

लेकिन मैं अभिव्यक्ति का निर्माण करना चाहता हूं, इसलिए यहां एक नई परीक्षा है:

static void UsingCompiledExpression() {
    var f1 = (Expression<Func<IEnumerable<int>, IEnumerable<int>>>)(l => l.Where(i => i % 2 == 0));
    var f2 = (Expression<Func<IEnumerable<int>, IEnumerable<int>>>)(l => l.Where(i => i > 5));
    var argX = Expression.Parameter(typeof(IEnumerable<int>), "x");
    var f3 = Expression.Invoke(f2, Expression.Invoke(f1, argX));
    var f = Expression.Lambda<Func<IEnumerable<int>, IEnumerable<int>>>(f3, argX);

    var c3 = f.Compile();

    var t0 = DateTime.Now.Ticks;
    for (int j = 1; j < MAX; j++) 
        var sss = c3(x).ToList();

    var tn = DateTime.Now.Ticks;
    Console.WriteLine("Using lambda compiled: {0}", tn - t0);
}

बेशक यह बिल्कुल ऊपर की तरह नहीं है, इसलिए निष्पक्ष होने के लिए, मैं पहले एक को थोड़ा संशोधित करता हूं:

static void UsingLambdaCombined() {
    Func<IEnumerable<int>, IEnumerable<int>> f1 = l => l.Where(i => i % 2 == 0);
    Func<IEnumerable<int>, IEnumerable<int>> f2 = l => l.Where(i => i > 5);
    Func<IEnumerable<int>, IEnumerable<int>> lambdaCombined = l => f2(f1(l));
    var t0 = DateTime.Now.Ticks;
    for (int j = 1; j < MAX; j++) 
        var sss = lambdaCombined(x).ToList();

    var tn = DateTime.Now.Ticks;
    Console.WriteLine("Using lambda combined: {0}", tn - t0);
}

अब परिणाम आता है MAX = 100000, VS2008, डीबगिंग पर:

Using lambda compiled: 23437500
Using lambda:           1250000
Using lambda combined:  1406250

और डिबगिंग ऑफ़ के साथ:

Using lambda compiled: 21718750
Using lambda:            937500
Using lambda combined:  1093750

आश्चर्य । संकलित अभिव्यक्ति अन्य विकल्पों की तुलना में लगभग 17x धीमी है। अब यहाँ प्रश्न आते हैं:

  1. क्या मैं गैर-समकक्ष भावों की तुलना कर रहा हूं?
  2. क्या संकलित अभिव्यक्ति को .NET "ऑप्टिमाइज़" करने के लिए एक तंत्र है?
  3. मैं उसी चेन कॉल को l.Where(i => i % 2 == 0).Where(i => i > 5);प्रोग्रामेटिक रूप से कैसे व्यक्त करूं ?

कुछ और आंकड़े। दृश्य स्टूडियो 2010, डिबगिंग ऑन, अनुकूलन बंद:

Using lambda:           1093974
Using lambda compiled: 15315636
Using lambda combined:   781410

डिबगिंग ऑन, ऑप्टिमाइज़ेशन ऑन:

Using lambda:            781305
Using lambda compiled: 15469839
Using lambda combined:   468783

डिबगिंग ऑफ़, अनुकूलन

Using lambda:            625020
Using lambda compiled: 14687970
Using lambda combined:   468765

नया आश्चर्य। VS2008 (C # 3) से VS2010 (C # 4) पर स्विच करना, UsingLambdaCombinedदेशी लैम्ब्डा की तुलना में तेज़ बनाता है।


ठीक है, मैंने परिमाण के एक आदेश से अधिक लम्बे संकलित प्रदर्शन को सुधारने का एक तरीका पाया है। यहाँ एक सलाह है; प्रोफाइलर को चलाने के बाद, 92% समय व्यतीत होता है:

System.Reflection.Emit.DynamicMethod.CreateDelegate(class System.Type, object)

हम्म्म्म ... यह हर पुनरावृत्ति में एक नया प्रतिनिधि क्यों बना रहा है? मुझे यकीन नहीं है, लेकिन समाधान एक अलग पोस्ट में है।


3
क्या ये दृश्य स्टूडियो में चलने से हैं? यदि ऐसा है, तो एक डिबगिंग मोड का उपयोग करके समय को दोहराएं और डिबगिंग के बिना चलाएं (जैसे कि विज़ुअल स्टूडियो में Ctrl + F5, या कमांड अनुभाग से)। इसके अलावा, Stopwatchसमय के बजाय उपयोग करने पर विचार करें DateTime.Now
जिम मेंथेल

12
मुझे नहीं पता कि यह धीमा क्यों है, लेकिन आपकी बेंचमार्क तकनीक बहुत अच्छी नहीं है। सबसे पहले, DateTime.Now केवल एक सेकंड के 1/64 के लिए सटीक है, इसलिए आपकी माप गोलाई त्रुटि बड़ी है। इसके बजाय स्टॉपवॉच का उपयोग करें; यह कुछ नैनोसेकंड के लिए सटीक है। दूसरा, आप कोड (पहली कॉल) और हर बाद की कॉल को जिताने के लिए दोनों समय को माप रहे हैं; वह औसत फेंक सकता है। (हालांकि इस मामले में एक सौ हजार का एक मैक्स शायद जीट के बोझ को दूर करने के लिए पर्याप्त है, फिर भी, इसे औसत में शामिल करने के लिए एक बुरा अभ्यास है।)
एरिक लिपर्ट

7
@ एरिक, राउंडिंग त्रुटि केवल तभी हो सकती है जब प्रत्येक ऑपरेशन में DateTime.Now.Ticks का उपयोग किया जाता है, शुरू होने से पहले और अंत के बाद, प्रदर्शन अंतर दिखाने के लिए मिलीसेकंड काउंट काफी अधिक होता है।
आकाश काव

1
अगर स्टॉपवॉच का उपयोग करते हुए, मैं सटीक परिणाम सुनिश्चित करने के लिए इस लेख का पालन करने की सलाह देता हूं: codeproject.com/KB/testing/stopwatch-measure-precise.aspx
Zach Green

1
@ एरिक, जबकि मैं मानता हूं कि यह सबसे सटीक माप तकनीक उपलब्ध नहीं है, हम अंतर के परिमाण के एक आदेश के बारे में बात कर रहे हैं। मैक्स महत्वपूर्ण विचलन को कम करने के लिए पर्याप्त उच्च है।
ह्यूगो सेरेनो फेरेरा

जवाबों:


43

क्या ऐसा हो सकता है कि भीतर के मेमनों को संकलित नहीं किया जा रहा है? यहाँ अवधारणा का प्रमाण दिया गया है:

static void UsingCompiledExpressionWithMethodCall() {
        var where = typeof(Enumerable).GetMember("Where").First() as System.Reflection.MethodInfo;
        where = where.MakeGenericMethod(typeof(int));
        var l = Expression.Parameter(typeof(IEnumerable<int>), "l");
        var arg0 = Expression.Parameter(typeof(int), "i");
        var lambda0 = Expression.Lambda<Func<int, bool>>(
            Expression.Equal(Expression.Modulo(arg0, Expression.Constant(2)),
                             Expression.Constant(0)), arg0).Compile();
        var c1 = Expression.Call(where, l, Expression.Constant(lambda0));
        var arg1 = Expression.Parameter(typeof(int), "i");
        var lambda1 = Expression.Lambda<Func<int, bool>>(Expression.GreaterThan(arg1, Expression.Constant(5)), arg1).Compile();
        var c2 = Expression.Call(where, c1, Expression.Constant(lambda1));

        var f = Expression.Lambda<Func<IEnumerable<int>, IEnumerable<int>>>(c2, l);

        var c3 = f.Compile();

        var t0 = DateTime.Now.Ticks;
        for (int j = 1; j < MAX; j++)
        {
            var sss = c3(x).ToList();
        }

        var tn = DateTime.Now.Ticks;
        Console.WriteLine("Using lambda compiled with MethodCall: {0}", tn - t0);
    }

और अब समय हैं:

Using lambda:                            625020
Using lambda compiled:                 14687970
Using lambda combined:                   468765
Using lambda compiled with MethodCall:   468765

यहाँ प्रारंभ करें इतना ही नहीं यह तेज है, यह देशी मेमने से तेज है। ( सिर खुजाना )।


बेशक उपरोक्त कोड बस लिखने के लिए बहुत दर्दनाक है। आइए करते हैं कुछ सरल जादू:

static void UsingCompiledConstantExpressions() {
    var f1 = (Func<IEnumerable<int>, IEnumerable<int>>)(l => l.Where(i => i % 2 == 0));
    var f2 = (Func<IEnumerable<int>, IEnumerable<int>>)(l => l.Where(i => i > 5));
    var argX = Expression.Parameter(typeof(IEnumerable<int>), "x");
    var f3 = Expression.Invoke(Expression.Constant(f2), Expression.Invoke(Expression.Constant(f1), argX));
    var f = Expression.Lambda<Func<IEnumerable<int>, IEnumerable<int>>>(f3, argX);

    var c3 = f.Compile();

    var t0 = DateTime.Now.Ticks;
    for (int j = 1; j < MAX; j++) {
        var sss = c3(x).ToList();
    }

    var tn = DateTime.Now.Ticks;
    Console.WriteLine("Using lambda compiled constant: {0}", tn - t0);
}

और कुछ समय, VS2010, अनुकूलन पर, डिबगिंग बंद:

Using lambda:                            781260
Using lambda compiled:                 14687970
Using lambda combined:                   468756
Using lambda compiled with MethodCall:   468756
Using lambda compiled constant:          468756

अब आप यह तर्क दे सकते हैं कि मैं पूरी अभिव्यक्ति को गतिशील नहीं बना रहा हूं; सिर्फ पीछा करने वाले चालान। लेकिन उपरोक्त उदाहरण में मैं पूरी अभिव्यक्ति उत्पन्न करता हूं। और टाइमिंग मैच। यह कम कोड लिखने का एक शॉर्टकट है।


मेरी समझ से, जो चल रहा है वह है .Compile () विधि आंतरिक लैम्ब्डा के संकलन का प्रचार नहीं करती है, और इस प्रकार यह निरंतर आह्वान करती है CreateDelegate। लेकिन वास्तव में इसे समझने के लिए, मुझे बहुत अच्छा लगेगा। .NET गुरु ने आंतरिक सामग्री के बारे में थोड़ी टिप्पणी की।

और क्यों , ओह, यह अब एक देशी मेमने से ज्यादा तेज क्यों है !?


1
मैं अपने स्वयं के उत्तर को स्वीकार करने के बारे में सोच रहा हूं, क्योंकि यह सबसे अधिक वोटों वाला है। क्या मुझे थोड़ा और इंतजार करना चाहिए?
ह्यूगो सेरेनो फेरेरा

देशी लैम्ब्डा की तुलना में आपके साथ कोड तेजी से प्राप्त करने के बारे में क्या होता है, आप इस पेज पर माइक्रोबेनचर्च (जिसमें वास्तव में जावा-विशिष्ट, नाम के बावजूद कुछ भी नहीं है) के बारे में एक नज़र रखना चाहते हैं: code.google.com/p/caliper/wiki / JavaMicrobenchmark
Blaisorblade

जैसा कि गतिशील रूप से संकलित लैम्ब्डा क्यों तेज है, मुझे संदेह है कि "लैम्ब्डा का उपयोग करना", पहले चलाया जा रहा है, जेआईटी को कुछ कोड होने के साथ दंडित किया जा रहा है।
Oskar Berggren

मुझे नहीं पता कि क्या हो रहा है, एक बार जब मैंने खेतों और संपत्तियों से सेटिंग और प्राप्त करने के लिए संकलित अभिव्यक्ति और क्रिएटगेट का परीक्षण किया था, तो सेलेगेट गुणों के लिए तेज़ था, लेकिन खेतों के लिए संकलित बहुत तेज़ था
nawfal

10

हाल ही में मैंने एक समान प्रश्न पूछा:

संकलित-टू-डेलिगेट अभिव्यक्ति का प्रदर्शन

मेरे लिए समाधान था कि मैं नहीं बुलाना चाहिए Compileपर Expressionहै, लेकिन है कि मैं फोन करना चाहिए CompileToMethodउस पर और संकलन Expressionएक करने के लिए staticविधानसभा में एक गतिशील विधि।

इस तरह:

var assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(
  new AssemblyName("MyAssembly_" + Guid.NewGuid().ToString("N")), 
  AssemblyBuilderAccess.Run);

var moduleBuilder = assemblyBuilder.DefineDynamicModule("Module");

var typeBuilder = moduleBuilder.DefineType("MyType_" + Guid.NewGuid().ToString("N"), 
  TypeAttributes.Public));

var methodBuilder = typeBuilder.DefineMethod("MyMethod", 
  MethodAttributes.Public | MethodAttributes.Static);

expression.CompileToMethod(methodBuilder);

var resultingType = typeBuilder.CreateType();

var function = Delegate.CreateDelegate(expression.Type,
  resultingType.GetMethod("MyMethod"));

हालांकि यह आदर्श नहीं है। मैं काफी के लिए जो प्रकार यह वास्तव में लागू होता है कुछ नहीं कर रहा हूँ, लेकिन मुझे लगता है कि प्रकार है कि प्रतिनिधि द्वारा प्रतिनिधि द्वारा पैरामीटर के रूप में लिया जाता है, या लौट आए है होना करने के लिए publicऔर गैर सामान्य। यह गैर-जेनेरिक होना चाहिए क्योंकि जेनेरिक प्रकार स्पष्ट रूप से एक्सेस करते हैं System.__Canonजो कि जेनेरिक प्रकारों के लिए हुड के तहत .NET द्वारा उपयोग किया जाने वाला एक आंतरिक प्रकार है और यह "एक publicप्रकार का नियम होना चाहिए" का उल्लंघन करता है ।

उन प्रकारों के लिए, आप स्पष्ट रूप से धीमी गति से उपयोग कर सकते हैं Compile। मैं निम्नलिखित तरीके से उनका पता लगाता हूं:

private static bool IsPublicType(Type t)
{

  if ((!t.IsPublic && !t.IsNestedPublic) || t.IsGenericType)
  {
    return false;
  }

  int lastIndex = t.FullName.LastIndexOf('+');

  if (lastIndex > 0)
  {
    var containgTypeName = t.FullName.Substring(0, lastIndex);

    var containingType = Type.GetType(containgTypeName + "," + t.Assembly);

    if (containingType != null)
    {
      return containingType.IsPublic;
    }

    return false;
  }
  else
  {
    return t.IsPublic;
  }
}

लेकिन जैसा कि मैंने कहा, यह आदर्श नहीं है और मैं अभी भी जानना चाहूंगा कि डायनामिक असेंबली के लिए एक विधि संकलित करना कभी-कभी तेजी का परिमाण क्यों होता है। और मैं कभी-कभी कहता हूं क्योंकि मैंने ऐसे मामलों को भी देखा है जहां एक Expressionसंकलित Compileप्रक्रिया सामान्य तरीके से उतनी ही तेजी से होती है। उसके लिए मेरा प्रश्न देखें।

या अगर किसी को publicडायनामिक असेंबली के साथ "नो नॉन- टाइप्स" बाधा को बायपास करने का एक तरीका पता है , तो यह स्वागत योग्य है।


4

आपके भाव समतुल्य नहीं हैं और इस प्रकार आपको तिरछे परिणाम मिलते हैं। मैंने इसे परखने के लिए एक टेस्ट बेंच लिखी। परीक्षणों में नियमित लैम्ब्डा कॉल, समतुल्य संकलित अभिव्यक्ति, एक हाथ से बना समान संकलित अभिव्यक्ति और साथ ही रचित संस्करण शामिल हैं। ये अधिक सटीक संख्या होनी चाहिए। दिलचस्प है, मैं सादे और रचित संस्करणों के बीच बहुत भिन्नता नहीं देख रहा हूं। और संकलित अभिव्यक्तियाँ स्वाभाविक रूप से धीमी हैं लेकिन केवल बहुत कम से। आपको कुछ अच्छे नंबर प्राप्त करने के लिए एक बड़े पर्याप्त इनपुट और पुनरावृत्ति गणना की आवश्यकता है। इससे फर्क पड़ता है।

आपके दूसरे प्रश्न के लिए, मुझे नहीं पता कि आप इसमें से अधिक प्रदर्शन कैसे प्राप्त कर पाएंगे, इसलिए मैं वहां आपकी मदद नहीं कर सकता। यह देखने में जितना अच्छा लगने वाला है।

आपको HandMadeLambdaExpression()विधि में आपके तीसरे प्रश्न का उत्तर मिल जाएगा । एक्सटेंशन विधियों के कारण निर्माण करने के लिए सबसे आसान अभिव्यक्ति नहीं है, लेकिन उल्लेखनीय है।

using System;
using System.Collections.Generic;
using System.Linq;

using System.Diagnostics;
using System.Linq.Expressions;

namespace ExpressionBench
{
    class Program
    {
        static void Main(string[] args)
        {
            var values = Enumerable.Range(0, 5000);
            var lambda = GetLambda();
            var lambdaExpression = GetLambdaExpression().Compile();
            var handMadeLambdaExpression = GetHandMadeLambdaExpression().Compile();
            var composed = GetComposed();
            var composedExpression = GetComposedExpression().Compile();
            var handMadeComposedExpression = GetHandMadeComposedExpression().Compile();

            DoTest("Lambda", values, lambda);
            DoTest("Lambda Expression", values, lambdaExpression);
            DoTest("Hand Made Lambda Expression", values, handMadeLambdaExpression);
            Console.WriteLine();
            DoTest("Composed", values, composed);
            DoTest("Composed Expression", values, composedExpression);
            DoTest("Hand Made Composed Expression", values, handMadeComposedExpression);
        }

        static void DoTest<TInput, TOutput>(string name, TInput sequence, Func<TInput, TOutput> operation, int count = 1000000)
        {
            for (int _ = 0; _ < 1000; _++)
                operation(sequence);
            var sw = Stopwatch.StartNew();
            for (int _ = 0; _ < count; _++)
                operation(sequence);
            sw.Stop();
            Console.WriteLine("{0}:", name);
            Console.WriteLine("  Elapsed: {0,10} {1,10} (ms)", sw.ElapsedTicks, sw.ElapsedMilliseconds);
            Console.WriteLine("  Average: {0,10} {1,10} (ms)", decimal.Divide(sw.ElapsedTicks, count), decimal.Divide(sw.ElapsedMilliseconds, count));
        }

        static Func<IEnumerable<int>, IList<int>> GetLambda()
        {
            return v => v.Where(i => i % 2 == 0).Where(i => i > 5).ToList();
        }

        static Expression<Func<IEnumerable<int>, IList<int>>> GetLambdaExpression()
        {
            return v => v.Where(i => i % 2 == 0).Where(i => i > 5).ToList();
        }

        static Expression<Func<IEnumerable<int>, IList<int>>> GetHandMadeLambdaExpression()
        {
            var enumerableMethods = typeof(Enumerable).GetMethods();
            var whereMethod = enumerableMethods
                .Where(m => m.Name == "Where")
                .Select(m => m.MakeGenericMethod(typeof(int)))
                .Where(m => m.GetParameters()[1].ParameterType == typeof(Func<int, bool>))
                .Single();
            var toListMethod = enumerableMethods
                .Where(m => m.Name == "ToList")
                .Select(m => m.MakeGenericMethod(typeof(int)))
                .Single();

            // helpers to create the static method call expressions
            Func<Expression, ParameterExpression, Func<ParameterExpression, Expression>, Expression> WhereExpression =
                (instance, param, body) => Expression.Call(whereMethod, instance, Expression.Lambda(body(param), param));
            Func<Expression, Expression> ToListExpression =
                instance => Expression.Call(toListMethod, instance);

            //return v => v.Where(i => i % 2 == 0).Where(i => i > 5).ToList();
            var exprParam = Expression.Parameter(typeof(IEnumerable<int>), "v");
            var expr0 = WhereExpression(exprParam,
                Expression.Parameter(typeof(int), "i"),
                i => Expression.Equal(Expression.Modulo(i, Expression.Constant(2)), Expression.Constant(0)));
            var expr1 = WhereExpression(expr0,
                Expression.Parameter(typeof(int), "i"),
                i => Expression.GreaterThan(i, Expression.Constant(5)));
            var exprBody = ToListExpression(expr1);
            return Expression.Lambda<Func<IEnumerable<int>, IList<int>>>(exprBody, exprParam);
        }

        static Func<IEnumerable<int>, IList<int>> GetComposed()
        {
            Func<IEnumerable<int>, IEnumerable<int>> composed0 =
                v => v.Where(i => i % 2 == 0);
            Func<IEnumerable<int>, IEnumerable<int>> composed1 =
                v => v.Where(i => i > 5);
            Func<IEnumerable<int>, IList<int>> composed2 =
                v => v.ToList();
            return v => composed2(composed1(composed0(v)));
        }

        static Expression<Func<IEnumerable<int>, IList<int>>> GetComposedExpression()
        {
            Expression<Func<IEnumerable<int>, IEnumerable<int>>> composed0 =
                v => v.Where(i => i % 2 == 0);
            Expression<Func<IEnumerable<int>, IEnumerable<int>>> composed1 =
                v => v.Where(i => i > 5);
            Expression<Func<IEnumerable<int>, IList<int>>> composed2 =
                v => v.ToList();
            var exprParam = Expression.Parameter(typeof(IEnumerable<int>), "v");
            var exprBody = Expression.Invoke(composed2, Expression.Invoke(composed1, Expression.Invoke(composed0, exprParam)));
            return Expression.Lambda<Func<IEnumerable<int>, IList<int>>>(exprBody, exprParam);
        }

        static Expression<Func<IEnumerable<int>, IList<int>>> GetHandMadeComposedExpression()
        {
            var enumerableMethods = typeof(Enumerable).GetMethods();
            var whereMethod = enumerableMethods
                .Where(m => m.Name == "Where")
                .Select(m => m.MakeGenericMethod(typeof(int)))
                .Where(m => m.GetParameters()[1].ParameterType == typeof(Func<int, bool>))
                .Single();
            var toListMethod = enumerableMethods
                .Where(m => m.Name == "ToList")
                .Select(m => m.MakeGenericMethod(typeof(int)))
                .Single();

            Func<ParameterExpression, Func<ParameterExpression, Expression>, Expression> LambdaExpression =
                (param, body) => Expression.Lambda(body(param), param);
            Func<Expression, ParameterExpression, Func<ParameterExpression, Expression>, Expression> WhereExpression =
                (instance, param, body) => Expression.Call(whereMethod, instance, Expression.Lambda(body(param), param));
            Func<Expression, Expression> ToListExpression =
                instance => Expression.Call(toListMethod, instance);

            var composed0 = LambdaExpression(Expression.Parameter(typeof(IEnumerable<int>), "v"),
                v => WhereExpression(
                    v,
                    Expression.Parameter(typeof(int), "i"),
                    i => Expression.Equal(Expression.Modulo(i, Expression.Constant(2)), Expression.Constant(0))));
            var composed1 = LambdaExpression(Expression.Parameter(typeof(IEnumerable<int>), "v"),
                v => WhereExpression(
                    v,
                    Expression.Parameter(typeof(int), "i"),
                    i => Expression.GreaterThan(i, Expression.Constant(5))));
            var composed2 = LambdaExpression(Expression.Parameter(typeof(IEnumerable<int>), "v"),
                v => ToListExpression(v));

            var exprParam = Expression.Parameter(typeof(IEnumerable<int>), "v");
            var exprBody = Expression.Invoke(composed2, Expression.Invoke(composed1, Expression.Invoke(composed0, exprParam)));
            return Expression.Lambda<Func<IEnumerable<int>, IList<int>>>(exprBody, exprParam);
        }
    }
}

और मेरी मशीन पर परिणाम:

लैम्ब्डा:
  बीता हुआ समय: 340971948 123230 (एमएस)
  औसत: 340.971948 0.12323 (एमएस)
लम्बा अभिव्यक्ति:
  बीता हुआ समय: 357077202 129051 (एमएस)
  औसत: 357.077202 0.129051 (एमएस)
हाथ से बने लैम्ब्डा एक्सप्रेशन:
  बीता हुआ: 345029281 124696 (एमएस)
  औसत: 345.029281 0.124696 (एमएस)

रचना:
  बीता हुआ समय: 340409238 123027 (एमएस)
  औसत: 340.409238 0.123027 (एमएस)
रचित अभिव्यक्ति:
  बीता हुआ: 350800599 126782 (एमएस)
  औसत: 350.800599 0.126782 (एमएस)
हाथ से बनाया गया अभिव्यक्ति:
  बीता हुआ समय: 352811359 127509 (एमएस)
  औसत: 352.811359 0.127509 (एमएस)

3

प्रतिनिधियों पर संकलित लंबोदर प्रदर्शन धीमा हो सकता है क्योंकि रनटाइम पर संकलित कोड को अनुकूलित नहीं किया जा सकता है, हालांकि आपके द्वारा मैन्युअल रूप से लिखे गए कोड और सी # संकलक के माध्यम से संकलित किया गया है।

दूसरा, कई लंबो अभिव्यक्तियों का अर्थ है कई गुमनाम तरीके, और उनमें से प्रत्येक को कॉल करना एक सीधी विधि का मूल्यांकन करने में थोड़ा अतिरिक्त समय लगता है। उदाहरण के लिए, कॉलिंग

Console.WriteLine(x);

तथा

Action x => Console.WriteLine(x);
x(); // this means two different calls..

अलग हैं, और दूसरे एक छोटे से ओवरहेड की आवश्यकता है क्योंकि संकलक के दृष्टिकोण से इसकी वास्तव में दो अलग-अलग कॉल हैं। पहले कॉलिंग x खुद और फिर उस कॉलिंग x के स्टेटमेंट के भीतर।

तो आपके संयुक्त लैम्ब्डा में निश्चित रूप से एकल लैम्ब्डा अभिव्यक्ति पर थोड़ा धीमा प्रदर्शन होगा।

और यह स्वतंत्र है कि अंदर क्या निष्पादित हो रहा है, क्योंकि आप अभी भी सही तर्क का मूल्यांकन कर रहे हैं, लेकिन आप प्रदर्शन करने के लिए संकलक के लिए अतिरिक्त कदम जोड़ रहे हैं।

एक्सप्रेशन ट्री संकलित होने के बाद भी, इसमें ऑप्टिमाइज़ेशन नहीं होगा, और यह अभी भी अपनी छोटी सी जटिल संरचना को संरक्षित करेगा, इसका मूल्यांकन करना और कॉल करना अतिरिक्त सत्यापन, अशक्त जांच आदि हो सकता है जो संकलित लैम्ब्डा अभिव्यक्तियों के प्रदर्शन को धीमा कर सकता है।


2
यदि आप बारीकी से देखते हैं, तो UsingLambdaCombinedपरीक्षण कई लंबो कार्यों का संयोजन कर रहा है, और इसका प्रदर्शन बहुत करीब है UsingLambda। ऑप्टिमाइज़ेशन के बारे में, मुझे विश्वास था कि वे JIT इंजन द्वारा नियंत्रित किए गए थे, और इस प्रकार रनटाइम जनरेट कोड (संकलन के बाद), किसी भी JIT ऑप्टिमाइज़ेशन का लक्ष्य होगा।
ह्यूगो सेरेनो फेरेरा

1
JIT ऑप्टिमाइज़ेशन और कंपाइल टाइम ऑप्टिमाइज़ेशन दो अलग-अलग चीज़ें हैं जिन्हें आप प्रोजेक्ट सेटिंग्स में कंपाइल टाइम ऑप्टिमाइज़ेशन बंद कर सकते हैं। दूसरे, अभिव्यक्ति संकलन शायद गतिशील एमएसआईएल का उत्सर्जन करेगा जो फिर से थोड़ा धीमा हो जाएगा क्योंकि यह तर्क है और संचालन के अनुक्रम में ज़रूरत के अनुसार शून्य जांच और वैधता होगी। आप परावर्तक में देख सकते हैं कि यह कैसे संकलित है।
आकाश काव

2
जब आपका तर्क उचित है, तो मुझे इस विशेष समस्या पर आपसे असहमत होना होगा (यानी, परिमाण के अंतर का क्रम स्थैतिक संकलन के कारण नहीं है)। पहला, क्योंकि यदि आप वास्तव में संकलन-समय के अनुकूलन को अक्षम करते हैं, तो अंतर अभी भी काफी है। दूसरे, क्योंकि मैंने पहले से ही डायनामिक पीढ़ी को अनुकूलित करने का एक तरीका ढूंढ लिया है, जो थोड़ा धीमा है। मुझे "क्यों" समझने की कोशिश करें और मैं परिणाम पोस्ट करूंगा।
ह्यूगो सेरेनो फरेरा
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.