C#效率优化(3)-- 使用foreach时避免装箱
Introduction:
※本文不是在描述旧版本Unity中mono编译器导致的foreach语句额外装箱错误
博主是一名Unity 3D游戏开发者,游戏使用C#+lua开发,最近在优化C#代码时,发现了一处使用foreach不恰当的地方,其结果是造成了每帧近3k的GC Alloc,如此高频率的GC堆内存分配,会导致垃圾回收的调用更加频繁,从而影响游戏性能,而这只需要简单的修改即可避免;
※使用.Net 2.0的Unity版本,如果是较新的.Net 4.x版本,由于FCL实现修改,本文中48->56
原始声明代码如下:
private readonly IDictionary<int, MyClass> mDic = new Dictionary<int, MyClass>();
在每帧逻辑里面,会多次对其进行遍历,遍历代码如下:
foreach(var keyValuePair in mDic)
{
//do...
}
通过Unity自带的Profiler分析,可以发现其导致的GC Alloc:

Body:
通过上面的Profiler可以发现此时foreach语句实际上调用的是Dictionary定义中隐式实现的IEnumerable<KeyValuePair<TKey, TValue>>.GetEnumerator()方法,该方法的声明如下:
IEnumerator<KeyValuePair<TKey, TValue>> IEnumerable<KeyValuePair<TKey, TValue>>.GetEnumerator()
{
return new Enumerator(this, Enumerator.KeyValuePair);
}
其中,Enumerator是在Dictionary类中定义的嵌套结构类型:

结构类型隐式转换为接口类型时会发生装箱,对于该Enumerator类型,其装箱后大小为16(开销字节)+8(字段dictionary)+8(字段next+stamp)+16(字段current:8(int字节对齐)+8)=48字节,调用29次即产生48*29=1392字节的堆内存分配,这符合我们看到Profiler里面看到的GC Alloc;
为了解决这个问题,只需要将变量声明时改为Dictionary即可,不使用接口类型的变量,即:
private readonly Dictionary<int, MyClass> mDic = new Dictionary<int, MyClass>();
此时,在对mDic进行foreach循环时,就会调用Dictionary<TKey,TValue>.GetEnumerator()方法,该方法返回值类型即结构类型的Enumerator,避免了装箱操作:

One more thing:
这里可能很多人有个误解,即foreach是只能对实现了IEnumerable或IEnumerable<T>的类型对象进行遍历,其实不然,foreach语句还可以对满足以下条件的任何类型的对象进行遍历:
实现了可访问的GetEnumerator()方法,且该方法的返回值类型符合:包含可访问的Current属性和bool MoveNext()方法;
Conclusion:
这样就知道了,.Net框架类库提供的泛型集合类型都实现了这样的方法,因此可以放心对泛型集合进行foreach遍历,而不产生堆内存的分配,也因此,我们在使用这些类型时,尽量避免直接对其接口类型的变量进行遍历;
如果您觉得阅读本文对您有帮助,请点一下“推荐”按钮,您的认可是我写作的最大动力!
作者:Minotauros
出处:https://www.cnblogs.com/minotauros/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
C#效率优化(3)-- 使用foreach时避免装箱的更多相关文章
- C#效率优化(1)-- 使用泛型时避免装箱
本想接着上一篇详解泛型接着写一篇使用泛型时需要注意的一个性能问题,但是后来想着不如将之前的详解XX系列更正为现在的效率优化XX系列,记录在工作时遇到的一些性能优化的经验和技巧,如果有什么不足,还请大家 ...
- php程序效率优化的一些策略小结
php程序效率优化的一些策略小结 1.在可以用file_get_contents替代file.fopen.feof.fgets等系列方法的情况下,尽量用 file_get_contents,因为他 ...
- php效率优化
php效率优化 最近在公司一边自学一边写PHP程序,由于公司对程序的运行效率要求很高,而自己又是个新手,一开始就注意程序的效率很重要,这里就结合网上的一些资料,总结下php程序效率优化的一些策略:1. ...
- C#效率优化(4)-- 编译器对数组遍历的优化
在平时开发过程中,数组是我们使用频率最高的类型之一,在使用定长列表时,数组可以说是最佳方案,这也是我们最熟悉的数据结构之一. 在C#中使用数组,可以获取在内存上连续的相同类型的一组变量,在连续访问时可 ...
- jquery选择器效率优化问题
jquery选择器效率优化问题 jquery选择器固然强大,但是使用不当回导致效率问题: 1.要养成将jQuery对象缓存进变量的习惯 //不好的写法 $('#btn').bind("c ...
- php性能效率优化
[size=5][color=Red]php性能效率优化[/color][/size] 最近在公司一边自学一边写PHP程序,由于公司对程序的运行效率要求很高,而自己又是个新手,一开始就注意程序的效率很 ...
- Jenkins Kubernetes Slave 调度效率优化小记
Jenkins K8S Slave 调度效率优化 by yue994488@126.com 使用kubernetes为测试工具Gatling进行大规模压测,压测期间发现Jenkins调度压测实例较慢, ...
- 见招拆招-PostgreSQL中文全文索引效率优化
* { color: #3e3e3e } body { font-family: "Helvetica Neue", Helvetica, "Hiragino Sans ...
- Unity3d代码及效率优化总结
1.PC平台的话保持场景中显示的顶点数少于200K~3M,移动设备的话少于10W,一切取决于你的目标GPU与CPU. 2.如果你用U3D自带的SHADER,在表现不差的情况下选择Mobile或Unli ...
随机推荐
- Matplotlib 基本用法
1.基础应用 >>> import matplotlib.pyplot as plt >>> import numpy as np #使用np.linspace定义 ...
- python基础之Day11
一.函数是对象 函数是第一类对象的含义是函数可以被当作数据处理 二.函数的嵌套定义 def f1(): def f2(): print('from f2') f2()f1() 三.名称空间与作用域 1 ...
- C++学习札记(3)
一边听着许巍的音乐,一遍学习着C++的精髓,这感觉这酸爽,我一个人体会和知道. 许巍是两代人共同的时代标志,他的音乐作品脍炙人口,堪称经典,经久不衰:此时此刻品味,依然有丰富的各种味道和感情.可能因为 ...
- [TestNG] Eclipse/STS中两种安装TestNG的方法
Two Ways To Install TestNG in Eclipse/STS Today I install the newest Sprint Tool Suite and want to i ...
- MybatisMapper 映射框架(增删改查 原始模式)
//增删改查 package TestDemo; import java.io.IOException; import java.io.InputStream; import java.util.Da ...
- ----这是一个register code----
这是一个register code,是需要用到<input>标签下的6个标签(?应该是标签喔) 然后附上代码 <html ><head><title>注 ...
- Mybatis的针对于同一个有自己父类或子类的递归查询 (如商品分类)
1.pojo代码 private Integer categoryId; private Integer superId; private String name; private Byte leve ...
- 使用itext生成pdf的,各种布局
代码如下,jar包为itext.jar,itextAsia.jar,最好都是最新的 :2张图片也在最后贴出,把图片放到D盘可以直接生成制定格式的pdf. 最后生成的pdf如下: 代码如下: packa ...
- Django基础—1
一. Django的安装1. 查看已安装的Django的版本 进入到终端以及Python的交互模式 python3/ ipython32. 交互模式中输入import django ...
- POJ3204 Ikki's Story I - Road Reconstruction
Ikki's Story I - Road Reconstruction Time Limit: 2000MS Memory Limit: 131072K Total Submissions: 7 ...