컴파일 된 C # Lambda 표현식 성능


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, 디버깅 ON에 대한 결과가 나타납니다.

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

디버깅을 끄면 :

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

놀람 . 컴파일 된 표현식은 다른 대안보다 약 17 배 느립니다. 이제 질문이 있습니다.

  1. 동등하지 않은 표현을 비교하고 있습니까?
  2. .NET이 컴파일 된 표현식을 "최적화"하도록 만드는 메커니즘이 있습니까?
  3. 동일한 체인 호출을 l.Where(i => i % 2 == 0).Where(i => i > 5);프로그래밍 방식으로 어떻게 표현 합니까?

더 많은 통계. Visual Studio 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네이티브 람다보다 빠릅니다.


좋아, 나는 람다 컴파일 성능을 10 배 이상 향상시킬 수있는 방법을 찾았다. 팁 이요; 프로파일 러를 실행 한 후 92 %의 시간이 다음에 소비됩니다.

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

흠 ... 모든 반복에서 새 델리게이트를 만드는 이유는 무엇입니까? 확실하지 않지만 솔루션은 별도의 게시물에 따릅니다.


3
Visual Studio에서 이러한 타이밍이 실행되고 있습니까? 그렇다면 릴리스 모드 빌드를 사용하여 타이밍을 반복하고 디버깅하지 않고 실행합니다 (예 : Visual Studio의 경우 Ctrl + F5 또는 명령 줄에서). 또한, 사용을 고려 Stopwatch보다는 타이밍에 대해 DateTime.Now.
Jim Mischel 2011

12
왜 느린 지 모르겠지만 벤치 마크 기술은 그리 좋지 않습니다. 먼저 DateTime.Now는 1/64 초까지만 정확하므로 측정 반올림 오류가 큽니다. 대신 스톱워치를 사용하십시오. 수 나노초까지 정확합니다. 둘째, 코드 (첫 번째 호출)와 모든 후속 호출을 jit하는 데 걸리는 시간을 모두 측정합니다. 평균을 떨어 뜨릴 수 있습니다. (이 경우 MAX가 10만이면 jit 부담을 평균화하기에 충분하지만 평균에 포함하는 것은 좋지 않습니다.)
Eric Lippert 2011

7
@Eric, 반올림 오류는 각 작업에서 DateTime.Now.Ticks를 사용하고 시작 전과 종료 후 밀리 초 수가 성능 차이를 표시 할만큼 충분히 높은 경우에만 발생할 수 있습니다.
Akash Kava 2011

1
스톱워치를 사용하는 경우 정확한 결과를 보장하기 위해이 기사를 따르는 것이 좋습니다. codeproject.com/KB/testing/stopwatch-measure-precise.aspx
Zach Green

1
@Eric, 사용 가능한 가장 정확한 측정 기술이 아니라는 데 동의하지만 우리는 차이의 크기에 대해 이야기하고 있습니다. MAX는 상당한 편차를 줄이기에 충분히 높습니다.
Hugo Sereno Ferreira 2011

답변:


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
가장 많은 표를 얻은 것이기 때문에 내 답변을 수락 할 생각입니다. 좀 더 기다려야하나요?
Hugo Sereno Ferreira 2011

네이티브 람다보다 코드를 더 빨리 가져 오면 무슨 일이 벌어지는 지에 대해서는 microbenchmarks에 대한이 페이지를 살펴볼 수 있습니다 (실제로 Java에 한정되지 않고 이름에도 불구하고) : code.google.com/p/caliper/wiki / JavaMicrobenchmarks
Blaisorblade

동적으로 컴파일 된 람다가 더 빠른 이유에 관해서는 "람다 사용"이 먼저 실행되면 일부 코드를 JIT해야하는 불이익을받는 것으로 의심됩니다.
Oskar Berggren 2013 년

어떤 일이 일어나고 있는지 모르겠습니다. 컴파일 된 표현식을 테스트하고 필드와 속성을 설정하고 가져 오기 위해 createelegate를 만들었을 때 createdelegate는 속성의 경우 훨씬 빠르지 만 컴파일은 필드의 경우 매우 약간 빠릅니다
nawfal

