由浅入深CIL系列:4.抛砖引玉:使用CIL来分析string类型在.NET运算中的性能和避免装箱

简介:

          一、在.NET中string是一种特殊的引用类型,它一旦被赋值在堆上的地址即不可改变,之后对其进行的字符串相加等操作之后的结果都指向另外一个堆地址,而非原来的字符串地址。现在我们看以下一段C#代码以观察string在实际编码过程中的使用。

 
  1. class Program 
  2. static void Main(string[] args) 
  3. //打印One Word 
  4. string str1 = "One"
  5. str1 += " Word"
  6. Console.WriteLine(str1); 
  7.  
  8. string str2 = "One"+" Word"
  9. Console.WriteLine(str2); 
  10.  
  11. //打印One Word43 
  12. int i = 43; 
  13. Console.WriteLine(str1+i); 
  14.  
  15. Console.WriteLine(str1 + i.ToString()); 

        二、上面的C#生成的CIL代码如下:

 
  1. .method private hidebysig static void Main(string[] args) cil managed 
  2. //初始化 
  3. .entrypoint 
  4. // 代码大小 80 (0x50) 
  5. .maxstack 2 
  6. .locals init ([0] string str1, 
  7. [1] string str2, 
  8. [2] int32 i) 
  9.  
  10. //两个字符串分为两个步骤相加 
  11. IL_0000: nop 
  12. IL_0001: ldstr "One" 
  13. IL_0006: stloc.0 
  14. IL_0007: ldloc.0 
  15. IL_0008: ldstr " Word" 
  16. IL_000d: call string [mscorlib]System.String::Concat(string, 
  17. string) 
  18. IL_0012: stloc.0 
  19. IL_0013: ldloc.0 
  20. IL_0014: call void [mscorlib]System.Console::WriteLine(string) 
  21.  
  22. //两个字符串在同一行里面进行相加 被翻译为CIL时就已经连接到一起 
  23. IL_0019: nop 
  24. IL_001a: ldstr "One Word" 
  25. IL_001f: stloc.1 
  26. IL_0020: ldloc.1 
  27. IL_0021: call void [mscorlib]System.Console::WriteLine(string) 
  28.  
  29. //将Int32字符装箱再和String合并打印 
  30. IL_0026: nop 
  31. IL_0027: ldc.i4.s 43 
  32. IL_0029: stloc.2 
  33. IL_002a: ldloc.0 
  34. IL_002b: ldloc.2 
  35. IL_002c: box [mscorlib]System.Int32 
  36. IL_0031: call string [mscorlib]System.String::Concat(object, 
  37. object) 
  38. IL_0036: call void [mscorlib]System.Console::WriteLine(string) 
  39.  
  40. //直接调用数字的ToString()方法 
  41. IL_003b: nop 
  42. IL_003c: ldloc.0 
  43. IL_003d: ldloca.s i 
  44. IL_003f: call instance string [mscorlib]System.Int32::ToString() 
  45. IL_0044: call string [mscorlib]System.String::Concat(string, 
  46. string) 
  47. IL_0049: call void [mscorlib]System.Console::WriteLine(string) 
  48. IL_004e: nop 
  49. IL_004f: ret 
  50. } // end of method Program::Main 

        三、首先我们看两种字符串的构造方式的不同而引起的效能变化。

              第1种

 
  1. //打印One Word 
  2. string str1 = "One"
  3. str1 += " Word"
  4. Console.WriteLine(str1); 

 

 
  1. //第一种、两个字符串分为两个步骤相加 
  2. IL_0000: nop 
  3. IL_0001: ldstr "One" //将One字符串存到堆上并且将其引用压栈 
  4. IL_0006: stloc.0 //从栈顶部的One字符串引用地址弹出到第一个参数0 
  5. IL_0007: ldloc.0 //将索引 0 处的局部变量加载到计算堆栈上 
  6. IL_0008: ldstr " Word" //将Word字符串存到堆上并且将其引用返回到计算机栈上 
  7. //调用系统函数将上面两个字符串相加其结果存到栈顶 
  8. IL_000d: call string [mscorlib]System.String::Concat(string, 
  9. string) 
  10. IL_0012: stloc.0 //从栈顶部的One字符串引用地址弹出到第一个参数0 
  11. IL_0013: ldloc.0 //将索引 0 处的局部变量加载到计算堆栈上 
  12. //调用系统参数打印One Word 
  13. IL_0014: call void [mscorlib]System.Console::WriteLine(string) 

             第2种

 
  1. string str2 = "One"+" Word"
  2. Console.WriteLine(str2); 

 

 
  1. //两个字符串在同一行里面进行相加 被翻译为CIL时就已经连接到一起 
  2. IL_0019: nop 
  3. IL_001a: ldstr "One Word" //直接已经构造成One Word 
  4. IL_001f: stloc.1 //从栈顶部的One Word字符串引用地址弹出到第一个参数1 
  5. IL_0020: ldloc.1 //将索引 1 处的局部变量加载到计算堆栈上 
  6. //调用系统参数打印One Word 
  7. IL_0021: call void [mscorlib]System.Console::WriteLine(string) 

        结论:通过上面两种方式构造方式的CIL我们可以很清晰的看出第二种方式的效率要高于第一种的字符串构造方式。所以我们在实际的编码过程中可以考虑尽量使用第二种编码方式。

        四、大家都知道装箱操作会在堆上寻找一个控件来存储值类型的值。会耗费大量的时间。所以下面我们来看两个实例代码

               第1种

 
  1. int i = 43; 
  2. Console.WriteLine(str1+i); 
  3.  
  4. //将Int32字符装箱再和String合并打印 
  5. IL_0026: nop 
  6. IL_0027: ldc.i4.s 43 
  7. IL_0029: stloc.2 
  8. IL_002a: ldloc.0 
  9. IL_002b: ldloc.2 
  10. IL_002c: box [mscorlib]System.Int32 //在这里有一个装箱的操作 
  11. IL_0031: call string [mscorlib]System.String::Concat(object, 
  12. object) 
  13. IL_0036: call void [mscorlib]System.Console::WriteLine(string) 

                 第2种

 
  1. int i = 43;  
  2. Console.WriteLine(str1 + i.ToString());  
  3.  
  4. //直接调用数字的ToString()方法  
  5. IL_003b: nop  
  6. IL_003c: ldloc.0  
  7. IL_003d: ldloca.s i  
  8. //这里没有装箱的操作,仅仅调用了重载的ToString()方法  
  9. IL_003f: call instance string [mscorlib]System.Int32::ToString()  
  10. IL_0044: call string [mscorlib]System.String::Concat(string,  
  11. string)  
  12. IL_0049: call void [mscorlib]System.Console::WriteLine(string)  
  13. IL_004e: nop  

        结论:在这里我们分析第一种情况是对Int32值进行了box装箱操作,然后调用系统函数和第一个 string相加才得到结果的。而第二种情况是直接对Int32调用其重载函数ToString()得到Int32值的字符再与第一个字符相加。在这里我 们避免了装箱分配堆地址查找空余堆地址等麻烦,所以如果再一个循环语句中,第二种情况肯定效率要高很多。


