Hiệu suất biểu thức C # Lambda được biên dịch


91

Hãy xem xét thao tác đơn giản sau trên một tập hợp:

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

Bây giờ hãy sử dụng Biểu thức. Đoạn mã sau gần tương đương:

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

Nhưng tôi muốn xây dựng biểu thức một cách nhanh chóng, vì vậy đây là một thử nghiệm mới:

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

Tất nhiên nó không hoàn toàn giống như ở trên, vì vậy công bằng mà nói, tôi sửa đổi cái đầu tiên một chút:

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

Bây giờ là kết quả cho MAX = 100000, VS2008, gỡ lỗi BẬT:

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

Và với gỡ lỗi TẮT:

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

Bất ngờ . Biểu thức được biên dịch chậm hơn khoảng 17 lần so với các lựa chọn thay thế khác. Bây giờ ở đây có các câu hỏi:

  1. Tôi có đang so sánh các biểu thức không tương đương không?
  2. Có cơ chế nào để làm cho .NET "tối ưu hóa" biểu thức đã biên dịch không?
  3. Làm cách nào để diễn đạt cùng một chuỗi cuộc gọi theo l.Where(i => i % 2 == 0).Where(i => i > 5);chương trình?

Thêm một số thống kê. Visual Studio 2010, gỡ lỗi BẬT, tối ưu hóa TẮT:

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

BẬT gỡ lỗi, BẬT tối ưu hóa:

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

Gỡ lỗi TẮT, tối ưu hóa BẬT:

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

