{"id":27969,"date":"2021-05-07T12:38:13","date_gmt":"2021-05-07T12:38:13","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/cppblog\/?p=27969"},"modified":"2021-05-07T12:38:13","modified_gmt":"2021-05-07T12:38:13","slug":"2x-3x-performance-improvements-for-debug-builds","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/cppblog\/2x-3x-performance-improvements-for-debug-builds\/","title":{"rendered":"2x-3x Performance Improvements for Debug Builds"},"content":{"rendered":"<p><span data-contrast=\"auto\">We have made substantial runtime performance improvements in the x86\/x64 C++ compiler for Visual Studio&#8217;s default debug configuration. For\u00a0<\/span><a href=\"https:\/\/visualstudio.microsoft.com\/vs\/preview\/%22%20\/\"><span data-contrast=\"none\">Visual Studio 2019<\/span><span data-contrast=\"auto\">\u00a0version 16.10 Preview 2<\/span><\/a><span data-contrast=\"auto\">, we measure\u00a02x &#8211;\u00a03x speedup for\u00a0programs compiled\u00a0in\u00a0debug\u00a0mode.\u00a0These\u00a0improvements\u00a0come from\u00a0reducing the\u00a0overhead\u00a0introduced by runtime checks (\/RTCs)\u00a0which are\u00a0enabled\u00a0by\u00a0default.<\/span><span data-ccp-props=\"{&quot;201341983&quot;:0,&quot;335559739&quot;:160,&quot;335559740&quot;:259}\">\u00a0<\/span><\/p>\n<h3><b><span data-contrast=\"auto\">Default debug configuration<\/span><\/b><span data-ccp-props=\"{&quot;201341983&quot;:0,&quot;335559739&quot;:160,&quot;335559740&quot;:259}\">\u00a0<\/span><\/h3>\n<p><span data-contrast=\"auto\">When you compile your code in debug configuration in Visual\u00a0Studio, there are some flags that are\u00a0passed\u00a0to the C++ compiler by default. Most relevant to this blog post\u00a0are\u00a0<\/span><a href=\"https:\/\/docs.microsoft.com\/en-us\/cpp\/build\/reference\/rtc-run-time-error-checks?view=msvc-160\"><span data-contrast=\"none\">\/RTC1<\/span><\/a><span data-contrast=\"auto\">,\u00a0<\/span><a href=\"https:\/\/docs.microsoft.com\/en-us\/cpp\/build\/reference\/jmc?view=msvc-160\"><span data-contrast=\"none\">\/JMC<\/span><\/a><span data-contrast=\"auto\">\u00a0and\u00a0<\/span><a href=\"https:\/\/docs.microsoft.com\/en-us\/cpp\/build\/reference\/z7-zi-zi-debug-information-format?view=msvc-160\"><span data-contrast=\"none\">\/ZI<\/span><\/a><span data-contrast=\"auto\">.\u00a0<\/span><span data-ccp-props=\"{&quot;201341983&quot;:0,&quot;335559739&quot;:160,&quot;335559740&quot;:259}\">\u00a0<\/span><\/p>\n<p><span data-contrast=\"auto\">While\u00a0all of\u00a0these flags\u00a0add useful debugging functionality, their interactions, particularly when \/RTC1 is involved,\u00a0add\u00a0significant overhead.\u00a0In\u00a0this release, we removed the unnecessary overhead while making sure\u00a0they\u00a0keep helping you find bugs and make your debugging experience smoother.<\/span><span data-ccp-props=\"{&quot;201341983&quot;:0,&quot;335559739&quot;:160,&quot;335559740&quot;:259}\">\u00a0<\/span><\/p>\n<p><span data-contrast=\"auto\">Consider the following simple function:<\/span><\/p>\n<div>\n<div>\n<pre>1    int foo() {\r\n2        return 32;\r\n3    }<\/pre>\n<\/div>\n<\/div>\n<p>and <span data-contrast=\"auto\">the x64 assembly generated by the 16.9 compiler when compiled with \/RTC1 \/JMC \/ZI (<a href=\"https:\/\/godbolt.org\/z\/MnWehTso7\">Godbolt link<\/a>):<\/span><span data-ccp-props=\"{&quot;201341983&quot;:0,&quot;335559739&quot;:160,&quot;335559740&quot;:259}\">\u00a0<\/span><\/p>\n<div>\n<pre>1    int foo(void) PROC\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\r\n2    $LN3:\r\n3    \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0push rbp\r\n4    \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0push rdi\r\n5    \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0sub rsp, 232\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0; extra space allocated due to \/ZI, \/JMC\r\n6    \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0lea rbp, QWORD PTR [rsp+32]\r\n7    \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0mov rdi, rsp\r\n8     \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0mov ecx, 58\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0; (= x)\r\n9    \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0mov eax, -858993460\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0; 0xCCCCCCCC\r\n10   \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0rep stosd\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0; write 0xCC on stack for x DWORDs\r\n11   \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0lea rcx, OFFSET FLAT:__977E49D0_example@cpp\r\n12   \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0; call due to \/JMC\r\n13   \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0call __CheckForDebuggerJustMyCode\r\n14    \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0mov eax, 32\r\n15   \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0lea rsp, QWORD PTR [rbp+200]\r\n16   \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0pop rdi\r\n17   \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0pop rbp\r\n18   \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0ret 0\r\n19    int foo(void) ENDP<\/pre>\n<\/div>\n<p><span data-contrast=\"auto\">In the assembly shown above, the \/JMC and \/ZI flags add a total of 232 additional bytes on the stack (line 5). This stack space is not always necessary. When combined with the \/RTC1 flag, which initializes the allocated stack space (line 10), it consumes a lot of CPU cycles. In this specific example, even though the stack space we allocated is necessary for proper functioning of \/JMC and \/ZI, its initialization is not. We can prove at compile time that these checks are unnecessary. There are plenty of such functions in any real-world C++ codebase, and that\u2019s where the performance benefit comes from.<\/span><span data-ccp-props=\"{&quot;201341983&quot;:0,&quot;335559739&quot;:160,&quot;335559740&quot;:259}\">\u00a0<\/span><\/p>\n<p><span data-contrast=\"auto\">Keep reading to get a deeper dive into each of these flags,\u00a0their interactions\u00a0with \/RTC1,\u00a0and how we avoid\u00a0its\u00a0unnecessary overhead.<\/span><span data-ccp-props=\"{&quot;201341983&quot;:0,&quot;335559739&quot;:160,&quot;335559740&quot;:259}\">\u00a0<\/span><\/p>\n<h4><b><span data-contrast=\"auto\">\/RTC1<\/span><\/b><span data-ccp-props=\"{&quot;201341983&quot;:0,&quot;335559739&quot;:160,&quot;335559740&quot;:259}\">\u00a0<\/span><\/h4>\n<p><span data-contrast=\"auto\">Using\u00a0<\/span><a href=\"https:\/\/docs.microsoft.com\/en-us\/cpp\/build\/reference\/rtc-run-time-error-checks?view=msvc-160\"><span data-contrast=\"none\">\/RTC1<\/span><\/a><span data-contrast=\"auto\">\u00a0flag\u00a0is equivalent to\u00a0using both\u00a0\/RTCs\u00a0and\u00a0\/RTCu\u00a0flags.\u00a0\/RTCs\u00a0initializes the stack frame of functions by 0xCC\u00a0to do various runtime checks\u00a0namely,\u00a0detecting uninitialized local variables,\u00a0detecting\u00a0array\u00a0overrun and underruns, and stack pointer verification\u00a0(for x86).\u00a0You can see the code bloat\u00a0with\u00a0\/RTCs\u00a0<\/span><a href=\"https:\/\/godbolt.org\/z\/fEo9sYGTG\"><span data-contrast=\"none\">here<\/span><\/a><span data-contrast=\"auto\">.<\/span><span data-ccp-props=\"{&quot;201341983&quot;:0,&quot;335559739&quot;:160,&quot;335559740&quot;:259}\">\u00a0<\/span><\/p>\n<p><span data-contrast=\"auto\">As\u00a0seen in the above\u00a0assembly\u00a0code\u00a0(line 10), the\u00a0<\/span><code><i><span data-contrast=\"auto\">rep\u00a0stosd<\/span><\/i><\/code><span data-contrast=\"auto\">\u00a0instruction, introduced by \/RTCs,\u00a0is the main reason for the slowdown. The situation is exacerbated when \/RTCs\u00a0(or \/RTC1)\u00a0is used\u00a0in conjunction\u00a0with \/JMC,\u00a0\/ZI, or both.<\/span><span data-ccp-props=\"{&quot;201341983&quot;:0,&quot;335559739&quot;:160,&quot;335559740&quot;:259}\">\u00a0<\/span><\/p>\n<h4><b><span data-contrast=\"auto\">Interactions with \/JMC<\/span><\/b><span data-ccp-props=\"{&quot;201341983&quot;:0,&quot;335559739&quot;:160,&quot;335559740&quot;:259}\">\u00a0<\/span><\/h4>\n<p><a href=\"https:\/\/docs.microsoft.com\/en-us\/cpp\/build\/reference\/jmc?view=msvc-160\"><span data-contrast=\"none\">\/JMC<\/span><\/a><span data-contrast=\"auto\">\u00a0stands for\u00a0<\/span><i><span data-contrast=\"auto\">Just\u00a0My\u00a0Code\u00a0Debugging<\/span><\/i><span data-contrast=\"auto\">\u00a0functionality<\/span><span data-contrast=\"auto\">,<\/span><span data-contrast=\"auto\"> and during debugging, it automatically skips over functions that are not written by you (such as framework, library, and other non-user code). It works by inserting a function call in the prologue that calls into the runtime library. This helps the debugger to distinguish between user and non-user code. The problem here is that inserting a function call into the prologue of every function in your project means that there are no leaf functions anymore in your whole project. If the function doesn&#8217;t need any stack frame originally, now it will, because as per <\/span><a href=\"https:\/\/docs.microsoft.com\/en-us\/cpp\/build\/stack-usage?view=msvc-160#stack-allocation\"><span data-contrast=\"none\">AMD64 ABI<\/span><\/a> for Windows platforms<span data-contrast=\"auto\">, we need to have at\u00a0least\u00a0four\u00a0stack slots available for function parameters (called\u00a0<em>P<\/em><\/span><i><span data-contrast=\"auto\">aram\u00a0Home\u00a0area<\/span><\/i><span data-contrast=\"auto\">). This means all the functions that were not being initialized earlier by \/RTCs, because they were leaf functions\u00a0and had no stack frame, will now be initialized. It&#8217;s normal to have lots and lots of leaf functions in your program<\/span><span data-contrast=\"auto\">,<\/span><span data-contrast=\"auto\">\u00a0especially if you are using\u00a0a heavily templated code\u00a0library\u00a0like C++ STL.\u00a0\/JMC will happily eat some of your CPU cycles\u00a0in this case. This doesn&#8217;t apply for\u00a0x86\u00a0(32 bit)\u00a0because we don&#8217;t have any param home area there.\u00a0You can see the effects of \/JMC\u00a0<\/span><a href=\"https:\/\/godbolt.org\/z\/v9qE3e7f5\"><span data-contrast=\"none\">here<\/span><\/a><span data-contrast=\"auto\">.<\/span><span data-ccp-props=\"{&quot;201341983&quot;:0,&quot;335559739&quot;:160,&quot;335559740&quot;:259}\">\u00a0<\/span><\/p>\n<h4><b><span data-contrast=\"auto\">Interaction with \/ZI<\/span><\/b><span data-ccp-props=\"{&quot;201341983&quot;:0,&quot;335559739&quot;:160,&quot;335559740&quot;:259}\">\u00a0<\/span><\/h4>\n<p><span data-contrast=\"auto\">The next interaction we are going to talk about is with\u00a0\/ZI. It enables your code for\u00a0<\/span><a href=\"https:\/\/docs.microsoft.com\/en-us\/visualstudio\/debugger\/edit-and-continue-visual-cpp?view=vs-2019\"><span data-contrast=\"none\">Edit and\u00a0Continue<\/span><\/a><span data-contrast=\"auto\">\u00a0support,\u00a0which means you don&#8217;t need to recompile\u00a0the whole\u00a0program during debugging for small changes.<\/span><span data-ccp-props=\"{&quot;201341983&quot;:0,&quot;335559739&quot;:160,&quot;335559740&quot;:259}\">\u00a0<\/span><\/p>\n<p><span data-contrast=\"auto\">In order to\u00a0add such support, we add\u00a0some\u00a0padding\u00a0bytes\u00a0to the stack\u00a0(the\u00a0actual number\u00a0of\u00a0padding\u00a0bytes\u00a0depends on how big a function is). This way<\/span><span data-contrast=\"auto\">,<\/span><span data-contrast=\"auto\">\u00a0all the new variables you add during your debugging session\u00a0can be allocated on\u00a0the padding area\u00a0without changing the\u00a0total\u00a0stack frame size, and you can continue your debugging without having to recompile your code.\u00a0\u00a0See\u00a0<\/span><a href=\"https:\/\/godbolt.org\/z\/oef3Pjn4G\"><span data-contrast=\"none\">here<\/span><\/a><span data-contrast=\"auto\">\u00a0how\u00a0enabling\u00a0this flag adds an extra 64 bytes to the generated code.<\/span><span data-ccp-props=\"{&quot;201341983&quot;:0,&quot;335559739&quot;:160,&quot;335559740&quot;:259}\">\u00a0<\/span><\/p>\n<p><span data-contrast=\"auto\">As you may have guessed, more stack area means more things to initialize by \/RTCs,\u00a0leading to\u00a0more overhead.<\/span><span data-ccp-props=\"{&quot;201341983&quot;:0,&quot;335559739&quot;:160,&quot;335559740&quot;:259}\">\u00a0<\/span><\/p>\n<h3><b><span data-contrast=\"auto\">Solution<\/span><\/b><span data-ccp-props=\"{&quot;201341983&quot;:0,&quot;335559739&quot;:160,&quot;335559740&quot;:259}\">\u00a0<\/span><\/h3>\n<p><span data-contrast=\"auto\">The root of all these problems is unnecessary initialization. Do we really need to initialize the stack area\u00a0every time?\u00a0No.\u00a0One can safely prove within the compiler when\u00a0stack initialization\u00a0is really needed. For example,\u00a0you need\u00a0it when there is at least one address-taken variable, an array\u00a0declared\u00a0in your function\u00a0or uninitialized variables.\u00a0For\u00a0every\u00a0other case, we can safely skip over the initialization,\u00a0as we are not going to find anything useful through runtime checks\u00a0anyway.<\/span><span data-ccp-props=\"{&quot;201341983&quot;:0,&quot;335559739&quot;:160,&quot;335559740&quot;:259}\">\u00a0<\/span><\/p>\n<p><span data-contrast=\"auto\">The situation gets\u00a0a bit\u00a0more complicated when you compile with edit-and-continue because now you may add uninitialized\u00a0variables\u00a0in the debugging session that can only be detected if we initialize\u00a0the stack area. And we may\u00a0not\u00a0have\u00a0done that.\u00a0To solve this problem, we\u00a0included\u00a0the necessary\u00a0bits\u00a0in\u00a0debugging info\u00a0and exposed it via <a href=\"https:\/\/docs.microsoft.com\/en-us\/visualstudio\/debugger\/debug-interface-access\/debug-interface-access-sdk?view=vs-2019\">Debug Interface Access SDK<\/a>.\u00a0This information tells the\u00a0debugger where the padding area introduced by \/ZI\u00a0<\/span><a href=\"https:\/\/docs.microsoft.com\/en-us\/visualstudio\/debugger\/debug-interface-access\/idiasymbol-get-framepadoffset?view=vs-2019\"><span data-contrast=\"none\">starts<\/span><\/a><span data-contrast=\"auto\">\u00a0and\u00a0<\/span><a href=\"https:\/\/docs.microsoft.com\/en-us\/visualstudio\/debugger\/debug-interface-access\/idiasymbol-get-framepadsize?view=vs-2019\"><span data-contrast=\"none\">ends<\/span><\/a><span data-contrast=\"auto\">. It also tells\u00a0the\u00a0debugger if the\u00a0function\u00a0<\/span><a href=\"https:\/\/docs.microsoft.com\/en-us\/visualstudio\/debugger\/debug-interface-access\/idiasymbol-get-isrtcs?view=vs-2019\"><span data-contrast=\"none\">needed any stack initialization<\/span><\/a><span data-contrast=\"auto\">.\u00a0If so, the\u00a0debugger then unconditionally initializes the stack area in this\u00a0memory\u00a0range for the functions that you have edited during your debugging session. The new variables are always allocated on top of this initialized area and our runtime\u00a0checks\u00a0can now detect if your newly added code is safe or not.<\/span><span data-ccp-props=\"{&quot;201341983&quot;:0,&quot;335559739&quot;:160,&quot;335559740&quot;:259}\">\u00a0<\/span><\/p>\n<h3><b><span data-contrast=\"auto\">Results<\/span><\/b><span data-ccp-props=\"{&quot;201341983&quot;:0,&quot;335559739&quot;:160,&quot;335559740&quot;:259}\">\u00a0<\/span><\/h3>\n<p><span data-contrast=\"auto\">We compiled\u00a0following\u00a0projects in default debug configuration and then used the\u00a0generated\u00a0executables to run tests. We noticed 2x \u2013 3x improvements in all the projects we tried. More STL-heavy projects may see larger improvements. Let us know in\u00a0the\u00a0comments\u00a0any\u00a0improvements you noticed in your projects.\u00a0Project 1\u00a0and\u00a0<\/span><a href=\"https:\/\/zeux.io\/2019\/01\/17\/is-c-fast\/\"><span data-contrast=\"none\">Project\u00a02<\/span><\/a><span data-contrast=\"auto\">\u00a0are customer provided samples.<\/span><span data-ccp-props=\"{&quot;201341983&quot;:0,&quot;335559739&quot;:160,&quot;335559740&quot;:259}\">\u00a0<\/span><\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/cppblog\/wp-content\/uploads\/sites\/9\/2021\/05\/results-5.png\"><img decoding=\"async\" class=\"size-full wp-image-28007 aligncenter\" src=\"https:\/\/devblogs.microsoft.com\/cppblog\/wp-content\/uploads\/sites\/9\/2021\/05\/results-5.png\" alt=\"Image results\" width=\"1024\" height=\"570\" srcset=\"https:\/\/devblogs.microsoft.com\/cppblog\/wp-content\/uploads\/sites\/9\/2021\/05\/results-5.png 1024w, https:\/\/devblogs.microsoft.com\/cppblog\/wp-content\/uploads\/sites\/9\/2021\/05\/results-5-300x167.png 300w, https:\/\/devblogs.microsoft.com\/cppblog\/wp-content\/uploads\/sites\/9\/2021\/05\/results-5-768x428.png 768w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/a><\/p>\n<p>&nbsp;<\/p>\n<h4><b><span data-contrast=\"auto\">Tell us what you think!<\/span><\/b><span data-ccp-props=\"{&quot;201341983&quot;:0,&quot;335559739&quot;:160,&quot;335559740&quot;:259}\">\u00a0<\/span><\/h4>\n<p><span data-contrast=\"auto\">We\u00a0hope this speedup\u00a0makes your debugging workflow efficient\u00a0and enjoyable.\u00a0We are\u00a0continuously\u00a0listening to your feedback\u00a0and working towards\u00a0improving your inner loop\u00a0experience.\u00a0We\u2019d love to hear about your experience in the comments below.\u00a0You can also get in touch with us\u00a0at\u00a0<\/span><a href=\"https:\/\/developercommunity.visualstudio.com\/spaces\/8\/index.html\"><span data-contrast=\"none\">Developer Community<\/span><\/a><span data-contrast=\"none\">, email (<\/span><a href=\"mailto:visualcpp@microsoft.com\"><span data-contrast=\"none\">visualcpp@microsoft.com<\/span><\/a><span data-contrast=\"none\">), and Twitter (<\/span><a href=\"https:\/\/twitter.com\/visualc\"><span data-contrast=\"none\">@VisualC<\/span><\/a><span data-contrast=\"none\">).<\/span><span data-ccp-props=\"{&quot;201341983&quot;:0,&quot;335559739&quot;:160,&quot;335559740&quot;:259}\">\u00a0<\/span><\/p>\n","protected":false},"excerpt":{"rendered":"<p>We have made substantial runtime performance improvements in the x86\/x64 C++ compiler for Visual Studio&#8217;s default debug configuration. For\u00a0Visual Studio 2019\u00a0version 16.10 Preview 2, we measure\u00a02x &#8211;\u00a03x speedup for\u00a0programs compiled\u00a0in\u00a0debug\u00a0mode.\u00a0These\u00a0improvements\u00a0come from\u00a0reducing the\u00a0overhead\u00a0introduced by runtime checks (\/RTCs)\u00a0which are\u00a0enabled\u00a0by\u00a0default.\u00a0 Default debug configuration\u00a0 When you compile your code in debug configuration in Visual\u00a0Studio, there are some flags that [&hellip;]<\/p>\n","protected":false},"author":31987,"featured_media":19546,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1,217,218],"tags":[3888,66],"class_list":["post-27969","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-cplusplus","category-faster","category-performance","tag-debug-builds","tag-performance"],"acf":[],"blog_post_summary":"<p>We have made substantial runtime performance improvements in the x86\/x64 C++ compiler for Visual Studio&#8217;s default debug configuration. For\u00a0Visual Studio 2019\u00a0version 16.10 Preview 2, we measure\u00a02x &#8211;\u00a03x speedup for\u00a0programs compiled\u00a0in\u00a0debug\u00a0mode.\u00a0These\u00a0improvements\u00a0come from\u00a0reducing the\u00a0overhead\u00a0introduced by runtime checks (\/RTCs)\u00a0which are\u00a0enabled\u00a0by\u00a0default.\u00a0 Default debug configuration\u00a0 When you compile your code in debug configuration in Visual\u00a0Studio, there are some flags that [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/posts\/27969","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/users\/31987"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/comments?post=27969"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/posts\/27969\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/media\/19546"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/media?parent=27969"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/categories?post=27969"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/tags?post=27969"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}