SORU
6 NİSAN 2011, ÇARŞAMBA


Derlenmiş C# Lambda İfadeleri Performans

Koleksiyonu aşağıdaki basit manipülasyon düşünün:

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);

Şimdi gelelim İfadeler kullanın. Aşağıdaki kodu kabaca eşdeğer

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);
}

Ama-the-fly ifade oluşturmak istiyorum, burada yeni bir test:

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);
}

Tabii ki aynen yukarıdaki gibi değil, adil olmak gerekirse, ben ilk biraz değiştirin:

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);
}

Şimdi gelir = 100000, VS2008, hata ayıklama için MAX: sonuçları

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

Ve hata ayıklama KAPALI:

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

Sürpriz. Derlenmiş ifadesi yaklaşık 17x diğer alternatiflere göre daha yavaştır. Şimdi geliyor soru

  1. Non-eşdeğer ifadeler karşılaştırma mıyım?
  2. Bir mekanizma yapmak .NET "optimize" derlenmiş ifadesi?
  3. Nasıl l.Where(i => i % 2 == 0).Where(i => i > 5); programlama yoluyla arayın aynı zincirin ifade ederim?

Biraz daha istatistik. Visual Studio 2010, hata ayıklama, optimizasyon OFF:

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

Hata ayıklama, optimizasyon:

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

KAPALI hata ayıklama, optimizasyon:

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

Yeni Sürpriz.VS2010 için VS2008 C#3) geçiş (C#4), UsingLambdaCombined yerli lambda daha hızlı yapar.


Tamam, büyüklükte bir sipariş daha fazla lambda derlenmiş performansını artırmak için bir yol buldum. İşte size bir ipucu; çalıştırdıktan sonra profiler, 92% zaman harcadık

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

Hmmmm... Neden her tekrarında yeni bir temsilci yaratıyor? Emin değilim ama çözüm ayrı bir yazı aşağıda.

CEVAP
6 NİSAN 2011, ÇARŞAMBA


Bu iç Lambda derlenmiş olan değil mi?!? İşte bir kanıtı:

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);
    }

Ve şimdi zamanlamaları

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

Heyo!! Hızlı değil, yerli lambda daha hızlı. (Sıfırdan kafa).


Elbette yukarıdaki kodu sadece çok acı yazmaktır. Hadi bir kaç basit büyü yapmak:

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);
}

Ve bazı zamanlamalar, VS2010, hata Ayıklama KAPALI Optimizasyon:

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

Şimdi bütün ifade dinamik olarak oluşturuluyor değilim; sadece zincirleme çağırmaları söyleyebiliriz. Ama yukarıdaki örnekte bütün bir ifade oluşturur. Ve zamanlamaları maç. Bu daha az kod yazmak için sadece bir kısayol.


Neler olduğunu benim anlayış olduğunu .() Derleme yöntemi iç Lambda, ve CreateDelegate sürekli çağırma böylece derlemeler yaymak değil. Ama gerçekten bunu anlamak için, bir sahip olmak isterdim .NET guru iç şeyler hakkında biraz yorum.

Veneden, ohnedenbu yerel bir lambda şimdi daha hızlı!?

Bunu Paylaş:
  • Google+
  • E-Posta
Etiketler:

YORUMLAR

SPONSOR VİDEO

Rastgele Yazarlar

  • circuschina

    circuschina

    16 Mart 2007
  • THELIFEOFPRICE

    THELIFEOFPRI

    16 Mart 2011
  • UrAvgConsumer

    UrAvgConsume

    1 Ocak 2012