{"id":25560,"date":"2020-02-28T15:42:42","date_gmt":"2020-02-28T15:42:42","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/cppblog\/?p=25560"},"modified":"2020-02-28T15:44:12","modified_gmt":"2020-02-28T15:44:12","slug":"avx2-floating-point-improvements-in-visual-studio-2019-version-16-5","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/cppblog\/avx2-floating-point-improvements-in-visual-studio-2019-version-16-5\/","title":{"rendered":"AVX2 floating point improvements in Visual Studio 2019 version 16.5"},"content":{"rendered":"<p>In Visual Studio 2019 We&#8217;ve been working hard on optimizing floating point operations with AVX2 instructions. This post will outline work done so far and recent improvements made in version 16.5.<\/p>\n<p><span data-contrast=\"auto\">T<\/span><span data-contrast=\"auto\">he speed of\u00a0<\/span><span data-contrast=\"auto\">floating point<\/span><span data-contrast=\"auto\">\u00a0operations directly impacts the frame rate of video games.\u00a0<\/span><span data-contrast=\"auto\">Newer x86 and x64 chips have added special vector<\/span><span data-contrast=\"auto\">\u00a0<\/span><a href=\"https:\/\/software.intel.com\/en-us\/cpp-compiler-developer-guide-and-reference-intrinsics-for-fused-multiply-add-operations\"><span data-contrast=\"auto\">Fused Multiply Add<\/span><\/a><span data-contrast=\"auto\">\u00a0instructions to<\/span><span data-contrast=\"auto\">\u00a0<\/span><span data-contrast=\"auto\">improve\u00a0<\/span><span data-contrast=\"auto\">and\u00a0<\/span><span data-contrast=\"auto\">parallelize<\/span><span data-contrast=\"auto\">\u00a0<\/span><span data-contrast=\"auto\">the performance of\u00a0<\/span><span data-contrast=\"auto\">floating point<\/span><span data-contrast=\"auto\">\u00a0operations<\/span><span data-contrast=\"auto\">. Starting with Visual\u00a0<\/span><span data-contrast=\"auto\">Studio\u00a0<\/span><span data-contrast=\"auto\">2019,<\/span><span data-contrast=\"auto\">\u00a0<\/span><span data-contrast=\"auto\">the<\/span><span data-contrast=\"auto\">\u00a0compiler<\/span><span data-contrast=\"auto\">\u00a0<\/span><span data-contrast=\"auto\">will\u00a0<\/span><span data-contrast=\"auto\">aggressively<\/span><span data-contrast=\"auto\">\u00a0identif<\/span><span data-contrast=\"auto\">y<\/span><span data-contrast=\"auto\">\u00a0opportunities to use the new\u00a0<\/span><span data-contrast=\"auto\">floating point<\/span><span data-contrast=\"auto\">\u00a0instructions and\u00a0<\/span><span data-contrast=\"auto\">perform constant propagation for such instructions<\/span><span data-contrast=\"auto\">\u00a0when the \/<\/span><span data-contrast=\"auto\">fp:fast<\/span><span data-contrast=\"auto\">\u00a0flag is passed<\/span><span data-contrast=\"auto\">.<\/span><span data-ccp-props=\"{&quot;201341983&quot;:0,&quot;335551550&quot;:1,&quot;335551620&quot;:1,&quot;335559739&quot;:160,&quot;335559740&quot;:259}\">\u00a0<\/span><\/p>\n<p><span data-contrast=\"auto\">With Visual Studio 2019 version 16.2,\u00a0<\/span><span data-contrast=\"auto\">the heuristics for vectorizing\u00a0<\/span><span data-contrast=\"auto\">floating point operations improved<\/span><span data-contrast=\"auto\">\u00a0and some\u00a0<\/span><span data-contrast=\"auto\">floating point<\/span><span data-contrast=\"auto\">\u00a0operations could be\u00a0<\/span><span data-contrast=\"auto\">reduced down<\/span><span data-contrast=\"auto\">\u00a0to a constant.\u00a0<\/span><span data-contrast=\"auto\">Natalia<\/span><span data-contrast=\"auto\">\u00a0<\/span><span data-contrast=\"auto\">Glagoleva\u00a0<\/span><span data-contrast=\"auto\">described<\/span><span data-contrast=\"auto\">\u00a0these and<\/span><span data-contrast=\"auto\">\u00a0<\/span><span data-contrast=\"auto\">a number of<\/span><a href=\"https:\/\/devblogs.microsoft.com\/cppblog\/game-performance-improvements-in-visual-studio-2019-version-16-2\/\"><span data-contrast=\"auto\"> gam<\/span><span data-contrast=\"auto\">e performance\u00a0<\/span><span data-contrast=\"auto\">improvements<\/span><\/a><span data-contrast=\"auto\">\u00a0last summer<\/span><span data-contrast=\"auto\">.<\/span><span data-ccp-props=\"{&quot;201341983&quot;:0,&quot;335551550&quot;:1,&quot;335551620&quot;:1,&quot;335559739&quot;:160,&quot;335559740&quot;:259}\">\u00a0<\/span><\/p>\n<p><span data-contrast=\"auto\">With Visual Studio 2019\u00a0<\/span><span data-contrast=\"auto\">vers<\/span><span data-contrast=\"auto\">i<\/span><span data-contrast=\"auto\">on 16.5<\/span><span data-contrast=\"auto\">, we\u00a0<\/span><span data-contrast=\"auto\">improved the SSA optimizer to recognize more opportunities to\u00a0<\/span><span data-contrast=\"auto\">use\u00a0<\/span><span data-contrast=\"auto\">AVX<\/span><span data-contrast=\"auto\">2 instructions and improved constant propagation for\u00a0<\/span><span data-contrast=\"auto\">vector<\/span><span data-contrast=\"auto\">\u00a0operations in<\/span><span data-contrast=\"auto\">volving shuffle<\/span><span data-contrast=\"auto\">.<\/span><span data-ccp-props=\"{&quot;201341983&quot;:0,&quot;335551550&quot;:1,&quot;335551620&quot;:1,&quot;335559739&quot;:160,&quot;335559740&quot;:259}\">\u00a0<\/span><\/p>\n<p><span data-contrast=\"auto\">All of\u00a0the following samples are compiled for x64 with these switches: \/arch:AVX2 \/O2 \/<\/span><span data-contrast=\"auto\">fp:fast<\/span><span data-contrast=\"auto\">\u00a0\/c \/Fa<\/span><span data-ccp-props=\"{&quot;201341983&quot;:0,&quot;335551550&quot;:1,&quot;335551620&quot;:1,&quot;335559739&quot;:160,&quot;335559740&quot;:259}\">\u00a0<\/span><\/p>\n<h3 aria-level=\"2\"><span data-contrast=\"none\">Constant Propagation<\/span><span data-contrast=\"none\">\u00a0for\u00a0<\/span><span data-contrast=\"none\">Multiply<\/span><span data-ccp-props=\"{&quot;201341983&quot;:0,&quot;335559738&quot;:40,&quot;335559739&quot;:0,&quot;335559740&quot;:259}\">\u00a0<\/span><\/h3>\n<p><span data-contrast=\"auto\">Starting with Visual Studio\u00a0<\/span><span data-contrast=\"auto\">2019<\/span><span data-contrast=\"auto\">\u00a0version 16.2<\/span><span data-contrast=\"auto\">,<\/span><span data-contrast=\"auto\">\u00a0some<\/span><span data-contrast=\"auto\">\u00a0<\/span><span data-contrast=\"auto\">floating point<\/span><span data-contrast=\"auto\">\u00a0vector operations could be reduced to a constant<\/span><span data-contrast=\"auto\">\u00a0if the initial vectors were\u00a0<\/span><span data-contrast=\"auto\">known<\/span><span data-contrast=\"auto\">\u00a0at compile time<\/span><span data-contrast=\"auto\">.<\/span><span data-contrast=\"auto\">\u00a0A good example is the inverse square root function.<\/span><span data-ccp-props=\"{&quot;201341983&quot;:0,&quot;335551550&quot;:1,&quot;335551620&quot;:1,&quot;335559739&quot;:160,&quot;335559740&quot;:259}\">\u00a0<\/span><\/p>\n<pre class=\"lang:c++ decode:true\">#include \r\n#include \r\nfloat\u00a0InvSqrt(float F)\r\n{\r\n\u00a0\u00a0\u00a0 const __m128\u00a0fOneHalf\u00a0= _mm_set_ss(0.5f);\r\n\u00a0\u00a0\u00a0 __m128 Y0, X0, X1, X2,\u00a0FOver2;\r\n\u00a0\u00a0\u00a0 float\u00a0temp;\r\n\u00a0\u00a0\u00a0 Y0 = _mm_set_ss(F);\r\n\u00a0\u00a0\u00a0 X0 = _mm_rsqrt_ss(Y0);\r\n\u00a0\u00a0\u00a0 FOver2 = _mm_mul_ss(Y0,\u00a0fOneHalf);\r\n\u00a0\u00a0\u00a0 X1 = _mm_mul_ss(X0, X0);\r\n\u00a0\u00a0\u00a0 X1 = _mm_sub_ss(fOneHalf, _mm_mul_ss(FOver2, X1));\r\n\u00a0\u00a0\u00a0 X1 = _mm_add_ss(X0, _mm_mul_ss(X0, X1));\r\n\u00a0\u00a0\u00a0 X2 = _mm_mul_ss(X1, X1);\r\n\u00a0\u00a0\u00a0 X2 = _mm_sub_ss(fOneHalf, _mm_mul_ss(FOver2, X2));\r\n\u00a0\u00a0\u00a0 X2 = _mm_add_ss(X1, _mm_mul_ss(X1, X2));\r\n\u00a0\u00a0\u00a0 _mm_store_ss(&amp;temp, X2);\r\n\u00a0\u00a0\u00a0\u00a0return\u00a0temp;\r\n}\u00a0\r\n\r\nfloat\u00a0ReturnInvSqrt()\r\n{\r\n\u00a0\u00a0\u00a0\u00a0return\u00a0InvSqrt(4.0);\r\n}<\/pre>\n<p><span data-contrast=\"auto\">Starting with\u00a0<\/span><span data-contrast=\"auto\">Visual Studio\u00a0<\/span><span data-contrast=\"auto\">16.2,\u00a0<\/span><span data-contrast=\"auto\">ReturnInvSqrt\u00a0could be reduced to a single constant:<\/span><span data-ccp-props=\"{&quot;201341983&quot;:0,&quot;335551550&quot;:1,&quot;335551620&quot;:1,&quot;335559739&quot;:160,&quot;335559740&quot;:259}\">\u00a0<\/span><\/p>\n<p><span data-ccp-props=\"{&quot;201341983&quot;:0,&quot;335551550&quot;:1,&quot;335551620&quot;:1,&quot;335559739&quot;:160,&quot;335559740&quot;:259}\"><img decoding=\"async\" class=\"alignnone wp-image-25563 size-full\" src=\"https:\/\/devblogs.microsoft.com\/cppblog\/wp-content\/uploads\/sites\/9\/2020\/02\/ReturnInvSquareDiff.png\" alt=\"17 instructions reduced to a single move instruction\" width=\"1099\" height=\"325\" srcset=\"https:\/\/devblogs.microsoft.com\/cppblog\/wp-content\/uploads\/sites\/9\/2020\/02\/ReturnInvSquareDiff.png 1099w, https:\/\/devblogs.microsoft.com\/cppblog\/wp-content\/uploads\/sites\/9\/2020\/02\/ReturnInvSquareDiff-300x89.png 300w, https:\/\/devblogs.microsoft.com\/cppblog\/wp-content\/uploads\/sites\/9\/2020\/02\/ReturnInvSquareDiff-1024x303.png 1024w, https:\/\/devblogs.microsoft.com\/cppblog\/wp-content\/uploads\/sites\/9\/2020\/02\/ReturnInvSquareDiff-768x227.png 768w\" sizes=\"(max-width: 1099px) 100vw, 1099px\" \/><\/span><\/p>\n<h3 aria-level=\"2\"><span data-contrast=\"none\">Constant Propagation for Shuffle<\/span><span data-ccp-props=\"{&quot;201341983&quot;:0,&quot;335559738&quot;:40,&quot;335559739&quot;:0,&quot;335559740&quot;:259}\">\u00a0<\/span><\/h3>\n<p><span data-contrast=\"auto\">Another common vector operation is to\u00a0<\/span><span data-contrast=\"auto\">create a normalized form of the vector, so that it has a\u00a0<\/span><span data-contrast=\"auto\">length<\/span><span data-contrast=\"auto\">\u00a0of\u00a0<\/span><span data-contrast=\"auto\">one.<\/span><span data-contrast=\"auto\">\u00a0T<\/span><span data-contrast=\"auto\">he length of a vector is the square root of its dot product.\u00a0<\/span><span data-contrast=\"auto\">The easiest way to\u00a0<\/span><span data-contrast=\"auto\">calculate the<\/span><span data-contrast=\"auto\">\u00a0dot product involves\u00a0<\/span><span data-contrast=\"auto\">a shuffle operation.<\/span><span data-ccp-props=\"{&quot;201341983&quot;:0,&quot;335551550&quot;:1,&quot;335551620&quot;:1,&quot;335559739&quot;:160,&quot;335559740&quot;:259}\">\u00a0<\/span><\/p>\n<pre class=\"lang:c++ decode:true\">__m128\u00a0 VectorDot4(const __m128 Vec1, const __m128 Vec2)\r\n{\r\n\u00a0\u00a0\u00a0\u00a0__m128 Temp1,\u00a0Temp2;\r\n\u00a0\u00a0\u00a0\u00a0Temp1 = _mm_mul_ps(Vec1, Vec2);\r\n\u00a0\u00a0\u00a0\u00a0Temp2 = _mm_shuffle_ps(Temp1, Temp1,\u00a00x4E);\r\n\u00a0\u00a0\u00a0\u00a0Temp1 = _mm_add_ps(Temp1, Temp2);\r\n\u00a0\u00a0\u00a0\u00a0Temp2 = _mm_shuffle_ps(Temp1, Temp1,\u00a00x39);\r\n\u00a0\u00a0\u00a0\u00a0return _mm_add_ps(Temp1, Temp2); \r\n}\u00a0\r\n\r\n__m128\u00a0\u00a0VectorNormalize_InvSqrt(const __m128 V)\r\n{\r\n\u00a0\u00a0\u00a0\u00a0const __m128 Len = VectorDot4(V, V);\r\n\u00a0\u00a0\u00a0\u00a0const float\u00a0LenComponent\u00a0= ((float*) &amp;Len)[0];\r\n\u00a0\u00a0\u00a0\u00a0const float\u00a0rlen\u00a0=\u00a0InvSqrt(LenComponent);\r\n\u00a0\u00a0\u00a0\u00a0return _mm_mul_ps(V, _mm_load1_ps(&amp;rlen));\r\n}<\/pre>\n<p><span data-contrast=\"auto\">Even in Visual Studio version 16.0<\/span><span data-contrast=\"auto\">\u00a0the optimizer could propagate constants through shuffle operations.<\/span><span data-contrast=\"auto\">\u00a0However,\u00a0<\/span><span data-contrast=\"auto\">due to some ordering issues with\u00a0<\/span><span data-contrast=\"auto\">the original implementation of fused multiply add constant\u00a0<\/span><span data-contrast=\"auto\">propa<\/span><span data-contrast=\"auto\">gation<\/span><span data-contrast=\"auto\">, constant propagation for\u00a0<\/span><span data-contrast=\"auto\">shuffle<\/span><span data-contrast=\"auto\">\u00a0prevented constant propagation for fused multiply add<\/span><span data-contrast=\"auto\">.<\/span><\/p>\n<p><span data-contrast=\"auto\">Starting with Visual Studio 16.5, constant propagation can handle cases that involve both\u00a0<\/span><span data-contrast=\"auto\">shuffle<\/span><span data-contrast=\"auto\">\u00a0and fused multiply add.\u00a0<\/span><span data-contrast=\"auto\">This means normalizing the inverse square root of a vector<\/span><span data-contrast=\"auto\">\u00a0known at compile time<\/span><span data-contrast=\"auto\">\u00a0can be completely\u00a0<\/span><span data-contrast=\"auto\">reduced down<\/span><span data-contrast=\"auto\">\u00a0to a constant<\/span><span data-contrast=\"auto\">\u00a0if the input is known at compile time<\/span><span data-contrast=\"auto\">.<\/span><span data-ccp-props=\"{&quot;201341983&quot;:0,&quot;335551550&quot;:1,&quot;335551620&quot;:1,&quot;335559739&quot;:160,&quot;335559740&quot;:240}\">\u00a0<\/span><\/p>\n<pre class=\"lang:c++ decode:true\">__m128\u00a0ReturnVectorNormalize_InvSqrt() {\r\n\u00a0\u00a0\u00a0\u00a0__m128 V0 = _mm_setr_ps(2.0f, -2.0f, 2.0f, -2.0f);\r\n\u00a0\u00a0\u00a0\u00a0return\u00a0VectorNormalize_InvSqrt(V0);\r\n}<\/pre>\n<p><span data-ccp-props=\"{&quot;201341983&quot;:0,&quot;335551550&quot;:1,&quot;335551620&quot;:1,&quot;335559739&quot;:160,&quot;335559740&quot;:240}\">\u00a0<img decoding=\"async\" class=\"alignnone wp-image-25564 size-full\" src=\"https:\/\/devblogs.microsoft.com\/cppblog\/wp-content\/uploads\/sites\/9\/2020\/02\/ReturnVectorNormalizeInvSqrtDiff.png\" alt=\"24 vector instructions reduced to a single move instruction.\" width=\"1426\" height=\"565\" srcset=\"https:\/\/devblogs.microsoft.com\/cppblog\/wp-content\/uploads\/sites\/9\/2020\/02\/ReturnVectorNormalizeInvSqrtDiff.png 1426w, https:\/\/devblogs.microsoft.com\/cppblog\/wp-content\/uploads\/sites\/9\/2020\/02\/ReturnVectorNormalizeInvSqrtDiff-300x119.png 300w, https:\/\/devblogs.microsoft.com\/cppblog\/wp-content\/uploads\/sites\/9\/2020\/02\/ReturnVectorNormalizeInvSqrtDiff-1024x406.png 1024w, https:\/\/devblogs.microsoft.com\/cppblog\/wp-content\/uploads\/sites\/9\/2020\/02\/ReturnVectorNormalizeInvSqrtDiff-768x304.png 768w\" sizes=\"(max-width: 1426px) 100vw, 1426px\" \/><\/span><\/p>\n<p><span data-contrast=\"auto\">We\u2019d love for you to download the <a href=\"https:\/\/visualstudio.microsoft.com\/vs\/\">latest version of Visual Studio 2019<\/a><\/span><span data-contrast=\"auto\">\u00a0and give these new improvements a try. As always, we welcome your feedback. We can be reached via the comments below or via email (visualcpp@microsoft.com). If you encounter problems with Visual Studio or MSVC, or have a suggestion for us, please let us know through Help &gt; Send Feedback &gt; Report A Problem \/ Provide a Suggestion in the product, or via <a href=\"https:\/\/developercommunity.visualstudio.com\/\">Developer Community<\/a>. You can also find us on Twitter (<a href=\"https:\/\/twitter.com\/visualc\">@VisualC<\/a>).<\/span><span data-ccp-props=\"{&quot;201341983&quot;:0,&quot;335551550&quot;:1,&quot;335551620&quot;:1,&quot;335559739&quot;:160,&quot;335559740&quot;:240}\">\u00a0<\/span><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Visual Studio version 16.2 provided a number of improvements to C++ video game development through vector optimizations. Visual Studio version 16.5 builds on these changes to improve the performance of even more vector operations.<\/p>\n","protected":false},"author":18811,"featured_media":35994,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[],"class_list":["post-25560","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-cplusplus"],"acf":[],"blog_post_summary":"<p>Visual Studio version 16.2 provided a number of improvements to C++ video game development through vector optimizations. Visual Studio version 16.5 builds on these changes to improve the performance of even more vector operations.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/posts\/25560","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\/18811"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/comments?post=25560"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/posts\/25560\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/media\/35994"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/media?parent=25560"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/categories?post=25560"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/tags?post=25560"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}