{"id":2197,"date":"2020-03-18T13:13:12","date_gmt":"2020-03-18T05:13:12","guid":{"rendered":"https:\/\/www.clonefactor.com\/wordpress\/?p=2197"},"modified":"2020-03-18T13:13:13","modified_gmt":"2020-03-18T05:13:13","slug":"for-loop-vs-foreach-in-unity","status":"publish","type":"post","link":"https:\/\/www.clonefactor.com\/wordpress\/program\/unity3d\/2197\/","title":{"rendered":"For..loop vs Foreach in Unity"},"content":{"rendered":"\n<p>Since Unity5.5 have 40kb GC issue.<br>I&#8217;m trying to avoid using Foreach when possible,&nbsp;<br><br>Until Unity3D Team claim they fixed the extra GC issue.<br>The problem was the &#8220;Foreach + List&#8221; combination will generator 40kb extra garbage during process.<br>and this issue should be fixed after 5.5.<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\"><p> <strong>foreach loops<\/strong> <br>In versions of Unity prior to 5.5, a <em>foreach<\/em> loop iterating over anything other than an array generates garbage each time the loop terminates. This is due to boxing that happens behind the scenes. A System.Object is allocated on the heap when the loop begins and disposed of when the loop terminates. This problem was fixed in Unity 5.5. <\/p><cite> Ref: <a href=\"https:\/\/learn.unity.com\/tutorial\/fixing-performance-problems#5c7f8528edbc2a002053b595\">fixing-performance-problems<\/a> <\/cite><\/blockquote>\n\n\n\n<p>I don&#8217;t believe that, since Foreach are requesting the List&lt;T&gt; to use IEnumerable interface to gain access the data, that should be Unity&#8217;s system implementation level bug.<br \/>so today I&#8217;m trying to run some test on it to find out what actually happen.<\/p>\n<p>and here is the source code that I used for test cases :<br \/>for testing we generate a List&lt;int&gt;, List&lt;object&gt; and Hashset&lt;object&gt; to compare.<\/p>\n<p>after run an 90000000 (Ninety million)<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>For..loop test on int list, End : 6393\/ms,<\/li><li>Foreach (int) test on int list, End : 8010\/ms,<\/li><li>Foreach test on Hastset, End : 896\/ms,<\/li><\/ul>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"csharp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">using System.Collections.Generic;\nusing UnityEngine;\nusing Stopwatch = System.Diagnostics.Stopwatch;\n\npublic class ForeachTest : MonoBehaviour\n{\n\t[SerializeField] int testCount = 90000000;\n\t[SerializeField] bool testCPU = false;\n\t[SerializeField] bool testExtraGC = false;\n\n\tprivate List&lt;int> listOfInts = null;\n\n\tprivate class TestObj : Object { }\n\tprivate List&lt;TestObj> listOfObjects = null;\n\tprivate HashSet&lt;TestObj> setOfObjects = null;\n\n\tStopwatch stopwatch = new Stopwatch();\n\tMemWatch memWatch = new MemWatch();\n\n\tprivate void Awake()\n\t{\n\t\tResetMemory();\n\t}\n\n\tprivate void Update()\n\t{\n\t\tif (Input.GetKeyUp(KeyCode.Alpha1)) TestForloop();\n\t\telse if (Input.GetKeyUp(KeyCode.Alpha2)) TestForEach();\n\t\telse if (Input.GetKeyUp(KeyCode.Alpha3)) TestForloop2();\n\t\telse if (Input.GetKeyUp(KeyCode.Alpha4)) TestForEach2();\n\t\telse if (Input.GetKeyUp(KeyCode.Alpha5)) TestForEach3();\n\t\telse if (Input.GetKeyUp(KeyCode.Alpha6)) TestForEach4();\n\t}\n\n\t[ContextMenu(\"reset memory\")]\n\tprivate void ResetMemory()\n\t{\n\t\tlistOfInts = new List&lt;int>(testCount);\n\t\tlistOfObjects = new List&lt;TestObj>(testCount);\n\t\tsetOfObjects = new HashSet&lt;TestObj>();\n\t\tfor (int i = 0; i &lt; testCount; i++)\n\t\t{\n\t\t\tlistOfInts.Add(i);\n\t\t\tlistOfObjects.Add(new TestObj());\n\t\t\tsetOfObjects.Add(new TestObj());\n\t\t}\n\t\t\n\t}\n\n\t[ContextMenu(\"A1) Test for loop\")]\n\tprivate void TestForloop()\n\t{\n\t\tint count = listOfInts.Count;\n\t\tStartTest();\n\t\tfor (int i = 0; i &lt; count; i++)\n\t\t{\n\t\t\tDoSomething(listOfInts[i]);\n\t\t}\n\t\tEndTest();\n\t\tPrintResult(\"For..loop test on &lt;b>int list&lt;\/b>\");\n\t\t\n\t}\n\n\t[ContextMenu(\"A2) Test for each\")]\n\tprivate void TestForEach()\n\t{\n\t\tStartTest();\n\t\tforeach (int i in listOfInts)\n\t\t{\n\t\t\tDoSomething(i);\n\t\t}\n\t\tEndTest();\n\t\tPrintResult(\"Foreach (int) test on &lt;b>int list&lt;\/b>\");\n\t}\n\n\t[ContextMenu(\"B1) Test for loop on object\")]\n\tprivate void TestForloop2()\n\t{\n\t\tint count = listOfObjects.Count;\n\t\tStartTest();\n\t\tfor (int i = 0; i &lt; count; i++)\n\t\t{\n\t\t\tDoSomething(listOfObjects[i]);\n\t\t}\n\t\tEndTest();\n\t\tPrintResult(\"For..loop test on &lt;b>object list&lt;\/b>\");\n\t}\n\n\t[ContextMenu(\"B2) Test for each on object\")]\n\tprivate void TestForEach2()\n\t{\n\t\tStartTest();\n\t\tforeach (TestObj i in listOfObjects)\n\t\t{\n\t\t\tDoSomething(i);\n\t\t}\n\t\tEndTest();\n\t\tPrintResult(\"Foreach on &lt;b>Object list&lt;\/b>\");\n\t}\n\n\t[ContextMenu(\"C2) Test for each on copy list\")]\n\tprivate void TestForEach3()\n\t{\n\t\tStartTest();\n\t\t{\n\t\t\tList&lt;int> tmp = new List&lt;int>(listOfInts);\n\t\t\tforeach (int i in tmp)\n\t\t\t{\n\t\t\t\tDoSomething(tmp[i]);\n\t\t\t}\n\t\t}\n\t\tEndTest();\n\t\tPrintResult($\"Foreach test on &lt;b>(copy int list)&lt;\/b>\");\n\t}\n\n\t[ContextMenu(\"D2) Test for each on hastset\")]\n\tprivate void TestForEach4()\n\t{\n\t\tStartTest();\n\t\tforeach (TestObj i in setOfObjects)\n\t\t{\n\t\t\tDoSomething(i);\n\t\t}\n\t\tEndTest();\n\t\tPrintResult(\"Foreach test on &lt;b>Hastset&lt;\/b>\");\n\t}\n\n\tprivate void StartTest()\n\t{\n\t\tstopwatch.Restart();\n\t\tmemWatch.Start();\n\t}\n\n\tprivate void EndTest()\n\t{\n\t\tmemWatch.Stop();\n\t\tstopwatch.Stop();\n\t}\n\n\tprivate void PrintResult(string prefix)\n\t{\n\t\tDebug.Log($\"{prefix}, End : {stopwatch.ElapsedMilliseconds.ToString()}\/ms, Memory usage : {memWatch.ToString()}\");\n\t}\n\n\tprivate void DoSomething(TestObj obj)\n\t{\n\t\tif (testCPU)\n\t\t\tobj.GetHashCode(); \/\/ CPU time\n\n\t\tif (testExtraGC)\n\t\t{\n\t\t\tTestObj tmp = obj; \/\/ GC\n\t\t}\n\t}\n\n\tprivate void DoSomething(int value)\n\t{\n\t\tif (testCPU)\n\t\t\tvalue.GetHashCode();\/\/ CPU time\n\n\t\tif (testExtraGC)\n\t\t{\n\t\t\tint v = value; \/\/ GC\n\t\t}\n\t}\n}\n\npublic class MemWatch\n{\n\tprivate long _beforeTotalMemory = 0;\n\tprivate long _afterTotalMemory = 0;\n\tpublic long MemorySizeChange = 0;\n\n\tpublic MemWatch() { }\n\t\/\/\u4fdd\u7559\u6e2c\u91cf\u958b\u59cb\u4e4b\u57fa\u6e96\n\tpublic void Start()\n\t{\n\t\t\/\/ System.GC.Collect();\n\t\t_afterTotalMemory = 0;\n\t\t_beforeTotalMemory = System.GC.GetTotalMemory(true);\n\t}\n\t\/\/\u6e2c\u91cf\u5f9eStart()\u81f3\u4eca\u7684\u8a18\u61b6\u9ad4\u8b8a\u5316\n\tpublic void Stop()\n\t{\n\t\t\/\/ System.GC.Collect();\n\t\t_afterTotalMemory = System.GC.GetTotalMemory(true);\n\t\tMemorySizeChange = _beforeTotalMemory - _afterTotalMemory;\n\t}\n\n\tpublic override string ToString()\n\t{\n\t\tstring sign = \"&lt;b>\" + (_beforeTotalMemory > _afterTotalMemory ? \">\" : \"&lt;\") + \"&lt;\/b>\";\n\t\treturn $\"Size change : {MemorySizeChangeInKB}\\nBefore {_beforeTotalMemory \/ 1024}KB, {sign} After {_afterTotalMemory \/ 1024}KB\";\n\t}\n\n\t\/\/\u8a18\u61b6\u9ad4\u4f7f\u7528\u91cf\u8b8a\u5316(\u4ee5KB\u8a08)\n\tpublic string MemorySizeChangeInKB => string.Format(\"{0:N0}KB\", MemorySizeChange \/ 1024);\n\t\/\/\u8a18\u61b6\u9ad4\u4f7f\u7528\u91cf\u8b8a\u5316(\u4ee5MB\u8a08)\n\tpublic string MemorySizeChangeInMB => string.Format(\"{0:N0}MB\", MemorySizeChange \/ 1024 \/ 1024);\n}<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>Since Unity5.5 have 40kb GC issue.I&#8217;m trying &hellip;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[11],"tags":[],"class_list":["post-2197","post","type-post","status-publish","format-standard","hentry","category-unity3d"],"_links":{"self":[{"href":"https:\/\/www.clonefactor.com\/wordpress\/wp-json\/wp\/v2\/posts\/2197","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.clonefactor.com\/wordpress\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.clonefactor.com\/wordpress\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.clonefactor.com\/wordpress\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.clonefactor.com\/wordpress\/wp-json\/wp\/v2\/comments?post=2197"}],"version-history":[{"count":3,"href":"https:\/\/www.clonefactor.com\/wordpress\/wp-json\/wp\/v2\/posts\/2197\/revisions"}],"predecessor-version":[{"id":2200,"href":"https:\/\/www.clonefactor.com\/wordpress\/wp-json\/wp\/v2\/posts\/2197\/revisions\/2200"}],"wp:attachment":[{"href":"https:\/\/www.clonefactor.com\/wordpress\/wp-json\/wp\/v2\/media?parent=2197"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.clonefactor.com\/wordpress\/wp-json\/wp\/v2\/categories?post=2197"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.clonefactor.com\/wordpress\/wp-json\/wp\/v2\/tags?post=2197"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}