{"id":707,"date":"2023-10-25T13:03:57","date_gmt":"2023-10-25T20:03:57","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/performance-diagnostics\/?p=707"},"modified":"2023-10-25T17:05:13","modified_gmt":"2023-10-26T00:05:13","slug":"deep-dive-analysis-why-did-this-dll-increase-in-file-size-by-50","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/performance-diagnostics\/deep-dive-analysis-why-did-this-dll-increase-in-file-size-by-50\/","title":{"rendered":"Deep Dive Analysis: Why did this dll increase in file size by 50%?"},"content":{"rendered":"<p>We recently noticed that one of the dlls that we produce had suddenly jumped up in file size. The WinUI Controls library, Microsoft.UI.Xaml.Controls.dll that ships as part of Windows App SDK had ballooned in size in our internal builds. It had suddenly grown from 7 MB to 10.5 MB &#8211; approximately a 50% increase in size. What had happened to cause this? We needed to investigate the issue and fix it.<\/p>\n<p>This post is a deep dive into the analysis that was done to figure out what caused this sudden increase in file size and how it could be fixed.<\/p>\n<p>File size of a dll is important not just due to its impact on diskspace on a user&#8217;s machine but it also has an impact on the app at runtime due to the number of pages loaded into memory. We have internal automated processes to check the size of each dll that is built and to flag up instances where there is a substantial increase in size. That&#8217;s what brought this issue to our attention. We needed to investigate to figure out what caused the increase and how to fix it.<\/p>\n<p>The first thing to do was to look over the commits that had gone into the branch within the relevant time period. None of the code changes that went in looked like they could have had such an impact. It is reasonable to expect new code and new features to increase the file size of the built binary, but the magnitude of the increase was not reasonable based on what had changed. The only change that stood out as a potential candidate was an update to the version number of the C++ compiler build tools that we use. We updated MSVC tools from 14.35 to 14.36. Since none of the other changes looked like they could have had such a big impact, this change looked like the most likely one to have introduced the file size increase.<\/p>\n<p>I tried building the dll on my machine, switching between the two MSVC versions and was able to confirm that moving from 14.35 to 14.36 did in fact increase the file size by 50%. But how?<\/p>\n<p>When investigating issues related to file size of a dll, a great tool to use is SizeBench (see blog post <a href=\"https:\/\/devblogs.microsoft.com\/performance-diagnostics\/sizebench-a-new-tool-for-analyzing-windows-binary-size\/\">SizeBench: a new tool for analyzing Windows binary size<\/a>). It is available to <a href=\"https:\/\/www.microsoft.com\/store\/productId\/9NDF4N1WG7D6\">install from the Microsoft Store<\/a>. This is a powerful tool that lets you open a dll along with its matching pdb symbols file and see a breakdown of how the space is being consumed by the different parts of your dll. One very useful feature is its ability to perform a diff of two different versions of a binary to see what has changed. If a particular class or function has suddenly gotten much bigger, it will show up in the diff.<\/p>\n<p>We look at a diff of the Compilands (Obj files) and sort by file size diff.<\/p>\n<p><img decoding=\"async\" class=\"wp-image-718\" src=\"https:\/\/devblogs.microsoft.com\/performance-diagnostics\/wp-content\/uploads\/sites\/64\/2023\/10\/a-screenshot-of-a-computer-description-automatica-8.png\" alt=\"A screenshot of SizeBench showing the diff in file size of a list of obj files\" width=\"733\" height=\"625\" \/><\/p>\n<p>No one .obj jumps out as the source of the 3.5 MB file size increase &#8211; instead it seems like many of the compilands increased by a few dozen kilobytes. We drill in some more to one of the obj files to see if we can figure out what is going on.<\/p>\n<p>Here we see a breakdown of the 87 KB increase in ScrollPresenter.properties:<\/p>\n<p><img decoding=\"async\" class=\"wp-image-719\" src=\"https:\/\/devblogs.microsoft.com\/performance-diagnostics\/wp-content\/uploads\/sites\/64\/2023\/10\/a-screenshot-of-a-computer-description-automatica-9.png\" alt=\"A screenshot of SizeBench showing the file size increase of ScrollPresenter.properties\" width=\"734\" height=\"774\" srcset=\"https:\/\/devblogs.microsoft.com\/performance-diagnostics\/wp-content\/uploads\/sites\/64\/2023\/10\/a-screenshot-of-a-computer-description-automatica-9.png 996w, https:\/\/devblogs.microsoft.com\/performance-diagnostics\/wp-content\/uploads\/sites\/64\/2023\/10\/a-screenshot-of-a-computer-description-automatica-9-285x300.png 285w, https:\/\/devblogs.microsoft.com\/performance-diagnostics\/wp-content\/uploads\/sites\/64\/2023\/10\/a-screenshot-of-a-computer-description-automatica-9-972x1024.png 972w, https:\/\/devblogs.microsoft.com\/performance-diagnostics\/wp-content\/uploads\/sites\/64\/2023\/10\/a-screenshot-of-a-computer-description-automatica-9-768x809.png 768w, https:\/\/devblogs.microsoft.com\/performance-diagnostics\/wp-content\/uploads\/sites\/64\/2023\/10\/a-screenshot-of-a-computer-description-automatica-9-24x24.png 24w\" sizes=\"(max-width: 734px) 100vw, 734px\" \/><\/p>\n<p>The increase is primarily in the &#8220;Code&#8221; and &#8220;Read-only data&#8221; sections of the binary. In the bottom right portion of the window, we can see a long list of individual symbols in the compiland that increased in size. Drilling in some more we take a look at one of the functions. We look at the function with the beautiful name of <code>winrt::impl::produce&lt;winrt::impl::reference&lt;enum winrt::Microsoft::UI::Xaml::Controls::ScrollingScrollMode&gt;, winrt::Windows::Foundation::IPropertyValue&gt;::GetUInt8Array<\/code><\/p>\n<p><img decoding=\"async\" width=\"2003\" height=\"1049\" class=\"wp-image-720\" src=\"https:\/\/devblogs.microsoft.com\/performance-diagnostics\/wp-content\/uploads\/sites\/64\/2023\/10\/a-screenshot-of-a-computer-description-automatica-10.png\" alt=\"A screenshot of SizeBench showing a diff of a function GetUInt8Array between the two dlls.\" srcset=\"https:\/\/devblogs.microsoft.com\/performance-diagnostics\/wp-content\/uploads\/sites\/64\/2023\/10\/a-screenshot-of-a-computer-description-automatica-10.png 2003w, https:\/\/devblogs.microsoft.com\/performance-diagnostics\/wp-content\/uploads\/sites\/64\/2023\/10\/a-screenshot-of-a-computer-description-automatica-10-300x157.png 300w, https:\/\/devblogs.microsoft.com\/performance-diagnostics\/wp-content\/uploads\/sites\/64\/2023\/10\/a-screenshot-of-a-computer-description-automatica-10-1024x536.png 1024w, https:\/\/devblogs.microsoft.com\/performance-diagnostics\/wp-content\/uploads\/sites\/64\/2023\/10\/a-screenshot-of-a-computer-description-automatica-10-768x402.png 768w, https:\/\/devblogs.microsoft.com\/performance-diagnostics\/wp-content\/uploads\/sites\/64\/2023\/10\/a-screenshot-of-a-computer-description-automatica-10-1536x804.png 1536w\" sizes=\"(max-width: 2003px) 100vw, 2003px\" \/><\/p>\n<p>Now this is interesting. In the &#8216;After&#8217; case, we have a unique function for this instantiation of the GetUInt8Array function template. But in the &#8216;Before&#8217; case we can see that there are 81 instantiations that all got folded together in the final binary as a result of COMDAT folding. This is an optimization feature of the linker where it will fold together functions that compiled down to identical bytes of code so it only needs to have a single copy in the binary file. Looking around in SizeBench some more we can find many instances of the same pattern where functions are getting folded in the Before case but not in the After case. Even though each of these functions is small individually, there are hundreds of them each with many instances that are no longer being folded. Something happened to prevent these functions from getting folded. But what?<\/p>\n<p>To dig in deeper, we use <a href=\"https:\/\/aka.ms\/windbg\">WinDbg<\/a>. This debugging tool has somewhat of a steep learning curve, but it is very powerful. In addition to debugging a running application or examining a memory dump, you can also directly open a dll to examine it. Counter-intuitively, you do this through choosing the &#8216;Open dump file&#8217; menu and selecting the dll file. You also need to make sure that the tool can find and load the matching pdb file.<\/p>\n<p>We launch two instances of WinDbg and open the Before dll and the After dll side by side. We use the &#8216;x&#8217; (<a href=\"https:\/\/learn.microsoft.com\/windows-hardware\/drivers\/debuggercmds\/x--examine-symbols-\">x (Examine Symbols)<\/a>) command to search for symbols matching a pattern like the functions we saw in SizeBench previously:<\/p>\n<p><code>x \/D \/a Microsoft_UI_Xaml_Controls!winrt::impl::produce*::GetUInt8Array<\/code><\/p>\n<p>We compare the results for Before and After.<\/p>\n<p>Before:<\/p>\n<p><img decoding=\"async\" width=\"1493\" height=\"750\" class=\"wp-image-721\" src=\"https:\/\/devblogs.microsoft.com\/performance-diagnostics\/wp-content\/uploads\/sites\/64\/2023\/10\/a-screenshot-of-a-computer-description-automatica-11.png\" alt=\"A screenshot of WinDBG showing a dump of all symbols matching a pattern.\" srcset=\"https:\/\/devblogs.microsoft.com\/performance-diagnostics\/wp-content\/uploads\/sites\/64\/2023\/10\/a-screenshot-of-a-computer-description-automatica-11.png 1493w, https:\/\/devblogs.microsoft.com\/performance-diagnostics\/wp-content\/uploads\/sites\/64\/2023\/10\/a-screenshot-of-a-computer-description-automatica-11-300x151.png 300w, https:\/\/devblogs.microsoft.com\/performance-diagnostics\/wp-content\/uploads\/sites\/64\/2023\/10\/a-screenshot-of-a-computer-description-automatica-11-1024x514.png 1024w, https:\/\/devblogs.microsoft.com\/performance-diagnostics\/wp-content\/uploads\/sites\/64\/2023\/10\/a-screenshot-of-a-computer-description-automatica-11-768x386.png 768w\" sizes=\"(max-width: 1493px) 100vw, 1493px\" \/><\/p>\n<p>After:<\/p>\n<p><img decoding=\"async\" class=\"alignnone size-full\" src=\"https:\/\/devblogs.microsoft.com\/performance-diagnostics\/wp-content\/uploads\/sites\/64\/2023\/10\/windbgAfter2-2.png\" alt=\"A screenshot of WinDBG showing a dump of all symbols matching a pattern.\" width=\"1492\" height=\"743\" \/><\/p>\n<p>As expected, in the Before case each of these functions share the same address, but in the After case each has a unique address (see the highlighted column on the left in the screenshots).<\/p>\n<p>Let&#8217;s compare two of these unique functions in the After case to see what is different. We use the &#8216;uf&#8217; command (<a href=\"https:\/\/learn.microsoft.com\/windows-hardware\/drivers\/debuggercmds\/uf--unassemble-function-\">uf (Unassemble Function)<\/a>) to disassemble the functions:<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/performance-diagnostics\/wp-content\/uploads\/sites\/64\/2023\/10\/uf1-1.png\"><img decoding=\"async\" class=\"alignnone size-full wp-image-743\" src=\"https:\/\/devblogs.microsoft.com\/performance-diagnostics\/wp-content\/uploads\/sites\/64\/2023\/10\/uf1-1.png\" alt=\"A screenshot of WinDbg showing the disassembly of a function.\" width=\"1737\" height=\"567\" srcset=\"https:\/\/devblogs.microsoft.com\/performance-diagnostics\/wp-content\/uploads\/sites\/64\/2023\/10\/uf1-1.png 1737w, https:\/\/devblogs.microsoft.com\/performance-diagnostics\/wp-content\/uploads\/sites\/64\/2023\/10\/uf1-1-300x98.png 300w, https:\/\/devblogs.microsoft.com\/performance-diagnostics\/wp-content\/uploads\/sites\/64\/2023\/10\/uf1-1-1024x334.png 1024w, https:\/\/devblogs.microsoft.com\/performance-diagnostics\/wp-content\/uploads\/sites\/64\/2023\/10\/uf1-1-768x251.png 768w, https:\/\/devblogs.microsoft.com\/performance-diagnostics\/wp-content\/uploads\/sites\/64\/2023\/10\/uf1-1-1536x501.png 1536w\" sizes=\"(max-width: 1737px) 100vw, 1737px\" \/><\/a><\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/performance-diagnostics\/wp-content\/uploads\/sites\/64\/2023\/10\/uf2.png\"><img decoding=\"async\" class=\"alignnone size-full wp-image-745\" src=\"https:\/\/devblogs.microsoft.com\/performance-diagnostics\/wp-content\/uploads\/sites\/64\/2023\/10\/uf2.png\" alt=\"A screenshot of WinDbg showing the disassembly of a function.\" width=\"1741\" height=\"580\" srcset=\"https:\/\/devblogs.microsoft.com\/performance-diagnostics\/wp-content\/uploads\/sites\/64\/2023\/10\/uf2.png 1741w, https:\/\/devblogs.microsoft.com\/performance-diagnostics\/wp-content\/uploads\/sites\/64\/2023\/10\/uf2-300x100.png 300w, https:\/\/devblogs.microsoft.com\/performance-diagnostics\/wp-content\/uploads\/sites\/64\/2023\/10\/uf2-1024x341.png 1024w, https:\/\/devblogs.microsoft.com\/performance-diagnostics\/wp-content\/uploads\/sites\/64\/2023\/10\/uf2-768x256.png 768w, https:\/\/devblogs.microsoft.com\/performance-diagnostics\/wp-content\/uploads\/sites\/64\/2023\/10\/uf2-1536x512.png 1536w\" sizes=\"(max-width: 1741px) 100vw, 1741px\" \/><\/a><\/p>\n<p>It&#8217;s not immediately obvious at first glance, but we can see that these two functions are identical, except the address passed to the second lea instruction is different across the two cases (highlighted). Let&#8217;s examine those addresses. We use the &#8216;da&#8217; command to dump out the content at the address as a string.<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/performance-diagnostics\/wp-content\/uploads\/sites\/64\/2023\/10\/da.png\"><img decoding=\"async\" class=\"alignnone size-full wp-image-751\" src=\"https:\/\/devblogs.microsoft.com\/performance-diagnostics\/wp-content\/uploads\/sites\/64\/2023\/10\/da.png\" alt=\"A screenshot of WinDbg showing the da command dumping out strings that contain the full names of two functions\" width=\"1737\" height=\"154\" srcset=\"https:\/\/devblogs.microsoft.com\/performance-diagnostics\/wp-content\/uploads\/sites\/64\/2023\/10\/da.png 1737w, https:\/\/devblogs.microsoft.com\/performance-diagnostics\/wp-content\/uploads\/sites\/64\/2023\/10\/da-300x27.png 300w, https:\/\/devblogs.microsoft.com\/performance-diagnostics\/wp-content\/uploads\/sites\/64\/2023\/10\/da-1024x91.png 1024w, https:\/\/devblogs.microsoft.com\/performance-diagnostics\/wp-content\/uploads\/sites\/64\/2023\/10\/da-768x68.png 768w, https:\/\/devblogs.microsoft.com\/performance-diagnostics\/wp-content\/uploads\/sites\/64\/2023\/10\/da-1536x136.png 1536w\" sizes=\"(max-width: 1737px) 100vw, 1737px\" \/><\/a><\/p>\n<p>So, we are loading a string that contains the full name of the function. Since these are obviously different across the two functions it makes sense that they cannot be COMDAT folded. What did this look like in the Before case?<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/performance-diagnostics\/wp-content\/uploads\/sites\/64\/2023\/10\/uf-before.png\"><img decoding=\"async\" class=\"alignnone size-full wp-image-753\" src=\"https:\/\/devblogs.microsoft.com\/performance-diagnostics\/wp-content\/uploads\/sites\/64\/2023\/10\/uf-before.png\" alt=\"A screenshot of WinDbg showing the da command dumping out an address containing the string GetUInt8Array\" width=\"1738\" height=\"628\" srcset=\"https:\/\/devblogs.microsoft.com\/performance-diagnostics\/wp-content\/uploads\/sites\/64\/2023\/10\/uf-before.png 1738w, https:\/\/devblogs.microsoft.com\/performance-diagnostics\/wp-content\/uploads\/sites\/64\/2023\/10\/uf-before-300x108.png 300w, https:\/\/devblogs.microsoft.com\/performance-diagnostics\/wp-content\/uploads\/sites\/64\/2023\/10\/uf-before-1024x370.png 1024w, https:\/\/devblogs.microsoft.com\/performance-diagnostics\/wp-content\/uploads\/sites\/64\/2023\/10\/uf-before-768x278.png 768w, https:\/\/devblogs.microsoft.com\/performance-diagnostics\/wp-content\/uploads\/sites\/64\/2023\/10\/uf-before-1536x555.png 1536w\" sizes=\"(max-width: 1738px) 100vw, 1738px\" \/><\/a><\/p>\n<p>Ah ha! So, in the Before case the second string loaded is just the simple name of the function (&#8220;GetUInt8Array&#8221;). But in the After case, it is the fully qualified name of the function including the template arguments.<\/p>\n<p>So, we have partially explained what caused the file size increase. But what introduced this change? We examine the source for this function. We can see the path and line number in WinDbg. This code is generated by a tool <a href=\"https:\/\/github.com\/microsoft\/cppwinrt\">C++\/WinRT<\/a> which makes heavy use of C++ templates.<\/p>\n<pre class=\"prettyprint language-cpp\"><code class=\"language-cpp\">int32_t __stdcall GetUInt8Array(uint32_t* __valueSize, uint8_t** value) noexcept final try\r\n{\r\n    clear_abi(value);\r\n    typename D::abi_guard guard(this-&gt;shim());\r\n    this-&gt;shim().GetUInt8Array(detach_abi&lt;uint8_t&gt;(__valueSize, value));\r\n    return 0;\r\n}\r\ncatch (...) { return to_hresult(); }<\/code><\/pre>\n<p>On its own this isn&#8217;t too enlightening, but from the disassembly listed above, we can tell that a winrt::hresult_not_implemented is being created and thrown. Let&#8217;s look at that:<\/p>\n<pre><code class=\"language-cpp\">struct hresult_not_implemented : hresult_error\r\n{\r\n\u00a0  hresult_not_implemented(WINRT_IMPL_SOURCE_LOCATION_ARGS_SINGLE_PARAM) noexcept :\r\n\u00a0 \u00a0hresult_error(impl::error_not_implemented WINRT_IMPL_SOURCE_LOCATION_FORWARD) {}\r\n\u00a0 \u00a0hresult_not_implemented(param::hstring const&amp; message WINRT_IMPL_SOURCE_LOCATION_ARGS) noexcept :\r\n\u00a0 \u00a0hresult_error(impl::error_not_implemented, message WINRT_IMPL_SOURCE_LOCATION_FORWARD) {}\r\n\u00a0 \u00a0hresult_not_implemented(take_ownership_from_abi_t WINRT_IMPL_SOURCE_LOCATION_ARGS) noexcept :\r\n\u00a0 \u00a0hresult_error(impl::error_not_implemented, take_ownership_from_abi WINRT_IMPL_SOURCE_LOCATION_FORWARD) {}\r\n}; <\/code><\/pre>\n<p>(from <a href=\"https:\/\/github.com\/microsoft\/cppwinrt\/blob\/bf4459b25aeeb7093e8262fb3b29ca70b0ab2e60\/strings\/base_error.h#L356\">base_error.h#L356<\/a>)<\/p>\n<p>The <code>WINRT_IMPL_SOURCE_LOCATION_ARGS_SINGLE_PARAM<\/code> macro seems relevant. We search for that:<\/p>\n<pre class=\"prettyprint language-cpp\"><code class=\"language-cpp\">#define WINRT_IMPL_SOURCE_LOCATION_ARGS_SINGLE_PARAM std::source_location const&amp; sourceInformation = std::source_location::current()<\/code><\/pre>\n<p>(from <a href=\"https:\/\/github.com\/microsoft\/cppwinrt\/blob\/bf4459b25aeeb7093e8262fb3b29ca70b0ab2e60\/strings\/base_macros.h#L90\">base_macros.h#L90<\/a>)<\/p>\n<p>And seeing where this is used:<\/p>\n<pre class=\"prettyprint language-cpp\"><code class=\"language-cpp\">winrt_throw_hresult_handler(sourceInformation.line(), sourceInformation.file_name(), sourceInformation.function_name(), WINRT_IMPL_RETURNADDRESS(), code);<\/code><\/pre>\n<p>(from <a href=\"https:\/\/github.com\/microsoft\/cppwinrt\/blob\/bf4459b25aeeb7093e8262fb3b29ca70b0ab2e60\/strings\/base_error.h#L309\">base_error.h#L309<\/a>)<\/p>\n<p>The call to <code>function_name()<\/code> looks like what we are looking for. A quick experiment in a test app confirms that <code>std::source_location::function_name<\/code> returns the simple function name in MSVC 14.35, but the fully qualified name including namespace and template arguments in MSVC 14.36. It turns out that this was a recent update in the MSVC standard library to enable this new functionality.<\/p>\n<p>So now we know what changed and how that caused the file size of our dll to increase so much. So, what can we do to fix it?<\/p>\n<p>Luckily C++\/WinRT includes a feature that allows us to turn off the use of std::source_location:<\/p>\n<p><a href=\"https:\/\/github.com\/microsoft\/cppwinrt\/pull\/1260\">Add a mechanism to suppress std::source_location by dmachaj \u00b7 Pull Request #1260 \u00b7 microsoft\/cppwinrt<\/a><\/p>\n<p>So, we update our code to define <code>WINRT_NO_SOURCE_LOCATION<\/code> and rebuild. Sure enough, we see the file size drop back down to where it was before. Success!<\/p>\n<p>After completing our analysis using SizeBench and WinDbg we were able to get to the bottom of the mystery of why our dll had suddenly jumped in size by 50% and to find the right fix to bring the size back down to the previous level.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>We recently noticed that one of the dlls that we produce had suddenly jumped up in file size. The WinUI Controls library, Microsoft.UI.Xaml.Controls.dll that ships as part of Windows App SDK had ballooned in size in our internal builds. It had suddenly grown from 7 MB to 10.5 MB &#8211; approximately a 50% increase in [&hellip;]<\/p>\n","protected":false},"author":132043,"featured_media":719,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1,34],"tags":[],"class_list":["post-707","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-performance-diagnostics","category-sizebench"],"acf":[],"blog_post_summary":"<p>We recently noticed that one of the dlls that we produce had suddenly jumped up in file size. The WinUI Controls library, Microsoft.UI.Xaml.Controls.dll that ships as part of Windows App SDK had ballooned in size in our internal builds. It had suddenly grown from 7 MB to 10.5 MB &#8211; approximately a 50% increase in [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/performance-diagnostics\/wp-json\/wp\/v2\/posts\/707","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/performance-diagnostics\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/performance-diagnostics\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/performance-diagnostics\/wp-json\/wp\/v2\/users\/132043"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/performance-diagnostics\/wp-json\/wp\/v2\/comments?post=707"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/performance-diagnostics\/wp-json\/wp\/v2\/posts\/707\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/performance-diagnostics\/wp-json\/wp\/v2\/media\/719"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/performance-diagnostics\/wp-json\/wp\/v2\/media?parent=707"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/performance-diagnostics\/wp-json\/wp\/v2\/categories?post=707"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/performance-diagnostics\/wp-json\/wp\/v2\/tags?post=707"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}