ProfileAnalysis.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369
  1. using System;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. namespace UnityEditor.Performance.ProfileAnalyzer
  5. {
  6. [Serializable]
  7. internal class ProfileAnalysis
  8. {
  9. FrameSummary m_FrameSummary = new FrameSummary();
  10. List<MarkerData> m_Markers = new List<MarkerData>();
  11. List<ThreadData> m_Threads = new List<ThreadData>();
  12. public ProfileAnalysis()
  13. {
  14. m_FrameSummary.first = 0;
  15. m_FrameSummary.last = 0;
  16. m_FrameSummary.count = 0;
  17. m_FrameSummary.msTotal = 0.0;
  18. m_FrameSummary.msMin = float.MaxValue;
  19. m_FrameSummary.msMax = 0.0f;
  20. m_FrameSummary.minFrameIndex = 0;
  21. m_FrameSummary.maxFrameIndex = 0;
  22. m_FrameSummary.maxMarkerDepth = 0;
  23. m_FrameSummary.totalMarkers = 0;
  24. m_FrameSummary.markerCountMax = 0;
  25. m_FrameSummary.markerCountMaxMean = 0.0f;
  26. for (int b = 0; b < m_FrameSummary.buckets.Length; b++)
  27. m_FrameSummary.buckets[b] = 0;
  28. m_Markers.Clear();
  29. m_Threads.Clear();
  30. }
  31. public void SetRange(int firstFrameIndex, int lastFrameIndex)
  32. {
  33. m_FrameSummary.first = firstFrameIndex;
  34. m_FrameSummary.last = lastFrameIndex;
  35. // Ensure these are initialized to frame indices within the range
  36. m_FrameSummary.minFrameIndex = firstFrameIndex;
  37. // if this wasn't initialized, and all frames had 0 length, it wouldn't be set in the UpdateSummary step of the analysis and point out of range
  38. m_FrameSummary.maxFrameIndex = firstFrameIndex;
  39. }
  40. public void AddMarker(MarkerData marker)
  41. {
  42. m_Markers.Add(marker);
  43. }
  44. public void AddThread(ThreadData thread)
  45. {
  46. m_Threads.Add(thread);
  47. }
  48. public void UpdateSummary(int frameIndex, float msFrame)
  49. {
  50. m_FrameSummary.msTotal += msFrame;
  51. m_FrameSummary.count += 1;
  52. if (msFrame < m_FrameSummary.msMin)
  53. {
  54. m_FrameSummary.msMin = msFrame;
  55. m_FrameSummary.minFrameIndex = frameIndex;
  56. }
  57. if (msFrame > m_FrameSummary.msMax)
  58. {
  59. m_FrameSummary.msMax = msFrame;
  60. m_FrameSummary.maxFrameIndex = frameIndex;
  61. }
  62. m_FrameSummary.frames.Add(new FrameTime(frameIndex, msFrame, 1));
  63. }
  64. FrameTime GetPercentageOffset(List<FrameTime> frames, float percent, out int outputFrameIndex)
  65. {
  66. int index = (int)((frames.Count - 1) * percent / 100);
  67. outputFrameIndex = frames[index].frameIndex;
  68. // True median is half of the sum of the middle 2 frames for an even count. However this would be a value never recorded so we avoid that.
  69. return frames[index];
  70. }
  71. float GetThreadPercentageOffset(List<ThreadFrameTime> frames, float percent, out int outputFrameIndex)
  72. {
  73. int index = (int)((frames.Count - 1) * percent / 100);
  74. outputFrameIndex = frames[index].frameIndex;
  75. // True median is half of the sum of the middle 2 frames for an even count. However this would be a value never recorded so we avoid that.
  76. return frames[index].ms;
  77. }
  78. public void SetupMarkers()
  79. {
  80. int countMax = 0;
  81. float countMaxMean = 0.0f;
  82. foreach (MarkerData marker in m_Markers)
  83. {
  84. marker.msAtMedian = 0.0f;
  85. marker.msMin = float.MaxValue;
  86. marker.msMax = float.MinValue;
  87. marker.minFrameIndex = 0;
  88. marker.maxFrameIndex = 0;
  89. marker.countMin = int.MaxValue;
  90. marker.countMax = int.MinValue;
  91. foreach (FrameTime frameTime in marker.frames)
  92. {
  93. var ms = frameTime.ms;
  94. int frameIndex = frameTime.frameIndex;
  95. // Total time for marker over frame
  96. if (ms < marker.msMin)
  97. {
  98. marker.msMin = ms;
  99. marker.minFrameIndex = frameIndex;
  100. }
  101. if (ms > marker.msMax)
  102. {
  103. marker.msMax = ms;
  104. marker.maxFrameIndex = frameIndex;
  105. }
  106. if (frameIndex == m_FrameSummary.medianFrameIndex)
  107. marker.msAtMedian = ms;
  108. var count = frameTime.count;
  109. // count for marker over frame
  110. if (count < marker.countMin)
  111. {
  112. marker.countMin = count;
  113. }
  114. if (count > marker.countMax)
  115. {
  116. marker.countMax = count;
  117. }
  118. }
  119. int unusedIndex;
  120. marker.msMean = marker.presentOnFrameCount > 0 ? (float)(marker.msTotal / marker.presentOnFrameCount) : 0f;
  121. marker.frames.Sort(FrameTime.CompareCount);
  122. marker.countMedian = GetPercentageOffset(marker.frames, 50, out marker.medianFrameIndex).count;
  123. marker.countLowerQuartile = GetPercentageOffset(marker.frames, 25, out unusedIndex).count;
  124. marker.countUpperQuartile = GetPercentageOffset(marker.frames, 75, out unusedIndex).count;
  125. marker.countMean = marker.presentOnFrameCount > 0 ? (float)marker.count / marker.presentOnFrameCount : 0f;
  126. marker.frames.Sort(FrameTime.CompareMs);
  127. marker.msMedian = GetPercentageOffset(marker.frames, 50, out marker.medianFrameIndex).ms;
  128. marker.msLowerQuartile = GetPercentageOffset(marker.frames, 25, out unusedIndex).ms;
  129. marker.msUpperQuartile = GetPercentageOffset(marker.frames, 75, out unusedIndex).ms;
  130. if (marker.countMax > countMax)
  131. countMax = marker.countMax;
  132. if (marker.countMean > countMaxMean)
  133. countMaxMean = marker.countMean;
  134. }
  135. m_FrameSummary.markerCountMax = countMax;
  136. m_FrameSummary.markerCountMaxMean = countMaxMean;
  137. }
  138. public void SetupMarkerBuckets()
  139. {
  140. foreach (MarkerData marker in m_Markers)
  141. {
  142. marker.ComputeBuckets(marker.msMin, marker.msMax);
  143. marker.ComputeCountBuckets(marker.countMin, marker.countMax);
  144. }
  145. }
  146. public void SetupFrameBuckets(float timeScaleMax)
  147. {
  148. float first = 0;
  149. float last = timeScaleMax;
  150. float range = last - first;
  151. int maxBucketIndex = m_FrameSummary.buckets.Length - 1;
  152. for (int bucketIndex = 0; bucketIndex < m_FrameSummary.buckets.Length; bucketIndex++)
  153. {
  154. m_FrameSummary.buckets[bucketIndex] = 0;
  155. }
  156. float scale = range > 0 ? m_FrameSummary.buckets.Length / range : 0;
  157. foreach (var frameData in m_FrameSummary.frames)
  158. {
  159. var msFrame = frameData.ms;
  160. //var frameIndex = frameData.frameIndex;
  161. int bucketIndex = (int)((msFrame - first) * scale);
  162. if (bucketIndex < 0 || bucketIndex > maxBucketIndex)
  163. {
  164. // It can occur for the highest entry in the range (max-min/range) = 1
  165. // if (ms > max) // Check for the spilling case
  166. // Debug.Log(string.Format("Frame {0}ms exceeds range {1}-{2} on frame {3}", msFrame, first, last, frameIndex));
  167. if (bucketIndex > maxBucketIndex)
  168. bucketIndex = maxBucketIndex;
  169. else
  170. bucketIndex = 0;
  171. }
  172. m_FrameSummary.buckets[bucketIndex] += 1;
  173. }
  174. if (range == 0)
  175. {
  176. // All buckets will be the same
  177. for (int bucketIndex = 1; bucketIndex < m_FrameSummary.buckets.Length; bucketIndex++)
  178. {
  179. m_FrameSummary.buckets[bucketIndex] = m_FrameSummary.buckets[0];
  180. }
  181. }
  182. }
  183. void CalculateThreadMedians()
  184. {
  185. foreach (var thread in m_Threads)
  186. {
  187. if (thread.frames.Count > 0)
  188. {
  189. thread.frames.Sort();
  190. int unusedIndex;
  191. thread.msMin = GetThreadPercentageOffset(thread.frames, 0, out thread.minFrameIndex);
  192. thread.msLowerQuartile = GetThreadPercentageOffset(thread.frames, 25, out unusedIndex);
  193. thread.msMedian = GetThreadPercentageOffset(thread.frames, 50, out thread.medianFrameIndex);
  194. thread.msUpperQuartile = GetThreadPercentageOffset(thread.frames, 75, out unusedIndex);
  195. thread.msMax = GetThreadPercentageOffset(thread.frames, 100, out thread.maxFrameIndex);
  196. // Put back in order of frames
  197. thread.frames.Sort((a, b) => a.frameIndex.CompareTo(b.frameIndex));
  198. }
  199. else
  200. {
  201. thread.msMin = 0f;
  202. thread.msLowerQuartile = 0f;
  203. thread.msMedian = 0f;
  204. thread.msUpperQuartile = 0f;
  205. thread.msMax = 0f;
  206. }
  207. }
  208. }
  209. public void Finalise(float timeScaleMax, int maxMarkerDepth)
  210. {
  211. if (m_FrameSummary.frames.Count > 0)
  212. {
  213. m_FrameSummary.frames.Sort();
  214. m_FrameSummary.msMean = (float)(m_FrameSummary.msTotal / m_FrameSummary.count);
  215. m_FrameSummary.msMedian = GetPercentageOffset(m_FrameSummary.frames, 50, out m_FrameSummary.medianFrameIndex).ms;
  216. int unusedIndex;
  217. m_FrameSummary.msLowerQuartile = GetPercentageOffset(m_FrameSummary.frames, 25, out unusedIndex).ms;
  218. m_FrameSummary.msUpperQuartile = GetPercentageOffset(m_FrameSummary.frames, 75, out unusedIndex).ms;
  219. }
  220. else
  221. {
  222. m_FrameSummary.msMean = 0f;
  223. m_FrameSummary.msMedian = 0f;
  224. m_FrameSummary.msLowerQuartile = 0f;
  225. m_FrameSummary.msUpperQuartile = 0f;
  226. // This started as float.MaxValue and won't have been updated
  227. m_FrameSummary.msMin = 0f;
  228. }
  229. // No longer need the frame time list ?
  230. //m_frameSummary.msFrame.Clear();
  231. m_FrameSummary.maxMarkerDepth = maxMarkerDepth;
  232. if (timeScaleMax <= 0.0f)
  233. {
  234. // If max frame time range not specified then use the max frame value found.
  235. timeScaleMax = m_FrameSummary.msMax;
  236. }
  237. else if (timeScaleMax < m_FrameSummary.msMax)
  238. {
  239. Debug.Log(string.Format("Expanding timeScaleMax {0} to match max value found {1}", timeScaleMax, m_FrameSummary.msMax));
  240. // If max frame time range too small we must expand it.
  241. timeScaleMax = m_FrameSummary.msMax;
  242. }
  243. SetupMarkers();
  244. SetupMarkerBuckets();
  245. SetupFrameBuckets(timeScaleMax);
  246. // Sort in median order (highest first)
  247. m_Markers.Sort(SortByAtMedian);
  248. CalculateThreadMedians();
  249. }
  250. int SortByAtMedian(MarkerData a, MarkerData b)
  251. {
  252. if (a.msAtMedian == b.msAtMedian)
  253. return -a.medianFrameIndex.CompareTo(b.medianFrameIndex);
  254. return -a.msAtMedian.CompareTo(b.msAtMedian);
  255. }
  256. public List<MarkerData> GetMarkers()
  257. {
  258. return m_Markers;
  259. }
  260. public List<ThreadData> GetThreads()
  261. {
  262. return m_Threads;
  263. }
  264. public ThreadData GetThreadByName(string threadNameWithIndex)
  265. {
  266. foreach (var thread in m_Threads)
  267. {
  268. if (thread.threadNameWithIndex == threadNameWithIndex)
  269. return thread;
  270. }
  271. return null;
  272. }
  273. public FrameSummary GetFrameSummary()
  274. {
  275. return m_FrameSummary;
  276. }
  277. public MarkerData GetMarker(int index)
  278. {
  279. if (index < 0 || index >= m_Markers.Count)
  280. return null;
  281. return m_Markers[index];
  282. }
  283. public int GetMarkerIndexByName(string markerName)
  284. {
  285. if (markerName == null)
  286. return -1;
  287. for (int index = 0; index < m_Markers.Count; index++)
  288. {
  289. var marker = m_Markers[index];
  290. if (marker.name == markerName)
  291. {
  292. return index;
  293. }
  294. }
  295. return -1;
  296. }
  297. public MarkerData GetMarkerByName(string markerName)
  298. {
  299. if (markerName == null)
  300. return null;
  301. for (int index = 0; index < m_Markers.Count; index++)
  302. {
  303. var marker = m_Markers[index];
  304. if (marker.name == markerName)
  305. {
  306. return marker;
  307. }
  308. }
  309. return null;
  310. }
  311. }
  312. }