Why is String.IsNullOrEmpty faster than String.Length?

前端 未结 7 2054
心在旅途
心在旅途 2020-12-14 17:28

ILSpy shows that String.IsNullOrEmpty is implemented in terms of String.Length. But then why is String.IsNullOrEmpty(s) faster than

相关标签:
7条回答
  • 2020-12-14 17:46

    it may be caused by the types of the involved variables. *Empty seems to use a boolean, length an int (i guess).

    Peace !

    • : edit
    0 讨论(0)
  • 2020-12-14 17:51

    You test is wrong somethere. IsNullOrEmpty can't be faster by definition, since it makes additional null comparison operation, and then tests the Length.

    So the answer can be: it's faster because of your test. However even your code shows that IsNullOrEmpty is consistently slower on my machine in both x86 and x64 modes.

    0 讨论(0)
  • 2020-12-14 17:51

    I believe your test is not correct:

    This test shows that string.IsNullOrEmpty is always slower than s.Length==0 because it performs an additional null check:

    var strings = "A,B,,C,DE,F,,G,H,,,,I,J,,K,L,MN,OP,Q,R,STU,V,W,X,Y,Z,".Split(',');
    var testers = new Func<string, bool>[] { 
        s => s == String.Empty, 
        s => s.Length == 0, 
        s => String.IsNullOrEmpty(s), 
        s => s == "" ,
    };
    int n = testers.Length;
    var stopwatches = Enumerable.Range(0, testers.Length).Select(_ => new Stopwatch()).ToArray();
    int count = 0;
    for(int i = 0; i < n; ++i) { // iterate testers one by one
        Stopwatch sw = stopwatches[i];
        var tester = testers[i];
        sw.Start();
        for(int j = 0; j < 10000000; ++j) // increase this count for better precision
            count += strings.Count(tester);
        sw.Stop();
    }
    for(int i = 0; i < testers.Length; i++)
        Console.WriteLine(stopwatches[i].ElapsedMilliseconds);
    

    Results:

    6573
    5328
    5488
    6419
    

    You can use s.Length==0 when you are ensure that target data does not contains null strings. In other cases I suggest you use the String.IsNullOrEmpty.

    0 讨论(0)
  • 2020-12-14 17:59

    I think it is impossible IsNullOrEmpty to be faster because as all the rest said it also makes a check for null. But faster or not the difference is going to be so small, that this gives a plus on using IsNullOrEmpty just because of this additional null check that makes your code safer.

    0 讨论(0)
  • 2020-12-14 18:01

    It's because you ran your benchmark from within Visual Studio which prevents JIT compiler from optimizing code. Without optimizations, this code is produced for String.IsNullOrEmpty

    00000000   push        ebp 
    00000001   mov         ebp,esp 
    00000003   sub         esp,8 
    00000006   mov         dword ptr [ebp-8],ecx 
    00000009   cmp         dword ptr ds:[00153144h],0 
    00000010   je          00000017 
    00000012   call        64D85BDF 
    00000017   mov         ecx,dword ptr [ebp-8] 
    0000001a   call        63EF7C0C 
    0000001f   mov         dword ptr [ebp-4],eax 
    00000022   movzx       eax,byte ptr [ebp-4] 
    00000026   mov         esp,ebp 
    00000028   pop         ebp 
    00000029   ret 
    

    and now compare it to code produced for Length == 0

    00000000   push   ebp 
    00000001   mov    ebp,esp 
    00000003   sub    esp,8 
    00000006   mov    dword ptr [ebp-8],ecx 
    00000009   cmp    dword ptr ds:[001E3144h],0 
    00000010   je     00000017 
    00000012   call   64C95BDF 
    00000017   mov    ecx,dword ptr [ebp-8] 
    0000001a   cmp    dword ptr [ecx],ecx 
    0000001c   call   64EAA65B 
    00000021   mov    dword ptr [ebp-4],eax 
    00000024   cmp    dword ptr [ebp-4],0 
    00000028   sete   al 
    0000002b   movzx  eax,al 
    0000002e   mov    esp,ebp 
    00000030   pop    ebp 
    00000031   ret 
    

    You can see, that code for Length == 0 does everything that does code for String.IsNullOrEmpty, but additionally it tries something like foolishly convert boolean value (returned from length comparison) again to boolean and this makes it slower than String.IsNullOrEmpty.

    If you compile program with optimizations enabled (Release mode) and run .exe file directly from Windows, code generated by JIT compiler is much better. For String.IsNullOrEmpty it is:

    001f0650   push    ebp
    001f0651   mov     ebp,esp
    001f0653   test    ecx,ecx
    001f0655   je      001f0663
    001f0657   cmp     dword ptr [ecx+4],0
    001f065b   sete    al
    001f065e   movzx   eax,al
    001f0661   jmp     001f0668
    001f0663   mov     eax,1
    001f0668   and     eax,0FFh
    001f066d   pop     ebp
    001f066e   ret
    

    and for Length == 0:

    001406f0   cmp     dword ptr [ecx+4],0
    001406f4   sete    al
    001406f7   movzx   eax,al
    001406fa   ret
    

    With this code, result are as expected, i.e. Length == 0 is slightly faster than String.IsNullOrEmpty.

    It's also worth mentioning, that using Linq, lambda expressions and computing modulo in your benchmark is not such a good idea, because these operations are slow (relatively to string comparison) and make result of benchmark inaccurate.

    0 讨论(0)
  • 2020-12-14 18:05

    Your benchmark does not measure String.IsNullOrEmpty vs String.Length, but rather how different lambda expressions are generated to functions. I.e. it is not very surprising that delegate that just contains single function call (IsNullOrEmpty) is faster than one with function call and comparison (Length == 0).

    To get comparison of actuall call - write code that calls them directly without delegates.

    EDIT: My rough measurements show that delegate version with IsNullOrEmpty is slightly faster then the rest, while direct calls to the same comparision are in reverse order (and about twice faster due to significantly less number of extra code) on my machine. Results likely to wary between machines, x86/x64 mode, as well between versions of runtime. For practical purposes I would consider all 4 ways are about the same if you need to use them in LINQ queries.

    Overall I doubt there will be measurable difference in real program cased by choice between these methods, so pick the one that is most readable to you and use it. I generally prefer IsNullOrEmpty since it gives less chance to get ==/!= wrong in a condition.

    Removal of string manipulation altogether from time critical code will likley bring much higer benifit that picking between these choices, also dropping LINQ for critical code is an option. As always - make sure to measure overall program speed in real life scenario.

    0 讨论(0)
提交回复
热议问题