首页 新闻 会员 周边

多if 与 switch模式匹配 的性能比较。

0
悬赏园豆:5 [待解决问题]

如下两段代码:

1、使用 switch模式匹配 是否能达到 多if分支 的效果?

2、使用 switch模式匹配 是否会比 多if分支 需要更多系统开销?影响有多大?

switch匹配模式:

        public async Task<IUser> AuthenticateAsync(string userName, string password, Action<string, string> reportError)
        {
            IUser user = null;
            SignInResult result = null;
            string p1 = string.Empty;
            string p2;
            switch (default(object))
            {
                case var _ when string.IsNullOrWhiteSpace(userName):
                    p1 = "UserName";
                    p2 = this.T["A user name is required."];
                    break;
                case var _ when string.IsNullOrWhiteSpace(password):
                    p1 = "Password";
                    p2 = this.T["A password is required."];
                    break;
                case var _ when (user = await this._userManager.FindByNameAsync(userName)) == null:
                    p2 = this.T["The specified username/password couple is invalid."];
                    break;
                case var _ when (result = await this._signInManager.CheckPasswordSignInAsync(user, password, true)).IsNotAllowed:
                    p2 = this.T["The specified user is not allowed to sign in."];
                    break;
                case var _ when result.RequiresTwoFactor:
                    p2 = this.T["The specified user is not allowed to sign in using password authentication."];
                    break;
                case var _ when !result.Succeeded:
                    p2 = this.T["The specified username/password couple is invalid."];
                    break;
                default:
                    return user;
            }
            reportError(p1, p2);
            return null;
        }

多if分支:

        public async Task<IUser> AuthenticateAsync(string userName, string password, Action<string, string> reportError)
        {
            if (string.IsNullOrWhiteSpace(userName))
            {
                reportError("UserName", this.T["A user name is required."]);
                return null;
            }

            if (string.IsNullOrWhiteSpace(password))
            {
                reportError("Password", this.T["A password is required."]);
                return null;
            }

            var user = await this._userManager.FindByNameAsync(userName);
            if (user == null)
            {
                reportError(string.Empty, this.T["The specified username/password couple is invalid."]);
                return null;
            }

            var result = await this._signInManager.CheckPasswordSignInAsync(user, password, lockoutOnFailure: true);
            if (result.IsNotAllowed)
            {
                reportError(string.Empty, this.T["The specified user is not allowed to sign in."]);
                return null;
            }
            else if (result.RequiresTwoFactor)
            {
                reportError(string.Empty, this.T["The specified user is not allowed to sign in using password authentication."]);
                return null;
            }
            else if (!result.Succeeded)
            {
                reportError(string.Empty, this.T["The specified username/password couple is invalid."]);
                return null;
            }

            return user;
        }
triout的主页 triout | 初学一级 | 园豆:13
提问于:2019-05-08 12:44
< >
分享
所有回答(4)
0

switch 性能更好,见 stackoverflow 上的回答

The compiler can build jump tables where applicable. For example, when you use the reflector to look at the code produced, you will see that for huge switches on strings, the compiler will actually generate code that uses a hash table to dispatch these. The hash table uses the strings as keys and delegates to the case codes as values.
This has asymptotic better runtime than lots of chained if tests and is actually faster even for relatively few strings.

dudu | 园豆:30979 (高人七级) | 2019-05-08 14:23
1

就提问中的情况来说,没有性能上的差异,请看一个简化的例子:

using System;
public class C {
    public string Name;
}

public class Program
{
    public void SwitchMatching(C c)
    {
        switch(default(object))
        {
            case var _ when c.Name == "Foo":
                Console.WriteLine("Foo");
                break;
            case var _ when String.IsNullOrEmpty(c.Name):
                Console.WriteLine("Null Or Empty");
                break;
            default:
                break;
        }
    }
    
    public void IfTest(C c)
    {
        if(c.Name == "Foo")
        {
            Console.WriteLine("Foo");
            return;
        }
        if(String.IsNullOrEmpty(c.Name))
        {
            Console.WriteLine("Null Or Empty");
            return;
        }
    }
}

