Unity3D系列1 : foreach对于性能到底有没有影响

0x00 序言

本文无意比较for和foreach谁效率更高,不会设计到for和foreach取值之类的等等。单纯探讨foreach会不会影响unity3d效率。

事情开端是这样的,之前在看unity优化的时候,遇见了这么一句:

尽量不要使用foreach,而是使用for。foreach其实会涉及到迭代器的使用,而据传说每一次循环所产生的迭代器会带来24 Bytes的垃圾。那么循环10次就是240Bytes。

由于刚接触unity以及c#不久,当时没有仔细研究内在原理,只是简单相信了这个说法。于是接下来我会在代码中尽量避免使用foreach,虽然我觉得foreach真的挺好用的。

然后,现在每次遇见遍历,我都会思考为什么foreach会有这样的行为,我想知道真正的原因,直到今天,我才深入寻找资料去论证这个观点。

0x01 c#中for比foreach指令更加精简,效率更高?

关于这个说法,百度了下(懒得翻墙去google),找到一篇被广泛转发的文章:

Unity里面应尽量避免使用foreach

里面代码如下:

using UnityEngine;
using System.Collections;

public class ForeachTest : MonoBehaviour {

    protected ArrayList m_array;

    void Start ()
    {
        m_array = new ArrayList();
        for (int i = 0; i < 2; i++)
            m_array.Add(i);
    } 

    void Update ()
    {
        for (int i = 0; i < 1000; i++)
        {
            foreach (int e in m_array)
            {
                //big gc alloc!!! do not use this code!
            }
        }

        for (int i = 0; i < 1000; i++)
        {
            for (int k = 0; k < m_array.Count; k++)
            {
                //no gc alloc!!
            }
        }
    }
}

初步看到代码后,很是疑惑,为什么一个简单的foreach会有这么多的内存开销。带着不解,继续搜索,直到看到知乎上这么一篇回答:

作为Unity3D的脚本而言,c#中for是否真的比foreach效率更高?

所以的疑惑在这里几乎都可以得到解答:

每一个foreach都会产生40B的内存,如果是在某个脚本的Update中使用foreach,那么每帧都会有40B的GC ALLOC。

之前在另一个地方看见过2014Unity亚洲开发者大会会议简录上有一个说法:

检测每帧都具有20B以上内存分配的选项

这几乎意味着在Update中使用foreach是不太明智的选择。关于为什么每帧20B以上内存分配不太好,我还没有仔细研究,个人猜测如果每帧20B,累计一段时间会有大量回收内存堆积,而mono回收机制里回收时间点不固定,如果隔一个较长时间点统一回收,必然会导致顿卡现象出现。

0x02 我们就不使用foreach了吗?

回答问题之前,我打算顺着 王剑飞 先生的代码验证一次,代码跟他在知乎回答里的几乎一致:

using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class GCTest : MonoBehaviour {

    List<int> iList = new List<int>();
    int count = 10;

    void Awake(){
        for (int i = 0; i < count; i++){
            iList.Add(i);
        }
    }

    // Use this for initialization
    void Start () {

    }

    // Update is called once per frame
    void Update () {
        TestForeach();

        //TestNoForeach();

        //TestFor();

        //TestUsing();
    }

    void TestForeach()
    {
        //Debug.Log("TestForeach");
        foreach (var e in iList){
            //Debug.Log(e);
        }

        //foreach (var e in iList){
        //    foreach (var item in iList){
        //        //Debug.Log(e);
        //    }
        //}
    }

    void TestNoForeach()
    {
        //Debug.Log("TestNoForeach");
        var e = iList.GetEnumerator();
        while (e.MoveNext()){
            //Debug.Log(e.Current);
        }
    }

    void TestFor()
    {
        //Debug.Log("TestFor");
        for (int i = 0; i < count; ++i){
            //Debug.Log(iList[i]);
        }
    }

    void TestUsing()
    {
        //Debug.Log("TestUsing");
        using(var e = iList.GetEnumerator()){
            while (e.MoveNext()){
                //Debug.Log(e.Current);
            }
        };
    }
}

请注意,这是直接在unity工程里面添加了C#脚本,使用内置的mono编译器来编译代码。

当我在Update中使用TestForeach()时,结果如下:

可以看见在我Win7 + Unity64 环境下,foreach的确有40B的GC Alloc。

接着TestForeach()中使用注释代码:

foreach (var e in iList){
            foreach (var item in iList){
                //Debug.Log(e);
            }
        }

一共440B内存开销,这侧面印证了一个foreach会产生40B的堆内存说法。

接着在Update中使用TestNoForeach(),结果如下:

可以看到没有GC Alloc。

这样的结果似乎还是在说,不要使用foreach啊!

我又回想起来知乎上还说过:

**真相就已经出现了:

在finally里,mono编译出来的代码中有一次将valuetype的Enumerator,boxing的过程!!

What a waste!!!

这就是Unity中所带的老版本mono编译器的一个bug!**

既然是mono老版本的bug,能绕过去吗?

答案是肯定的,因为我们的项目正好是把脚本达成dll包,放入工程使用。编译脚本时,使用的是MS最新的编译器,这样是不是没问题呢?

测试一下,将新编译的dll放入工程,结果如下:

同样使用的是TestForeach()方法,不过这次已经没有GC Alloc了。

0x03 结论?

相信看完上面的朋友已经有了自己的想法了。

1.脚本直接放在Unity工程中,如果不是在Update中每帧调用,使用foreach没有太大顾虑。如果是Update中,而且是多个地方频繁使用foreach,就需要慎重考虑了。

