Kompilasi Kinerja Ekspresi C # Lambda

91

Pertimbangkan manipulasi sederhana berikut atas koleksi:

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

Sekarang mari gunakan Ekspresi. Kode berikut kira-kira setara:

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

Tapi saya ingin membangun ekspresi dengan cepat, jadi inilah tes baru:

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

Tentu saja tidak persis seperti di atas, jadi agar adil, saya sedikit memodifikasi yang pertama:

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

Sekarang sampai pada hasil untuk MAX = 100000, VS2008, debugging ON:

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

Dan dengan debugging OFF:

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

Kejutan . Ekspresi yang dikompilasi kira-kira 17x lebih lambat dari alternatif lainnya. Sekarang inilah pertanyaannya:

  1. Apakah saya membandingkan ekspresi yang tidak setara?
  2. Apakah ada mekanisme untuk membuat .NET "mengoptimalkan" ekspresi yang dikompilasi?
  3. Bagaimana cara mengekspresikan panggilan berantai yang sama secara l.Where(i => i % 2 == 0).Where(i => i > 5);terprogram?

Beberapa statistik lagi. Visual Studio 2010, debugging ON, optimization OFF:

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

Debugging AKTIF, pengoptimalan AKTIF:

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

Debugging NONAKTIF, pengoptimalan AKTIF:

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

