{"id":5691,"date":"2021-12-14T10:46:00","date_gmt":"2021-12-14T18:46:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/pix\/?p=5691"},"modified":"2022-01-25T13:43:04","modified_gmt":"2022-01-25T21:43:04","slug":"critical-path-analysis-in-timing-captures","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/pix\/critical-path-analysis-in-timing-captures\/","title":{"rendered":"Critical path analysis in Timing Captures"},"content":{"rendered":"<p>The<a href=\"https:\/\/devblogs.microsoft.com\/pix\/download\/\"> 2201.24 version of PIX on Windows<\/a> includes a new feature that uses the CPU context switch data collected during a <a href=\"https:\/\/devblogs.microsoft.com\/pix\/timing-captures-new\/\">Timing Capture<\/a> to compute the critical path for a selected PIX event. The critical path is the series of events and thread dependencies that, if shortened, would reduce the overall duration of the selected event.<\/p>\n<p>As an example, consider the following PIX event named <strong>MainLoop 17864<\/strong>.\u00a0 This event represents one frame of CPU time. In this frame, we can see where execution begins and ends, as represented by the dark blue boxes.\u00a0 However, there is also a significant portion of the frame drawn in a dim hatched color.\u00a0 This period of time represents a stall, or a period of time in which our main thread is not running.<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/pix\/wp-content\/uploads\/sites\/41\/2021\/12\/critical_path_stalled_frame.png\"><img decoding=\"async\" class=\"alignnone size-full wp-image-5693\" src=\"https:\/\/devblogs.microsoft.com\/pix\/wp-content\/uploads\/sites\/41\/2021\/12\/critical_path_stalled_frame.png\" alt=\"Image critical path stalled frame\" width=\"511\" height=\"294\" srcset=\"https:\/\/devblogs.microsoft.com\/pix\/wp-content\/uploads\/sites\/41\/2021\/12\/critical_path_stalled_frame.png 511w, https:\/\/devblogs.microsoft.com\/pix\/wp-content\/uploads\/sites\/41\/2021\/12\/critical_path_stalled_frame-300x173.png 300w\" sizes=\"(max-width: 511px) 100vw, 511px\" \/><\/a><\/p>\n<p>If we can reduce, or eliminate this time altogether, we can reduce the overall duration of the frame.\u00a0 This is where critical path analysis comes in.<\/p>\n<p>To compute the critical path, right click on the event and select <strong>Calculate Critical Path for Selected PIX event<\/strong> from the context menu.<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/pix\/wp-content\/uploads\/sites\/41\/2021\/12\/critical_path_context_menu.png\"><img decoding=\"async\" class=\"alignnone size-full wp-image-5695\" src=\"https:\/\/devblogs.microsoft.com\/pix\/wp-content\/uploads\/sites\/41\/2021\/12\/critical_path_context_menu.png\" alt=\"Image critical path context menu\" width=\"619\" height=\"358\" srcset=\"https:\/\/devblogs.microsoft.com\/pix\/wp-content\/uploads\/sites\/41\/2021\/12\/critical_path_context_menu.png 619w, https:\/\/devblogs.microsoft.com\/pix\/wp-content\/uploads\/sites\/41\/2021\/12\/critical_path_context_menu-300x174.png 300w\" sizes=\"(max-width: 619px) 100vw, 619px\" \/><\/a><\/p>\n<p>The critical path is displayed in a new lane in the Timeline named \u201cCritical path\u201d followed by the event name.\u00a0 As before, we can see the periods of time at the beginning and the end of the frame when our main thread is executing.\u00a0 But now, the shaded area representing the stall is filled in with information about the events that were running during the stall, and therefore accounted for the overall stalled time.<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/pix\/wp-content\/uploads\/sites\/41\/2021\/12\/critical_path_critical_path_lane.png\"><img decoding=\"async\" class=\"alignnone size-full wp-image-5697\" src=\"https:\/\/devblogs.microsoft.com\/pix\/wp-content\/uploads\/sites\/41\/2021\/12\/critical_path_critical_path_lane.png\" alt=\"Image critical path critical path lane\" width=\"1158\" height=\"237\" srcset=\"https:\/\/devblogs.microsoft.com\/pix\/wp-content\/uploads\/sites\/41\/2021\/12\/critical_path_critical_path_lane.png 1158w, https:\/\/devblogs.microsoft.com\/pix\/wp-content\/uploads\/sites\/41\/2021\/12\/critical_path_critical_path_lane-300x61.png 300w, https:\/\/devblogs.microsoft.com\/pix\/wp-content\/uploads\/sites\/41\/2021\/12\/critical_path_critical_path_lane-1024x210.png 1024w, https:\/\/devblogs.microsoft.com\/pix\/wp-content\/uploads\/sites\/41\/2021\/12\/critical_path_critical_path_lane-768x157.png 768w\" sizes=\"(max-width: 1158px) 100vw, 1158px\" \/><\/a><\/p>\n<p>The change in color of the thread indicator line during the stall from peach, to green to orange indicates that there are three different threads running during this time.<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/pix\/wp-content\/uploads\/sites\/41\/2021\/12\/critical_path_thread_indicator.png\"><img decoding=\"async\" class=\"alignnone size-full wp-image-5699\" src=\"https:\/\/devblogs.microsoft.com\/pix\/wp-content\/uploads\/sites\/41\/2021\/12\/critical_path_thread_indicator.png\" alt=\"Image critical path thread indicator\" width=\"1091\" height=\"218\" srcset=\"https:\/\/devblogs.microsoft.com\/pix\/wp-content\/uploads\/sites\/41\/2021\/12\/critical_path_thread_indicator.png 1091w, https:\/\/devblogs.microsoft.com\/pix\/wp-content\/uploads\/sites\/41\/2021\/12\/critical_path_thread_indicator-300x60.png 300w, https:\/\/devblogs.microsoft.com\/pix\/wp-content\/uploads\/sites\/41\/2021\/12\/critical_path_thread_indicator-1024x205.png 1024w, https:\/\/devblogs.microsoft.com\/pix\/wp-content\/uploads\/sites\/41\/2021\/12\/critical_path_thread_indicator-768x153.png 768w\" sizes=\"(max-width: 1091px) 100vw, 1091px\" \/><\/a><\/p>\n<p>So now that we\u2019ve seen what\u2019s running, let\u2019s look at why those events ran, which resulted in this long stall.<\/p>\n<p>We\u2019ll start by focusing on the transition from our main thread to the first thread that runs during the stall.\u00a0 Clicking on the vertical red line at the point in time when our main thread stopped running causes a dependency arrow to be drawn.\u00a0 This arrow indicates there is a direct dependency between these two threads.<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/pix\/wp-content\/uploads\/sites\/41\/2021\/12\/critical_path_thread_dependency_arrow.png\"><img decoding=\"async\" class=\"alignnone size-full wp-image-5701\" src=\"https:\/\/devblogs.microsoft.com\/pix\/wp-content\/uploads\/sites\/41\/2021\/12\/critical_path_thread_dependency_arrow.png\" alt=\"Image critical path thread dependency arrow\" width=\"723\" height=\"184\" srcset=\"https:\/\/devblogs.microsoft.com\/pix\/wp-content\/uploads\/sites\/41\/2021\/12\/critical_path_thread_dependency_arrow.png 723w, https:\/\/devblogs.microsoft.com\/pix\/wp-content\/uploads\/sites\/41\/2021\/12\/critical_path_thread_dependency_arrow-300x76.png 300w\" sizes=\"(max-width: 723px) 100vw, 723px\" \/><\/a><\/p>\n<p>Tooltips can be used to understand why the dependency exists.\u00a0 Hovering over the new thread displays a tooltip that describes the reason for the dependency.\u00a0 Specifically, the tooltip shows that the <em>Loader<\/em> thread started running because it was readied by the <em>AppMain<\/em> thread.<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/pix\/wp-content\/uploads\/sites\/41\/2021\/12\/critical_path_thread_tooltip1.png\"><img decoding=\"async\" class=\"alignnone size-full wp-image-5702\" src=\"https:\/\/devblogs.microsoft.com\/pix\/wp-content\/uploads\/sites\/41\/2021\/12\/critical_path_thread_tooltip1.png\" alt=\"Image critical path thread tooltip1\" width=\"796\" height=\"152\" srcset=\"https:\/\/devblogs.microsoft.com\/pix\/wp-content\/uploads\/sites\/41\/2021\/12\/critical_path_thread_tooltip1.png 796w, https:\/\/devblogs.microsoft.com\/pix\/wp-content\/uploads\/sites\/41\/2021\/12\/critical_path_thread_tooltip1-300x57.png 300w, https:\/\/devblogs.microsoft.com\/pix\/wp-content\/uploads\/sites\/41\/2021\/12\/critical_path_thread_tooltip1-768x147.png 768w\" sizes=\"(max-width: 796px) 100vw, 796px\" \/><\/a><\/p>\n<p>The dependency between the <em>Loader<\/em> thread and <em>AppMain<\/em> is one of the reasons for the stall, but it is not the only reason.\u00a0 Two additional threads ran during the time that <em>AppMain<\/em> was stalled, the thread that ran a series of <em>TaskWrapper<\/em> events and the thread that ran an event named <em>DoneWorking<\/em>.<\/p>\n<p>These two threads are unrelated, that is, there is no direct dependency between them.\u00a0 However, the fact that one follows after the other in the timeline means that these events are executing in sequence.\u00a0 But if they\u2019re not dependent on each other, why can\u2019t they run in parallel, thereby reducing the time of our stall?<\/p>\n<p>By hovering over these events, tooltips are displayed that provides the answer.\u00a0 In this case, the two threads were both assigned to the same core (Core 4), and also had the same priority, so core contention is the reason these two events ran sequentially.\u00a0 One way to fix this is to assign these threads to different CPU cores to eliminate this contention.<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/pix\/wp-content\/uploads\/sites\/41\/2021\/12\/critical_path_core_contention.png\"><img decoding=\"async\" class=\"alignnone size-full wp-image-5704\" src=\"https:\/\/devblogs.microsoft.com\/pix\/wp-content\/uploads\/sites\/41\/2021\/12\/critical_path_core_contention.png\" alt=\"Image critical path core contention\" width=\"938\" height=\"282\" srcset=\"https:\/\/devblogs.microsoft.com\/pix\/wp-content\/uploads\/sites\/41\/2021\/12\/critical_path_core_contention.png 938w, https:\/\/devblogs.microsoft.com\/pix\/wp-content\/uploads\/sites\/41\/2021\/12\/critical_path_core_contention-300x90.png 300w, https:\/\/devblogs.microsoft.com\/pix\/wp-content\/uploads\/sites\/41\/2021\/12\/critical_path_core_contention-768x231.png 768w\" sizes=\"(max-width: 938px) 100vw, 938px\" \/><\/a><\/p>\n<p>Analyzing additional context switches and readying events (as represented by the vertical red lines), would show that both the <em>WorkerH1<\/em> and <em>Worker<\/em> threads were readied by the <em>Loader<\/em> thread, giving us a complete picture of what caused the stall.\u00a0 To summarize:<\/p>\n<ol>\n<li>AppMain readied the Loader thread.\u00a0 This caused AppMain to stop running.<\/li>\n<li>The Loader thread then readied WorkerH1 and Worker<\/li>\n<li>WorkerH1 and Worker further lengthened the stall because they ran on the same CPU core<\/li>\n<li>AppMain had to wait for WorkerH1 and Worker to finish before resuming execution.<\/li>\n<\/ol>\n<p>A more complete description of the critical path feature can be found on the XYZ documentation page.<\/p>\n<p>Steven.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>The 2201.24 version of PIX on Windows includes a new feature that uses the CPU context switch data collected during a Timing Capture to compute the critical path for a selected PIX event. The critical path is the series of events and thread dependencies that, if shortened, would reduce the overall duration of the selected [&hellip;]<\/p>\n","protected":false},"author":1915,"featured_media":5693,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[],"class_list":["post-5691","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-pix"],"acf":[],"blog_post_summary":"<p>The 2201.24 version of PIX on Windows includes a new feature that uses the CPU context switch data collected during a Timing Capture to compute the critical path for a selected PIX event. The critical path is the series of events and thread dependencies that, if shortened, would reduce the overall duration of the selected [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/pix\/wp-json\/wp\/v2\/posts\/5691","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/pix\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/pix\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/pix\/wp-json\/wp\/v2\/users\/1915"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/pix\/wp-json\/wp\/v2\/comments?post=5691"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/pix\/wp-json\/wp\/v2\/posts\/5691\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/pix\/wp-json\/wp\/v2\/media\/5693"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/pix\/wp-json\/wp\/v2\/media?parent=5691"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/pix\/wp-json\/wp\/v2\/categories?post=5691"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/pix\/wp-json\/wp\/v2\/tags?post=5691"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}