Bất ngờ mới. Chuyển từ VS2008 (C # 3) sang VS2010 (C # 4), làm cho UsingLambdaCombinedtốc độ nhanh hơn lambda bản địa.


Được rồi, tôi đã tìm ra một cách để cải thiện hiệu suất biên dịch lambda nhiều hơn một bậc. Đây là một mẹo; sau khi chạy trình biên dịch, 92% thời gian được dành cho:

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

Hmmmm ... Tại sao nó lại tạo một đại biểu mới trong mỗi lần lặp lại? Tôi không chắc chắn, nhưng giải pháp có trong một bài đăng riêng biệt.


3
Những thời gian này có chạy trong Visual Studio không? Nếu vậy, hãy lặp lại thời gian bằng cách sử dụng bản dựng ở chế độ Phát hành và chạy mà không cần gỡ lỗi (tức là Ctrl + F5 trong Visual Studio hoặc từ dòng lệnh). Ngoài ra, hãy xem xét sử dụng Stopwatchcho thời gian hơn là DateTime.Now.
Jim Mischel

12
Tôi không biết tại sao nó lại chậm hơn, nhưng kỹ thuật benchmark của bạn không tốt lắm. Trước hết, DateTime.Now chỉ chính xác đến 1/64 giây, do đó, sai số làm tròn số đo của bạn là lớn. Sử dụng Đồng hồ bấm giờ để thay thế; nó chính xác đến vài nano giây. Thứ hai, bạn đang đo lường cả thời gian để gõ mã (cuộc gọi đầu tiên) và mọi cuộc gọi tiếp theo; có thể làm giảm mức trung bình. (Mặc dù trong trường hợp này một MAX của một trăm ngàn có lẽ là đủ để trung bình đi những gánh nặng JIT, vẫn còn, nó là một thực tế xấu bao gồm nó trong mức trung bình.)
Eric Lippert

7
@Eric, lỗi làm tròn chỉ có thể có nếu trong mỗi thao tác DateTime.Now.Ticks được sử dụng, trước khi bắt đầu và sau khi kết thúc, số mili giây đủ cao để hiển thị sự khác biệt về hiệu suất.
Akash Kava

1
nếu sử dụng đồng hồ bấm giờ, tôi khuyên bạn nên làm theo bài viết này để đảm bảo kết quả chính xác: codeproject.com/KB/testing/stopwatch-measure-precise.aspx
Zach Green

1
@Eric, mặc dù tôi đồng ý rằng đây không phải là kỹ thuật đo lường chính xác nhất hiện có, nhưng chúng ta đang nói về thứ tự độ lớn của sự khác biệt. MAX đủ cao để giảm độ lệch đáng kể.
Hugo Sereno Ferreira

Câu trả lời:


43

Có thể là các lambdas bên trong không được biên dịch?!? Đây là một bằng chứng về khái niệm:

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

Và bây giờ là thời gian:

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

Chà! Nó không chỉ nhanh mà còn nhanh hơn lambda bản địa. ( Đầu cào ).


Tất nhiên đoạn mã trên chỉ đơn giản là quá khó để viết. Hãy làm một số phép thuật đơn giản:

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

Và một số thời gian, VS2010, BẬT tối ưu hóa, TẮT gỡ lỗi:

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

Bây giờ bạn có thể tranh luận rằng tôi không tạo toàn bộ biểu thức một cách động; chỉ là lời gọi chuỗi. Nhưng trong ví dụ trên, tôi tạo ra toàn bộ biểu thức. Và thời gian phù hợp. Đây chỉ là một phím tắt để viết ít mã hơn.


Theo hiểu biết của tôi, điều đang xảy ra là phương thức .Compile () không truyền các biên dịch đến các lambdas bên trong, và do đó, liên tục gọi CreateDelegate. Nhưng để thực sự hiểu được điều này, tôi rất muốn có một chuyên gia .NET bình luận một chút về nội dung đang diễn ra.

tại sao , oh tại sao nó bây giờ nhanh hơn lambda bản địa !?


1
Tôi đang suy nghĩ để chấp nhận câu trả lời của riêng mình, vì đó là câu trả lời có nhiều phiếu bầu nhất. Tôi có nên đợi thêm một thời gian nữa không?
Hugo Sereno Ferreira

Về những gì xảy ra với việc bạn nhận được mã nhanh hơn lambda gốc, bạn có thể muốn xem trang này về microbenchmarks (không có gì thực sự dành riêng cho Java, mặc dù có tên): code.google.com/p/caliper/wiki / JavaMicrobenchmarks
Blaisorblade 4/12/12

Đối với lý do tại sao lambda được biên dịch động nhanh hơn, tôi nghi ngờ rằng "sử dụng lambda", đang được chạy trước, đang bị phạt khi phải JIT một số mã.
Oskar Berggren

Tôi không biết chuyện gì đang xảy ra
nawfal

10

Gần đây tôi đã hỏi một câu hỏi gần như giống hệt nhau:

Hiệu suất của Biểu thức được biên dịch thành đại biểu

Các giải pháp đối với tôi là tôi không nên gọi Compilevào Expression, nhưng mà tôi nên gọi CompileToMethodvào nó và biên dịch Expressionmộtstatic phương thức trong một hợp ngữ động.

Như vậy:

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

Tuy nhiên, nó không lý tưởng. Tôi không hoàn toàn chắc chắn về loại nào áp dụng chính xác, nhưng tôi nghĩ rằng các loại được ủy nhiệm lấy làm tham số hoặc được trả về bởi ủy quyền phảipublicvà không chung chung. Nó phải không chung chung vì các loại chung chung dường như truy cậpSystem.__Canon là kiểu nội bộ được .NET sử dụng bên dưới cho các kiểu chung và điều này vi phạm publicquy tắc "phải là một loại).

Đối với những loại đó, bạn có thể sử dụng tốc độ chậm hơn Compile. Tôi phát hiện chúng theo cách sau:

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

Nhưng như tôi đã nói, điều này không lý tưởng và tôi vẫn muốn biết tại sao việc biên dịch một phương thức thành một hợp ngữ động lại đôi khi lại nhanh hơn một bậc. Và tôi nói đôi khi bởi vì tôi cũng đã thấy những trường hợp mà một Expressionbiên dịch với Compilenhanh như một phương pháp bình thường. Xem câu hỏi của tôi cho điều đó.

Hoặc nếu ai đó biết cách bỏ qua publicràng buộc "no non- type" với cụm động, điều đó cũng được hoan nghênh.


4

Các biểu thức của bạn không tương đương và do đó bạn nhận được kết quả sai lệch. Tôi đã viết một băng ghế thử nghiệm để kiểm tra điều này. Các bài kiểm tra bao gồm lệnh gọi lambda thông thường, biểu thức được biên dịch tương đương, biểu thức được biên dịch tương đương được tạo thủ công, cũng như các phiên bản đã soạn. Đây phải là những con số chính xác hơn. Thật thú vị, tôi không thấy có nhiều sự khác biệt giữa phiên bản đơn giản và sáng tác. Và các biểu thức được biên dịch tự nhiên chậm hơn nhưng chỉ bằng rất ít. Bạn cần một đầu vào đủ lớn và số lần lặp lại để có được một số con số tốt. Nó tạo ra sự khác biệt.

Đối với câu hỏi thứ hai của bạn, tôi không biết làm thế nào bạn có thể đạt được hiệu suất cao hơn từ việc này nên tôi không thể giúp bạn ở đó. Nó có vẻ tốt như nó sẽ nhận được.

Bạn sẽ tìm thấy câu trả lời của tôi cho câu hỏi thứ ba của bạn trong HandMadeLambdaExpression()phương pháp này. Không phải là biểu thức dễ tạo nhất do các phương thức mở rộng, nhưng có thể thực hiện được.

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

Và kết quả trên máy của tôi:

Lambda:
  Đã trôi qua: 340971948 123230 (mili giây)
  Trung bình: 340,971948 0,12323 (mili giây)
Biểu thức Lambda:
  Đã trôi qua: 357077202 129051 (mili giây)
  Trung bình: 357.077202 0.129051 (mili giây)
Biểu thức Lambda làm bằng tay:
  Đã trôi qua: 345029281 124696 (mili giây)
  Trung bình: 345.029281 0.124696 (mili giây)

Sáng tác:
  Đã trôi qua: 340409238 123027 (mili giây)
  Trung bình: 340,409238 0,123027 (mili giây)
Biểu thức tổng hợp:
  Đã trôi qua: 350800599 126782 (mili giây)
  Trung bình: 350.800599 0,126782 (mili giây)
Biểu thức sáng tác làm bằng tay:
  Đã trôi qua: 352811359 127509 (mili giây)
  Trung bình: 352.811359 0.127509 (mili giây)

3

Hiệu suất lambda được biên dịch trên các đại biểu có thể chậm hơn vì mã đã biên dịch trong thời gian chạy có thể không được tối ưu hóa tuy nhiên mã bạn đã viết thủ công và mã được biên dịch qua trình biên dịch C # được tối ưu hóa.

Thứ hai, nhiều biểu thức lambda có nghĩa là nhiều phương thức ẩn danh và việc gọi mỗi phương thức trong số chúng sẽ mất thêm ít thời gian so với việc đánh giá một phương thức thẳng. Ví dụ, gọi

Console.WriteLine(x);

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

là khác nhau, và với thứ hai cần thêm một chi phí nhỏ hơn theo quan điểm của trình biên dịch, thực tế là hai lệnh gọi khác nhau. Đầu tiên gọi x chính nó và sau đó gọi x là câu lệnh.

Vì vậy, Lambda kết hợp của bạn chắc chắn sẽ có hiệu suất chậm hơn biểu thức lambda đơn lẻ.

Và điều này độc lập với những gì đang thực thi bên trong, bởi vì bạn vẫn đang đánh giá logic chính xác, nhưng bạn đang thêm các bước bổ sung để trình biên dịch thực hiện.

Ngay cả sau khi cây biểu thức được biên dịch, nó sẽ không có tối ưu hóa và nó sẽ vẫn giữ nguyên cấu trúc phức tạp nhỏ, việc đánh giá và gọi nó có thể có thêm xác nhận, kiểm tra null, v.v. có thể làm chậm hiệu suất của biểu thức lambda đã biên dịch.


2
Nếu bạn quan sát kỹ, UsingLambdaCombinedbài kiểm tra đang kết hợp nhiều hàm lambda và hiệu suất của nó là rất gần UsingLambda. Về việc tối ưu hóa, tôi tin rằng chúng được xử lý bởi công cụ JIT và do đó mã được tạo trong thời gian chạy (sau khi biên dịch), cũng sẽ là mục tiêu của bất kỳ tối ưu hóa JIT nào.
Hugo Sereno Ferreira

1
Tối ưu hóa JIT và tối ưu hóa thời gian biên dịch là hai điều khác nhau mà bạn có thể tắt tối ưu hóa thời gian biên dịch trong cài đặt dự án. Thứ hai, biên dịch biểu thức có thể sẽ phát ra MSIL động mà sẽ chậm hơn một chút vì logic và trình tự hoạt động của nó sẽ chứa các kiểm tra rỗng và tính hợp lệ theo nhu cầu. Bạn có thể nhìn vào phản xạ về cách nó được biên dịch.
Akash Kava

2
Trong khi lý luận của bạn là đúng đắn, tôi phải không đồng ý với bạn về vấn đề cụ thể này (tức là, sự khác biệt về thứ tự độ lớn không phải do biên dịch tĩnh). Đầu tiên, bởi vì nếu bạn thực sự vô hiệu hóa tối ưu hóa thời gian biên dịch, sự khác biệt vẫn còn đáng kể. Thứ hai, vì tôi đã tìm ra cách để tối ưu hóa thế hệ động để chỉ chậm hơn một chút. Hãy để tôi cố gắng hiểu "tại sao" và tôi sẽ đăng kết quả.
Hugo Sereno Ferreira
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.