本文转自程兴亮 51CTO博客,原文链接:http://blog.51cto.com/chengxingliang/826595


相关文章
|
3天前
|
人工智能 Java 编译器
.NET 9 发布 性能提升、AI 支持与全方位改进
【11月更文挑战第5天】.NET 9 引入了多项改进,包括性能提升、AI 支持和全方位功能优化。性能方面,编译器增强、服务器 GC 优化、矢量化和硬件支持等提升了执行效率。AI 方面,新增学习材料、合作伙伴生态、原生支持和生成式 AI 集成。此外,.NET Aspire 组件升级、编程语言新功能和开发工具更新进一步提升了开发体验。
|
28天前
|
数据可视化 Java
让星星月亮告诉你,通过反射创建类的实例对象,并通过Unsafe theUnsafe来修改实例对象的私有的String类型的成员属性的值
本文介绍了如何使用 Unsafe 类通过反射机制修改对象的私有属性值。主要包括: 1. 获取 Unsafe 的 theUnsafe 属性:通过反射获取 Unsafe类的私有静态属性theUnsafe,并放开其访问权限,以便后续操作 2. 利用反射创建 User 类的实例对象:通过反射创建User类的实例对象,并定义预期值 3. 利用反射获取实例对象的name属性并修改:通过反射获取 User类实例对象的私有属性name,使用 Unsafe`的compareAndSwapObject方法直接在内存地址上修改属性值 核心代码展示了详细的步骤和逻辑,确保了对私有属性的修改不受 JVM 访问权限的限制
49 4
|
1月前
|
存储 分布式计算 NoSQL
大数据-40 Redis 类型集合 string list set sorted hash 指令列表 执行结果 附截图
大数据-40 Redis 类型集合 string list set sorted hash 指令列表 执行结果 附截图
24 3
|
3月前
|
SQL 分布式计算 DataWorks
DataWorks产品使用合集之如何将STRING类型转换为DATETIME类型
DataWorks作为一站式的数据开发与治理平台,提供了从数据采集、清洗、开发、调度、服务化、质量监控到安全管理的全套解决方案,帮助企业构建高效、规范、安全的大数据处理体系。以下是对DataWorks产品使用合集的概述,涵盖数据处理的各个环节。
|
3月前
|
开发者 C# Android开发
Xamarin 与 .NET:解锁现代化移动应用开发的超级武器——深入探讨C#与.NET框架如何赋能跨平台应用,实现高效编码与卓越性能
【8月更文挑战第31天】Xamarin 与 .NET 的结合为开发者提供了强大的平台,用于构建现代化移动应用。通过 C# 和 .NET 框架,Xamarin 可以实现一次编写、多平台运行,覆盖 iOS、Android 和 Windows。这种方式不仅节省了开发时间和成本,还保证了应用的一致性和高质量。Xamarin 是一个开源框架,专为跨平台移动应用开发设计,允许使用 C# 语言和 .NET 核心库构建原生应用,并访问各平台特定功能。微软维护的 Xamarin 是 Visual Studio 生态系统的一部分,极大地提高了开发效率。
82 0
|
3月前
|
存储 NoSQL 索引
MPP架构数据仓库使用问题之在ORC文件中,String类型字段是怎么进行编码的
MPP架构数据仓库使用问题之在ORC文件中,String类型字段是怎么进行编码的
|
3月前
|
开发框架 缓存 .NET
【App Service】在Azure App Service中分析.NET应用程序的性能的好帮手(Review Stack Traces)
【App Service】在Azure App Service中分析.NET应用程序的性能的好帮手(Review Stack Traces)
|
3月前
|
开发工具 数据安全/隐私保护
【Azure Developer】使用MSAL4J 与 ADAL4J 的SDK时候,遇见了类型冲突问题 "java.util.Collections$SingletonList cannot be cast to java.lang.String"
【Azure Developer】使用MSAL4J 与 ADAL4J 的SDK时候,遇见了类型冲突问题 "java.util.Collections$SingletonList cannot be cast to java.lang.String"
|
3月前
|
存储 C++
【C/C++学习笔记】string 类型的输入操作符和 getline 函数分别如何处理空白字符
【C/C++学习笔记】string 类型的输入操作符和 getline 函数分别如何处理空白字符
38 0
|
2月前
|
Java 索引
java基础(13)String类
本文介绍了Java中String类的多种操作方法,包括字符串拼接、获取长度、去除空格、替换、截取、分割、比较和查找字符等。
36 0
java基础(13)String类
下一篇
无影云桌面