Kejutan Baru. Beralih dari VS2008 (C # 3) ke VS2010 (C # 4), membuat UsingLambdaCombinedlebih cepat daripada lambda asli.


Oke, saya telah menemukan cara untuk meningkatkan kinerja yang dikompilasi lambda dengan lebih dari urutan besarnya. Berikut tipnya; setelah menjalankan profiler, 92% waktunya dihabiskan untuk:

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

Hmmmm ... Mengapa itu membuat delegasi baru di setiap iterasi? Saya tidak yakin, tetapi solusinya mengikuti di posting terpisah.

Hugo Sereno Ferreira
sumber
3
Apakah pengaturan waktu ini berjalan di Visual Studio? Jika demikian, ulangi pengaturan waktu menggunakan mode Rilis build dan jalankan tanpa debugging (yaitu Ctrl + F5 di Visual Studio, atau dari baris perintah). Juga, pertimbangkan menggunakan Stopwatchuntuk pengaturan waktu daripada DateTime.Now.
Jim Mischel
12
Saya tidak tahu mengapa ini lebih lambat, tetapi teknik benchmark Anda tidak terlalu bagus. Pertama, DateTime. Sekarang hanya akurat hingga 1/64 detik, jadi kesalahan pembulatan pengukuran Anda besar. Gunakan Stopwatch sebagai gantinya; akurat untuk beberapa nanodetik. Kedua, Anda mengukur waktu untuk melakukan jit kode (panggilan pertama) dan setiap panggilan berikutnya; yang bisa membuang rata-rata. (Meskipun dalam kasus ini MAX seratus ribu mungkin cukup untuk menghilangkan beban jit secara rata-rata, namun tetap merupakan praktik yang buruk untuk memasukkannya ke dalam rata-rata.)
Eric Lippert
7
@Eric, kesalahan pembulatan hanya dapat terjadi jika dalam setiap operasi DateTime.Now.Ticks digunakan, sebelum memulai dan setelah akhir, hitungan milidetik cukup tinggi untuk menunjukkan perbedaan kinerja.
Akash Kava
1
jika menggunakan stopwatch, saya sarankan mengikuti artikel ini untuk memastikan hasil yang akurat: codeproject.com/KB/testing/stopwatch-measure-precise.aspx
Zach Green
1
@ Eric, sementara saya setuju ini bukan teknik pengukuran paling tepat yang tersedia, kita berbicara tentang urutan besarnya perbedaan. MAX cukup tinggi untuk mengurangi penyimpangan yang signifikan.
Hugo Sereno Ferreira

Jawaban:

43

Mungkinkah lambda bagian dalam tidak dikompilasi?!? Inilah bukti konsepnya:

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

Dan sekarang waktunya adalah:

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

Woot! Tidak hanya cepat, ini lebih cepat dari lambda asli. ( Gores kepala ).


Tentu saja kode di atas terlalu menyakitkan untuk ditulis. Mari kita lakukan sihir sederhana:

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

Dan beberapa pengaturan waktu, VS2010, Pengoptimalan AKTIF, Debugging MATI:

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

Sekarang Anda dapat berargumen bahwa saya tidak membuat keseluruhan ekspresi secara dinamis; hanya doa yang dirantai. Tetapi dalam contoh di atas saya menghasilkan ekspresi keseluruhan. Dan waktunya cocok. Ini hanyalah jalan pintas untuk menulis lebih sedikit kode.


Dari pemahaman saya, apa yang terjadi adalah bahwa metode .Compile () tidak menyebarkan kompilasi ke lambda dalam, dan dengan demikian pemanggilan konstan CreateDelegate. Tetapi untuk benar-benar memahami hal ini, saya ingin memiliki komentar guru .NET sedikit tentang hal-hal internal yang terjadi.

Dan kenapa , oh kenapa sekarang ini lebih cepat dari lambda asli !?

Hugo Sereno Ferreira
sumber
1
Saya sedang berpikir untuk menerima jawaban saya sendiri, karena itu yang dengan suara terbanyak. Haruskah saya menunggu lebih lama?
Hugo Sereno Ferreira
Tentang apa yang terjadi dengan Anda mendapatkan kode lebih cepat daripada lambda asli, Anda mungkin ingin melihat halaman ini tentang microbenchmark (yang tidak benar-benar khusus untuk Java, terlepas dari namanya): code.google.com/p/caliper/wiki / JavaMicrobenchmarks
Blaisorblade
Adapun mengapa lambda yang dikompilasi secara dinamis lebih cepat, saya menduga bahwa "menggunakan lambda", yang dijalankan pertama kali, sedang dihukum karena harus melakukan JIT beberapa kode.
Oskar Berggren
Saya tidak tahu apa yang terjadi, suatu ketika ketika saya menguji ekspresi terkompilasi dan membuat delegasi untuk pengaturan dan mendapatkan dari bidang dan properti, membuat delegasi jauh lebih cepat untuk properti, tetapi dikompilasi sangat sedikit lebih cepat untuk bidang
nawfal
10

Baru-baru ini saya mengajukan pertanyaan yang hampir identik:

Performa Ekspresi yang dikompilasi untuk didelegasikan

Solusi bagi saya adalah bahwa saya tidak harus menelepon Compilepada Expression, tapi yang saya harus menelepon CompileToMethoddi atasnya dan mengkompilasi Expressionke staticmetode dalam dinamis perakitan.

Seperti:

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

Namun itu tidak ideal. Aku tidak cukup yakin yang jenis ini berlaku persis, tapi saya berpikir bahwa jenis yang diambil sebagai parameter oleh delegasi, atau dikembalikan oleh delegasi harus menjadi publicdan non-generik. Ini harus non-generik karena tipe generik tampaknya akses System.__Canonyang merupakan tipe internal yang digunakan oleh .NET di bawah tenda untuk tipe generik dan ini melanggar aturan "harus menjadi publictipe).

Untuk tipe tersebut, Anda bisa menggunakan yang tampaknya lebih lambat Compile. Saya mendeteksinya dengan cara berikut:

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

Tapi seperti yang saya katakan, ini tidak ideal dan saya masih ingin tahu mengapa mengompilasi metode ke perakitan dinamis terkadang urutan besarnya lebih cepat. Dan saya katakan kadang-kadang karena saya juga melihat kasus di mana Expressiondikompilasi dengan Compilesecepat metode normal. Lihat pertanyaan saya untuk itu.

Atau jika seseorang mengetahui cara untuk melewati publicbatasan "tanpa non- tipe" dengan dynamic assembly, itu juga diterima.

JulianR
sumber
4

Ekspresi Anda tidak setara dan dengan demikian Anda mendapatkan hasil yang miring. Saya menulis bangku tes untuk menguji ini. Pengujian tersebut mencakup panggilan lambda reguler, ekspresi terkompilasi yang setara, ekspresi terkompilasi yang dibuat dengan tangan, serta versi yang disusun. Ini harus angka yang lebih akurat. Menariknya, saya tidak melihat banyak variasi antara versi biasa dan versi tersusun. Dan ekspresi yang dikompilasi lebih lambat secara alami tetapi hanya dengan sangat sedikit. Anda membutuhkan input dan jumlah iterasi yang cukup besar untuk mendapatkan beberapa angka yang bagus. Itu membuat perbedaan.

Adapun pertanyaan kedua Anda, saya tidak tahu bagaimana Anda bisa mendapatkan lebih banyak kinerja dari ini jadi saya tidak bisa membantu Anda di sana. Ini terlihat sebagus yang akan didapat.

Anda akan menemukan jawaban saya untuk pertanyaan ketiga Anda dalam HandMadeLambdaExpression()metode ini. Bukan ekspresi termudah untuk dibuat karena metode ekstensi, tetapi bisa dilakukan.

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

Dan hasilnya di mesin saya:

Lambda:
  Berlalu: 340971948 123230 (ms)
  Rata-rata: 340.971948 0.12323 (ms)
Ekspresi Lambda:
  Berlalu: 357077202 129051 (ms)
  Rata-rata: 357.077202 0.129051 (ms)
Ekspresi Lambda Buatan Tangan:
  Berlalu: 345029281 124696 (ms)
  Rata-rata: 345.029281 0.124696 (ms)

Tersusun:
  Berlalu: 340409238 123027 (ms)
  Rata-rata: 340.409238 0.123027 (ms)
Ekspresi Tersusun:
  Berlalu: 350800599 126782 (md)
  Rata-rata: 350.800599 0.126782 (ms)
Ekspresi Buatan Tangan:
  Berlalu: 352811359 127509 (ms)
  Rata-rata: 352.811359 0.127509 (ms)
Jeff Mercado
sumber
3

Performa lambda yang dikompilasi atas delegasi mungkin lebih lambat karena kode yang dikompilasi pada waktu proses mungkin tidak dioptimalkan namun kode yang Anda tulis secara manual dan yang dikompilasi melalui kompiler C # dioptimalkan.

Kedua, beberapa ekspresi lambda berarti beberapa metode anonim, dan memanggil masing-masing membutuhkan sedikit waktu ekstra untuk mengevaluasi metode lurus. Misalnya, menelepon

Console.WriteLine(x);

dan

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

berbeda, dan dengan satu detik lebih sedikit overhead diperlukan karena dari perspektif penyusun, sebenarnya dua panggilan berbeda. Pertama-tama panggil x itu sendiri dan kemudian di dalam pemanggilan pernyataan x itu.

Jadi Lambda gabungan Anda pasti akan memiliki kinerja yang sedikit lambat dibandingkan ekspresi lambda tunggal.

Dan ini tidak tergantung pada apa yang dijalankan di dalamnya, karena Anda masih mengevaluasi logika yang benar, tetapi Anda menambahkan langkah-langkah tambahan untuk dijalankan oleh compiler.

Bahkan setelah pohon ekspresi dikompilasi, ia tidak akan memiliki pengoptimalan, dan ia masih akan mempertahankan struktur kecilnya yang rumit, mengevaluasi dan memanggilnya mungkin memiliki validasi tambahan, pemeriksaan null, dll. Yang mungkin memperlambat kinerja ekspresi lambda yang dikompilasi.

Akash Kava
sumber
2
Jika Anda melihat lebih dekat, UsingLambdaCombinedpengujian ini menggabungkan beberapa fungsi lambda, dan kinerjanya sangat mendekati UsingLambda. Mengenai pengoptimalan, saya yakin bahwa mereka ditangani oleh mesin JIT, dan dengan demikian kode yang dihasilkan waktu proses (setelah kompilasi), juga akan menjadi target pengoptimalan JIT apa pun.
Hugo Sereno Ferreira
1
Pengoptimalan JIT dan pengoptimalan waktu kompilasi adalah dua hal berbeda yang dapat Anda matikan pengoptimalan waktu kompilasi dalam pengaturan proyek. Kedua, kompilasi ekspresi mungkin akan memancarkan MSIL dinamis yang lagi-lagi akan sedikit lebih lambat karena logika dan urutan operasinya akan berisi pemeriksaan dan validitas nol sesuai kebutuhan. Anda dapat melihat di reflektor tentang bagaimana itu disusun.
Akash Kava
2
Meskipun alasan Anda masuk akal, saya harus tidak setuju dengan Anda tentang masalah khusus ini (yaitu, urutan perbedaan besaran bukan karena kompilasi statis). Pertama, karena jika Anda benar-benar menonaktifkan pengoptimalan waktu kompilasi, perbedaannya masih cukup besar. Kedua, karena saya telah menemukan cara untuk mengoptimalkan generasi dinamis menjadi sedikit lebih lambat. Izinkan saya mencoba memahami "mengapa" dan saya akan memposting hasilnya.
Hugo Sereno Ferreira