2017-07-31 6 views
1

私は2つのFunc<T,T2>オブジェクトを繰り返し呼び出すメソッドで使用しますが(このメソッドはこの1つの場所からのみ呼び出されます)、Funcを宣言する方が効率的でしょうか? (一度インスタンス化するだけで済むように)子関数に渡すか、子関数の中に入れる(使い方に近いように)。Func宣言の位置を最適化する

FuncクラスまたはC#コンパイラの最適化アルゴリズムの内部動作についてはあまり知られていません。説明の目的のために

状況1:

static void ThreadWorker(ref int current, ref int count) 
    { 
     bool isFinished = false; 
     while (!isFinished) 
     { 
      int workingValue = current++; 
      if (workingValue > TARGET) 
      { 
       isFinished = true; 
      } 
      else 
      { 
       if (EightyNineChain(workingValue)) 
       { 
        count++; 
       } 
      } 
     } 


    } 

    private static bool EightyNineChain(int value) 
    { 
     Func<int, int[]> getDigits = v => v.ToString().Select(x => int.Parse(x.ToString()).ToArray(); 
     Func<int[], int> getDigitSquareSum = x => (int)x.Select(d => Math.Pow(d, 2)).Sum(); 
     //More code here 
     return result; 
    } 

や状況2:Func sはに近づく宣言されているように私に

static void ThreadWorker(ref int current, ref int count) 
    { 
     Func<int, int[]> getDigits = v => v.ToString().Select(x => int.Parse(x.ToString()).ToArray(); 
     Func<int[], int> getDigitSquareSum = x => (int)x.Select(d => Math.Pow(d, 2)).Sum(); 
     bool isFinished = false; 
     while (!isFinished) 
     { 
      int workingValue = current++; 
      if (workingValue > TARGET) 
      { 
       isFinished = true; 
      } 
      else 
      { 
       if (EightyNineChain(workingValue, getDigits, getDigitSquareSum)) 
       { 
        count++; 
       } 
      } 
     } 


    } 

    private static bool EightyNineChain(int value, Func<int,int[]> getDigits, Func<int[],int> getDigitSquareSum) 
    { 

     //More code here 
     return result; 
    } 

、状況1は、コードクリーナーを作りますそれらが使用されている場所では、より読みやすくなります。しかし、ロジックは、状況2がより速く効率的でなければならないことを私に伝えます。

+1

これらが動的な 'Func <>'の理由はありますか?なぜそれらを通常の機能にしないのですか? – nvoigt

+0

正直なところ、この特定の文脈では、それらは規則的な機能である可能性があり、今私はそれらを変更しました(ちょうど怠け者でした)が、私の質問はまだ有用な回答をもたらす可能性があると信じています –

答えて

1

ここでの最良のアプローチは、C#7のローカル関数を使用することです。

は、次のコードを考えてみましょう:

using System; 
using System.Linq; 

namespace ConsoleApp1 
{ 
    public class Program 
    { 
     static void Main() 
     { 
      Console.WriteLine(test1(1)); 
      Console.WriteLine(test2(1)); 

      Func<int, int[]> getDigits = v => v.ToString().Select(Convert.ToInt32).ToArray(); 
      Func<int[], int> getDigitSquareSum = x => (int)x.Select(d => Math.Pow(d, 2)).Sum(); 

      Console.WriteLine(test3(1, getDigits, getDigitSquareSum)); 
     } 

     static int test1(int value) // Use local Func<> 
     { 
      Func<int, int[]> getDigits = v => v.ToString().Select(Convert.ToInt32).ToArray(); 
      Func<int[], int> getDigitSquareSum = x => (int)x.Select(d => Math.Pow(d, 2)).Sum(); 

      var a = getDigits(value); 
      var b = getDigitSquareSum(a); 

      return b; 
     } 

     static int test2(int value) // Use local function. 
     { 
      int[] digits(int v) => v.ToString().Select(Convert.ToInt32).ToArray(); 
      int digitSquareSum(int[] x) => (int) x.Select(d => Math.Pow(d, 2)).Sum(); 

      var a = digits(value); 
      var b = digitSquareSum(a); 

      return b; 
     } 

     // Pass in Func<> 
     static int test3(int value, Func<int, int[]> getDigits, Func<int[], int> getDigitSquareSum) 
     { 
      var a = getDigits(value); 
      var b = getDigitSquareSum(a); 

      return b; 
     } 
    } 
} 

これは、次のILコードに変換されます。

.class public auto ansi beforefieldinit ConsoleApp1.Program 
    extends [mscorlib]System.Object 
{ 
    .method public hidebysig specialname rtspecialname instance void .ctor() cil managed 
    { 
     IL_0000: ldarg.0 
     IL_0001: call instance void [mscorlib]System.Object::.ctor() 
     IL_0006: ret 
    } 

    .method private hidebysig static void Main() cil managed 
    { 
     .entrypoint 
     .locals init (
      [0] class [mscorlib]System.Func`2<int32, int32[]> getDigits, 
      [1] class [mscorlib]System.Func`2<int32[], int32> getDigitSquareSum 
     ) 

     IL_0000: ldc.i4.1 
     IL_0001: call int32 ConsoleApp1.Program::test1(int32) 
     IL_0006: call void [mscorlib]System.Console::WriteLine(int32) 
     IL_000b: ldc.i4.1 
     IL_000c: call int32 ConsoleApp1.Program::test2(int32) 
     IL_0011: call void [mscorlib]System.Console::WriteLine(int32) 
     IL_0016: ldsfld class [mscorlib]System.Func`2<int32, int32[]> ConsoleApp1.Program/'<>c'::'<>9__0_0' 
     IL_001b: dup 
     IL_001c: brtrue.s IL_0035 

     IL_001e: pop 
     IL_001f: ldsfld class ConsoleApp1.Program/'<>c' ConsoleApp1.Program/'<>c'::'<>9' 
     IL_0024: ldftn instance int32[] ConsoleApp1.Program/'<>c'::'<Main>b__0_0'(int32) 
     IL_002a: newobj instance void class [mscorlib]System.Func`2<int32, int32[]>::.ctor(object, native int) 
     IL_002f: dup 
     IL_0030: stsfld class [mscorlib]System.Func`2<int32, int32[]> ConsoleApp1.Program/'<>c'::'<>9__0_0' 

     IL_0035: stloc.0 
     IL_0036: ldsfld class [mscorlib]System.Func`2<int32[], int32> ConsoleApp1.Program/'<>c'::'<>9__0_1' 
     IL_003b: dup 
     IL_003c: brtrue.s IL_0055 

     IL_003e: pop 
     IL_003f: ldsfld class ConsoleApp1.Program/'<>c' ConsoleApp1.Program/'<>c'::'<>9' 
     IL_0044: ldftn instance int32 ConsoleApp1.Program/'<>c'::'<Main>b__0_1'(int32[]) 
     IL_004a: newobj instance void class [mscorlib]System.Func`2<int32[], int32>::.ctor(object, native int) 
     IL_004f: dup 
     IL_0050: stsfld class [mscorlib]System.Func`2<int32[], int32> ConsoleApp1.Program/'<>c'::'<>9__0_1' 

     IL_0055: stloc.1 
     IL_0056: ldc.i4.1 
     IL_0057: ldloc.0 
     IL_0058: ldloc.1 
     IL_0059: call int32 ConsoleApp1.Program::test3(int32, class [mscorlib]System.Func`2<int32, int32[]>, class [mscorlib]System.Func`2<int32[], int32>) 
     IL_005e: call void [mscorlib]System.Console::WriteLine(int32) 
     IL_0063: ret 
    } 

    .method private hidebysig static int32 test1 (
      int32 'value' 
     ) cil managed 
    { 
     .locals init (
      [0] class [mscorlib]System.Func`2<int32, int32[]> getDigits, 
      [1] int32[] a 
     ) 

     IL_0000: ldsfld class [mscorlib]System.Func`2<int32, int32[]> ConsoleApp1.Program/'<>c'::'<>9__1_0' 
     IL_0005: dup 
     IL_0006: brtrue.s IL_001f 

     IL_0008: pop 
     IL_0009: ldsfld class ConsoleApp1.Program/'<>c' ConsoleApp1.Program/'<>c'::'<>9' 
     IL_000e: ldftn instance int32[] ConsoleApp1.Program/'<>c'::'<test1>b__1_0'(int32) 
     IL_0014: newobj instance void class [mscorlib]System.Func`2<int32, int32[]>::.ctor(object, native int) 
     IL_0019: dup 
     IL_001a: stsfld class [mscorlib]System.Func`2<int32, int32[]> ConsoleApp1.Program/'<>c'::'<>9__1_0' 

     IL_001f: stloc.0 
     IL_0020: ldsfld class [mscorlib]System.Func`2<int32[], int32> ConsoleApp1.Program/'<>c'::'<>9__1_1' 
     IL_0025: dup 
     IL_0026: brtrue.s IL_003f 

     IL_0028: pop 
     IL_0029: ldsfld class ConsoleApp1.Program/'<>c' ConsoleApp1.Program/'<>c'::'<>9' 
     IL_002e: ldftn instance int32 ConsoleApp1.Program/'<>c'::'<test1>b__1_1'(int32[]) 
     IL_0034: newobj instance void class [mscorlib]System.Func`2<int32[], int32>::.ctor(object, native int) 
     IL_0039: dup 
     IL_003a: stsfld class [mscorlib]System.Func`2<int32[], int32> ConsoleApp1.Program/'<>c'::'<>9__1_1' 

     IL_003f: ldloc.0 
     IL_0040: ldarg.0 
     IL_0041: callvirt instance int32[] class [mscorlib]System.Func`2<int32, int32[]>::Invoke(!0) 
     IL_0046: stloc.1 
     IL_0047: ldloc.1 
     IL_0048: callvirt instance int32 class [mscorlib]System.Func`2<int32[], int32>::Invoke(!0) 
     IL_004d: ret 
    } 

    .method private hidebysig static int32 test2 (
      int32 'value' 
     ) cil managed 
    { 
     IL_0000: ldarg.0 
     IL_0001: call int32[] ConsoleApp1.Program::'<test2>g__digits2_0'(int32) 
     IL_0006: call int32 ConsoleApp1.Program::'<test2>g__digitSquareSum2_1'(int32[]) 
     IL_000b: ret 
    } 

    .method private hidebysig static int32 test3 (
      int32 'value', 
      class [mscorlib]System.Func`2<int32, int32[]> getDigits, 
      class [mscorlib]System.Func`2<int32[], int32> getDigitSquareSum 
     ) cil managed 
    { 
     .locals init (
      [0] int32[] a 
     ) 

     IL_0000: ldarg.1 
     IL_0001: ldarg.0 
     IL_0002: callvirt instance int32[] class [mscorlib]System.Func`2<int32, int32[]>::Invoke(!0) 
     IL_0007: stloc.0 
     IL_0008: ldarg.2 
     IL_0009: ldloc.0 
     IL_000a: callvirt instance int32 class [mscorlib]System.Func`2<int32[], int32>::Invoke(!0) 
     IL_000f: ret 
    } 
} 

test1()のためのILを見てみましょう。いくつかのオブジェクトを新しくしなければならず、他にもたくさんのことをする必要があります。

test3()のILを調べ、渡されたパラメータはFunc<>です。これははるかに効率的ですが、それでもへのcallvirtコールをFunc<>で行う必要があることに注意してください。

ローカル機能を使用するtest2()を参照してください。それはcallvirtまたはInvoke()を必要とせずに直接関​​数を呼び出すだけです。それは明らかにずっと良いです。

しかし、すべてのメソッドでは、関数自体のコードはコンパイル時に一度だけコンパイルされることに注意してください。test1()でも、test1()が呼び出されるたびに実行されないため、その観点からオーバーヘッドはありません。

+0

非常に徹底的な答えと、 C#7ありがとう! –

関連する問題