2.脚本放入Dll中,爱咋咋地吧!

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-08-28 06:02:35

Unity3D系列1 : foreach对于性能到底有没有影响的相关文章

我的架构经验系列文章 - 后端架构 - 性能层面

性能层面: 性能分析 我觉得性能分析的话要注意几个要点: 不要去猜:对于自己写的代码你是否知道你的代码要执行多久,是不是还在用时间相减来测试代码执行时间?现在有很多自动化的工具可以在程序运行的时候,测试代码中每一句语句的执行时间,可以有效分析出代码的性能瓶颈.对于比较重要的业务逻辑建议采用类似的工具来进行性能分析,有的时候性能慢的代码不一定是自己写的还可能是框架内提供的,如果没有一个丰富的编码经验是不太可能知道这些点的,但是通过这样的分析工具你就能知道这个地方会慢,虽然框架的代码我们不能改,但是

Java 8 Stream的性能到底如何?

那么,Stream API的性能到底如何呢,代码整洁的背后是否意味着性能的损耗呢?本文我们对Stream API的性能一探究竟. 为保证测试结果真实可信,我们将JVM运行在 -server 模式下,测试数据在GB量级,测试机器采用常见的商用服务器,配置如下: OSCentOS 6.7 x86_64CPUIntel Xeon X5675, 12M Cache 3.06 GHz, 6 Cores 12 Threads内存96GBJDKjava version 1.8.0_91, Java HotSp

百度竞价和正常SEO搜索排名到底有没有影响?

相信很多小伙伴们对百度竞价都有所了解,百度竞价给大家的第一感觉大部分都是"烧钱",选择百度竞价推广的一般都是大企业或者暴利产品.项目等,针对中小企业做推广显然不是太合适.SEO自然搜索排名是任何人.企业.行业一直向往的排名,当然,大部分企业为了同时获得更多流量都会选择2项(百度竞价.SEO优化)同时进行,如果我们做了百度竞价会不会对我们网站SEO优化出来的排名有影响了?下面由李勇SEO为大家做个详细介绍以及分析.百度竞价和正常SEO搜索排名到底有没有影响?官方给出的答案是没有影响的,因

人民币贬值对房价到底有哪些影响

人民币贬值意味着同样的钱却不值那么多钱了,它对房价方面有哪些影响呢? 实事证明,人民币的贬值与房价的涨跌没有太直接的关系.因此,在这部分资金为了减少由于人民币贬值带来的利差损失时,可能会抛售房地产资产,转成美元汇出境外,可能会在短期内,导致房价的下跌.但是,在实际操作中,如果房价下跌幅度,超过人民币贬值的幅度,也许这些资金就可能不会抛售物业资产,避免造成汇率和价格的双重损失.从今年和去年外资大规模成交的案例来看,前期的短期投机资本已经退出很多了,取而代之的是一些长期投资型基金,它们通常是投资购买

standby 磁盘IO性能较差,影响Primary性能

1. 近日处理一个由于standby 磁盘IO性能较差,导致Primary的性能受到影响.主库主要是等待"log file switch completion",通过ASH dump分析,最终发现实际等待事件是"LGWR-LNS wait on channel".这个事件基本上可以将问题归结到网络性能和standby的IO性能,而客户的传输模式是"MAXIMUM AVAILABILITY"最后提出两个解决方案,(1). 更换性能更好的standb

nginx+FastCGI到底是谁影响超时时间

需求: 一个php程序要跑一段时间,但是时间不确定. 问题: 当该php程序运行超过一段时间被强制断开连接. PHP本身超时处理 在 php.ini 中,有一个参数 max_execution_time 可以设置 PHP 脚本的最大执行时间,但是,在 php-cgi(php-fpm) 中,该参数不会起效.真正能够控制 PHP 脚本最大执行时: <value name="request_terminate_timeout">0s</value> 就是说如果是使用

.NET面试题系列[15] - LINQ:性能

.NET面试题系列目录 当你使用LINQ to SQL时,请使用工具(比如LINQPad)查看系统生成的SQL语句,这会帮你发现问题可能发生在何处. 提升性能的小技巧 避免遍历整个序列 当我们仅需要一个资料的时候,我们可以考虑使用First / FirstOrDefault / Take / Any等方法,它们都会在取得合乎要求的资料后退出,而不会遍历整个序列(除非最后一个资料才是合乎要求的哈哈).而类似ToList / Max / Last / Sum / Contain等方法显而易见会遍历整

AutoCAD .net 开发 SelectionFilter Foreach Linq 性能比较

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Diagnostics; using Autodesk.AutoCAD.Runtime; using Autodesk.AutoCAD.DatabaseServices; using Autodesk.AutoCAD.ApplicationS

性能:前端的性能到底对业务数据有多大的影响?

性能是个特别有意思的话题,在我之前的工作中,从入门的初级工程师到高级别的技术专家,大家都很喜欢谈性能,我以前参与晋升评审,每年总能听到很多关于性能的晋升述职 那么,今天我就来谈谈我眼中的性能. 性能总论while 循环快还是 for 循环快? |0 是不是比 Math.floor 性能好? 网上随处可以见到一类对性能的讨论.一些新人也非常热衷此类讨论.但是实际上,它们除了让你写代码的时候纠结之外,毫无意义. 为什么这样讲呢?我想讲一个小故事. 从前有个工程师,特别注重代码细节,有一天他发现系统中