10

최근에 거의 동일한 질문을했습니다.

대리자로 컴파일 된 식의 성능

나를 위해이 솔루션은 내가 전화를 안이었다 CompileExpression,하지만 난 전화를해야 CompileToMethod그것을하고 컴파일 ExpressionA를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동적 어셈블리 에서 "no non- type"제약 조건 을 우회하는 방법을 알고있는 사람도 환영합니다.


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

내 컴퓨터의 결과 :

Lambda :
  경과 : 340971948 123230 (ms)
  평균 : 340.971948 0.12323 (ms)
람다 식 :
  경과 : 357077202 129051 (ms)
  평균 : 357.077202 0.129051 (ms)
손으로 만든 Lambda 표현 :
  경과 : 345029281 124696 (ms)
  평균 : 345.029281 0.124696 (ms)

구성 :
  경과 : 340409238 123027 (ms)
  평균 : 340.409238 0.123027 (ms)
구성된 표현 :
  경과 : 350800599 126782 (ms)
  평균 : 350.800599 0.126782 (ms)
손으로 만든 구성된 표현 :
  경과 : 352811359 127509 (ms)
  평균 : 352.811359 0.127509 (ms)

3

런타임에 컴파일 된 코드는 최적화되지 않을 수 있지만 수동으로 작성하고 C # 컴파일러를 통해 컴파일 된 코드는 최적화되어 있기 때문에 대리자를 통해 컴파일 된 람다 성능이 느려질 수 있습니다.

둘째, 여러 람다 식은 여러 익명 메서드를 의미하며 각 메서드를 호출하면 직선 메서드를 평가하는 것보다 약간의 추가 시간이 걸립니다. 예를 들어

Console.WriteLine(x);

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

컴파일러의 관점에서 볼 때 두 번째로 약간 더 많은 오버 헤드가 필요하며 실제로 두 번의 다른 호출이 필요합니다. 먼저 x 자체를 호출 한 다음 해당 호출 x의 문 내에서 호출합니다.

따라서 결합 된 Lambda는 단일 람다 표현식에 비해 성능이 거의 저하되지 않습니다.

그리고 이것은 올바른 논리를 평가하고 있지만 컴파일러가 수행 할 추가 단계를 추가하고 있기 때문에 내부에서 실행되는 것과는 독립적입니다.

표현식 트리가 컴파일 된 후에도 최적화되지 않고 여전히 약간 복잡한 구조를 유지하며 평가하고 호출하면 컴파일 된 람다 표현식의 성능을 저하시킬 수있는 추가 유효성 검사, 널 검사 등이있을 수 있습니다.


2
자세히 살펴보면 UsingLambdaCombined테스트는 여러 람다 함수를 결합하고 있으며 성능은 UsingLambda. 최적화와 관련하여, JIT 엔진에 의해 처리되므로 컴파일 후 런타임 생성 코드도 JIT 최적화의 대상이 될 것이라고 확신했습니다.
Hugo Sereno Ferreira

1
JIT 최적화와 컴파일 시간 최적화는 프로젝트 설정에서 컴파일 시간 최적화를 끌 수있는 두 가지 다른 것입니다. 둘째, 표현식 컴파일은 아마도 동적 MSIL을 방출 할 것입니다. 이것은 논리와 연산 순서가 필요에 따라 널 검사와 유효성을 포함하기 때문에 조금 더 느릴 것입니다. 리플렉터에서 어떻게 컴파일되는지 볼 수 있습니다.
Akash Kava 2011

2
당신의 추론은 타당하지만, 나는이 특정한 문제에 대해 당신의 의견에 동의하지 않습니다 (즉, 크기 차이는 정적 컴파일 때문이 아닙니다). 첫째, 실제로 컴파일 시간 최적화를 비활성화하더라도 그 차이는 여전히 상당하기 때문입니다. 둘째, 동적 생성을 약간 더 느리게 최적화하는 방법을 이미 찾았 기 때문입니다. "이유"를 이해하고 결과를 게시하겠습니다.
Hugo Sereno Ferreira 2011
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.