优化功能声明位置

时间:2017-07-31 08:45:22

标签: c# optimization delegates func

我有两个Func<T,T2>个对象将在一个将被重复调用的方法中使用(虽然这个方法只能从这个位置调用),声明{{1}会更有效吗?在父函数中(以便它们只需要实例化一次)并将它们传递给子函数或将它们放在子函数中(以便它们更接近使用)。

我不太了解Func类或Func编译器优化算法的内部工作原理。

出于说明目的:

情况1:

C#

情况2:

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

对我而言,情境1使代码更清晰,因为 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; } 被宣布更接近他们使用的位置,因此它使其更具可读性。然而,逻辑告诉我情况2应该更快更有效。

1 个答案:

答案 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
    }
}

查看IL test1()。它必须新建几个对象并做其他一些事情。

现在查看test3()的IL,它使用传入的Func<>个参数。这样效率会更高,但请注意,它仍然需要callvirtInvoke() Func<>进行test2()调用。

现在看看callvirt,它使用本地函数。它只是直接调用函数而无需Invoke()test1()。这显然好多了。

但请注意,在所有方法中,函数本身的代码在编译时只编译一次 - 即使对于test1(),每次调用mutate_if时也不会这样做,所以没有开销那个观点。