为什么String.IsNullOrEmpty比string.length减得快? [英] Why is String.IsNullOrEmpty faster than String.Length?

查看:311
本文介绍了为什么String.IsNullOrEmpty比string.length减得快?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

ILSpy显示, String.IsNullOrEmpty string.length减方面实现。不过,为什么是 String.IsNullOrEmpty(S)快于 s.Length == 0

例如,它的5%,在此测试中领先优势:

  VAR秒表= Enumerable.Range(0,4)。选择(_ =>新建秒表())。的ToArray();
VAR字符串=A,B ,, C,DE,女,, G,H ,,,, I,J ,, K,L,MN,OP,Q,R,STU,V,W,X,Y, Z,斯普利特('')。
VAR测试人员=新的函数功能:其中,字符串,布尔> [] {S =>小号==的String.Empty,S => s.Length == 0,S => String.IsNullOrEmpty(S)中,s =>小号==};
诠释计数= 0;
的for(int i = 0; I< 10000; ++ I){
    秒表[I%4]。开始();
    对于(INT J = 0; J< 1000; ++ j)条
        数+ = strings.Count(测试[I%4]);
    秒表[I%4] .Stop();
}
 

(其它基准测试显示了相似的结果。这其中最小克鲁夫特我的计算机上运行的影响。另外,顺便说一句,比较空字符串测试师出同在比 13%左右速度较慢IsNullOrEmpty

此外,为什么 IsNullOrEmpty 只有更快的x86,而在x64 string.length减约9%的速度?

更新:测试设​​置详细信息:.NET 4.0的64位运行的是Windows 7,英特尔酷睿i5处理器,编译控制台项目优化code已启用。然而,在模块加载燮preSS JIT优化也被启用(见接受的答案和注释)。

通过优化完全启用,长度 IsNullOrEmpty 更快的委托和删除其他开销约14%,如在此测试

  VAR字符串=A,B ,, C,DE,女,, G,H ,,,, I,J ,, K,L,MN,OP,Q, R 1,STU,V ,,W¯¯...,X ,,, YC,Z,斯普利特('')。
诠释计数= 0;
对于(UINT I = 0; I<亿; ++ I)
    数+ =字符串[我%32] .Length == 0? 1:0; //更换String.IsNullOrEmpty长度测试
 

解决方案

这是因为你从Visual Studio其中prevents JIT编译器优化code之内跑了基准测试。如果没有优化,这code制造的的 String.IsNullOrEmpty

  00000000推送EBP
00000001 MOV EBP,ESP
00000003分ESP,8
00000006 MOV DWORD PTR [EBP-8],ECX
00000009 CMP DWORD PTR DS:[00153144h] 0
00000010 00000017济
00000012电话64D85BDF
00000017 MOV ECX,DWORD PTR [EBP-8]
0000001a电话63EF7C0C
0000001f MOV DWORD PTR [EBP-4],EAX
00000022 MOVZX EAX,字节PTR [EBP-4]
00000026 MOV ESP,EBP
00000028弹出EBP
00000029 RET
 

和现在比较,产生的code的长度== 0

  00000000推送EBP
00000001 MOV EBP,ESP
00000003分ESP,8
00000006 MOV DWORD PTR [EBP-8],ECX
00000009 CMP DWORD PTR DS:[001E3144h] 0
00000010 00000017济
00000012电话64C95BDF
00000017 MOV ECX,DWORD PTR [EBP-8]
0000001a CMP DWORD PTR [ECX],ECX
0000001c电话64EAA65B
00000021 MOV DWORD PTR [EBP-4],EAX
00000024 CMP DWORD PTR [EBP-4],0
00000028 SETE人
0000002b MOVZX EAX,人
0000002e MOV ESP,EBP
00000030弹出EBP
00000031 RET
 

您可以看到,$ C $下的长度== 0 的做的一切,做$ C $下的 String.IsNullOrEmpty 的,但另外它试图像傻傻的再次转换布尔值(长度比较返回),以布尔,这使得它比的 String.IsNullOrEmpty 的慢。

如果您编译程序启用优化(释放模式),并直接从Windows中运行.exe文件,由JIT编译器生成code好得多。对于 String.IsNullOrEmpty 的是:

  001f0650推EBP
001f0651 MOV EBP,ESP
001f0653测试ECX,ECX
001f0655济001f0663
001f0657 CMP DWORD PTR [ECX + 4],0
001f065b SETE人
001f065e MOVZX EAX,人
001f0661 JMP 001f0668
001f0663 MOV EAX,1
001f0668和EAX,0FFh的
001f066d弹出EBP
001f066e RET
 

长度== 0 的:

  001406f0 CMP DWORD PTR [ECX + 4],0
001406f4 SETE人
001406f7 MOVZX EAX,人
001406fa RET
 

通过这个code,结果与预期一致,即长度== 0 的稍快于 String.IsNullOrEmpty 的。

另外值得一提的,是使用LINQ,拉姆达EX pressions并在基准计算模是不是一个好主意,因为这些操作慢(相对于字符串比较),使基准不准确的结果。

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

For example, it's 5% faster in this benchmark:

var stopwatches = Enumerable.Range(0, 4).Select(_ => new Stopwatch()).ToArray();
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 count = 0;
for (int i = 0; i < 10000; ++i) {
    stopwatches[i % 4].Start();
    for (int j = 0; j < 1000; ++j)
        count += strings.Count(testers[i % 4]);
    stopwatches[i % 4].Stop();
}

(Other benchmarks show similar results. This one minimized the effect of cruft running on my computer. Also, as an aside, the tests comparing to empty strings came out the same at about 13% slower than IsNullOrEmpty.)

Additionally, why is IsNullOrEmpty only faster on x86, whereas on x64 String.Length is about 9% faster?

Update: Test setup details: .NET 4.0 running on 64-bit Windows 7, Intel Core i5 processor, console project compiled with "Optimize code" enabled. However, "Suppress JIT optimization on module load" was also enabled (see accepted answer and comments).

With optimization fully enabled, Length is about 14% faster than IsNullOrEmpty with the delegate and other overhead removed, as in this test:

var strings = "A,B,,C,DE,F,,G,H,,,,I,J,,K,L,MN,OP,Q,R,,STU,V,,W,,X,,,Y,,Z,".Split(',');
int count = 0;
for (uint i = 0; i < 100000000; ++i)
    count += strings[i % 32].Length == 0 ? 1 : 0; // Replace Length test with String.IsNullOrEmpty

解决方案

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.

这篇关于为什么String.IsNullOrEmpty比string.length减得快?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