对应的 IL 为:

    // Methods
    .method public hidebysig 
        instance void SwitchMatching (
            class C c
        ) cil managed 
    {
        // Method begins at RVA 0x205c
        // Code size 71 (0x47)
        .maxstack 2
        .locals init (
            [0] object,
            [1] object
        )

        IL_0000: nop
        IL_0001: ldnull
        IL_0002: stloc.1
        IL_0003: ldnull
        IL_0004: stloc.0
        // sequence point: hidden
        IL_0005: br.s IL_0007

        IL_0007: ldarg.1
        IL_0008: ldfld string C::Name
        IL_000d: ldstr "Foo"
        IL_0012: call bool [mscorlib]System.String::op_Equality(string, string)
        IL_0017: brtrue.s IL_001b

        // sequence point: hidden
        IL_0019: br.s IL_0028

        IL_001b: ldstr "Foo"
        IL_0020: call void [mscorlib]System.Console::WriteLine(string)
        IL_0025: nop
        IL_0026: br.s IL_0046

        IL_0028: ldarg.1
        IL_0029: ldfld string C::Name
        IL_002e: call bool [mscorlib]System.String::IsNullOrEmpty(string)
        IL_0033: brtrue.s IL_0037

        // sequence point: hidden
        IL_0035: br.s IL_0044

        IL_0037: ldstr "Null Or Empty"
        IL_003c: call void [mscorlib]System.Console::WriteLine(string)
        IL_0041: nop
        IL_0042: br.s IL_0046

        IL_0044: br.s IL_0046

        IL_0046: ret
    } // end of method Program::SwitchMatching

    .method public hidebysig 
        instance void IfTest (
            class C c
        ) cil managed 
    {
        // Method begins at RVA 0x20b0
        // Code size 65 (0x41)
        .maxstack 2
        .locals init (
            [0] bool,
            [1] bool
        )

        IL_0000: nop
        IL_0001: ldarg.1
        IL_0002: ldfld string C::Name
        IL_0007: ldstr "Foo"
        IL_000c: call bool [mscorlib]System.String::op_Equality(string, string)
        IL_0011: stloc.0
        // sequence point: hidden
        IL_0012: ldloc.0
        IL_0013: brfalse.s IL_0023

        IL_0015: nop
        IL_0016: ldstr "Foo"
        IL_001b: call void [mscorlib]System.Console::WriteLine(string)
        IL_0020: nop
        IL_0021: br.s IL_0040

        IL_0023: ldarg.1
        IL_0024: ldfld string C::Name
        IL_0029: call bool [mscorlib]System.String::IsNullOrEmpty(string)
        IL_002e: stloc.1
        // sequence point: hidden
        IL_002f: ldloc.1
        IL_0030: brfalse.s IL_0040

        IL_0032: nop
        IL_0033: ldstr "Null Or Empty"
        IL_0038: call void [mscorlib]System.Console::WriteLine(string)
        IL_003d: nop
        IL_003e: br.s IL_0040

        IL_0040: ret
    } // end of method Program::IfTest

可以看到,编译器生成的 IL 几乎没有区别,你只是换了种方法来写 if-else 而已

不如隐茶去 | 园豆:559 (小虾三级) | 2019-05-08 18:14
0

这个问题很早就讨论了,在这层次的语言这两者主要是语法。
楼下的说法有根据但特殊,没记错的siwtch的翻译并不特定。
性能你还是去c反编看比较有意义,jump的指令对比。

花飘水流兮 | 园豆:13560 (专家六级) | 2019-05-09 10:03
0

switch分支较多时,编译时是会生成内部跳转表的。当使用内部跳转表时,就不再与每个case进行比较,而是根据条件语句的值直接跳转到对应的地址去,效率比if else要高很多。
当然switch分支较少时,不会使用跳转表,与if else是没有区别的。
有问题可以加我QQ 2057388734

Joey先生 | 园豆:232 (菜鸟二级) | 2019-05-13 15:21
清除回答草稿
   您需要登录以后才能回答,未注册用户请先注册