{"id":7943,"date":"2012-04-03T07:00:00","date_gmt":"2012-04-03T07:00:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/2012\/04\/03\/introducing-the-unrolled-switch-anti-pattern\/"},"modified":"2012-04-03T07:00:00","modified_gmt":"2012-04-03T07:00:00","slug":"introducing-the-unrolled-switch-anti-pattern","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20120403-00\/?p=7943\/","title":{"rendered":"Introducing the unrolled-switch anti-pattern"},"content":{"rendered":"<p>\nOver the years, I&#8217;ve seen a bunch of coding anti-patterns.\nI figured maybe I&#8217;ll share a few.\n<\/p>\n<p>\nToday, I&#8217;ll introduce\nwhat I&#8217;m calling the <i>unrolled-switch<\/i> anti-pattern,\nalso known as\n&#8220;<a HREF=\"http:\/\/blogs.msdn.com\/b\/oldnewthing\/archive\/2010\/04\/27\/10002865.aspx\">Specialization is always faster, right?<\/a>&#8221;\n<\/p>\n<pre>\nenum Axis\n{\n    XAxis,\n    YAxis,\n    ZAxis,\n};\n\/\/ code earlier in the function ensure that\n\/\/ \"axis\" is always a valid axis\nint newPosition;\nswitch (axis)\n{\ncase XAxis:\n    newPosition = m_position[XAxis] + amount;\n    if (newPosition &lt; m_minPosition[XAxis])\n        newPosition = m_minPosition[XAxis];\n    if (newPosition &gt; m_maxPosition[XAxis])\n        newPosition = m_maxPosition[XAxis];\n    m_position[XAxis] = amount;\n    break;\ncase YAxis:\n    newPosition = m_position[YAxis] + amount;\n    if (newPosition &lt; m_minPosition[YAxis])\n        newPosition = m_minPosition[YAxis];\n    if (newPosition &gt; m_maxPosition[YAxis])\n        newPosition = m_maxPosition[YAxis];\n    m_position[YAxis] = amount;\n    break;\ncase ZAxis:\n    newPosition = m_position[ZAxis] + amount;\n    if (newPosition &lt; m_minPosition[ZAxis])\n        newPosition = m_minPosition[ZAxis];\n    if (newPosition &gt; m_maxPosition[XAxis])\n        newPosition = m_maxPosition[XAxis];\n    m_position[ZAxis] = amount;\n    break;\n}\n<\/pre>\n<p>As we all know,\nspecial-case code is faster than general-purpose code.\nInstead of writing slow general-purpose code:\n<\/p>\n<pre>\n    newPosition = m_position[axis] + amount;\n    if (newPosition &lt; m_minPosition[axis])\n        newPosition = m_minPosition[axis];\n    if (newPosition &gt; m_maxPosition[axis])\n        newPosition = m_maxPosition[axis];\n    m_position[axis] = amount;\n<\/pre>\n<p>\nwe unroll it into a switch statement, thereby generating\nhighly-optimized\nspecial-purpose code, one for each axis.\n<\/p>\n<p>\nWhat makes this anti-pattern particularly frustrating is that you\ncannot tell at a glance whether all the cases really are the same\n(just with different axes).\n<\/p>\n<p>\nIn fact, they aren&#8217;t.\n<\/p>\n<p>\nIf you look closely, you&#8217;ll see that we check the\nnew Z-position against the X-axis maximum rather than the Z-axis\nmaximum.\nIf you&#8217;re reading this code, you now start to wonder,\n&#8220;Is this a copy\/paste bug, or is there some reason that\nwe really do want to check the Z-position against the X-axis minimum?&#8221;\n<\/p>\n<p>\nA variation on the <i>unrolled-switch<\/i> is the\n<i>unrolled-if<\/i>, used if the item you want to unroll\ncannot be used in a <i>switch<\/i> statement:\n<\/p>\n<pre>\nFruitBasket *BananaBasket;\nFruitBasket *AppleBasket;\nFruitBasket *PearBasket;\nFruitBasket *MangoBasket;\nif (basket == BananaBasket) {\n  if (!BananaBasket-&gt;IsEmpty()) {\n    fruit = BananaBasket-&gt;TakeFruit();\n    if (HaveKnife()) {\n      TakeKnife();\n      fruit-&gt;Peel();\n      fruit-&gt;Slice();\n      fruit-&gt;Eat();\n      ReplaceKnife();\n    } else {\n      BananaBasket-&gt;AddFruit(fruit);\n    }\n  }\n} else if (basket == AppleBasket) {\n  if (!AppleBasket-&gt;IsEmpty()) {\n    fruit = AppleBasket-&gt;TakeFruit();\n    if (HaveKnife()) {\n      TakeKnife();\n      fruit-&gt;Peel();\n      fruit-&gt;Slice();\n      fruit-&gt;Eat();\n      ReplaceKnife();\n    } else {\n      AppleBasket-&gt;AddFruit(fruit);\n    }\n  }\n} else if (basket == PearBasket) {\n  if (!PearBasket-&gt;IsEmpty()) {\n    fruit = PearBasket-&gt;TakeFruit();\n    if (HaveKnife()) {\n      TakeKnife();\n      fruit-&gt;Slice();\n      fruit-&gt;Eat();\n      ReplaceKnife();\n    } else {\n      PearBasket-&gt;AddFruit(fruit);\n    }\n  }\n} else if (basket == MangoBasket) {\n  if (!MangoBasket-&gt;IsEmpty()) {\n    fruit = MangoBasket-&gt;TakeFruit();\n    if (HaveKnife()) {\n      TakeKnife();\n      fruit-&gt;Peel();\n      fruit-&gt;Slice();\n      fruit-&gt;Eat();\n      ReplaceKnife();\n    } else {\n      BananaBasket-&gt;AddFruit(fruit);\n    }\n  }\n}\n<\/pre>\n<p>\nWhen I pointed out in an aside to the customer that this could\nbe simplified (after fixing the copy\/paste errors) to\n<\/p>\n<pre>\nif (!basket-&gt;IsEmpty()) {\n  fruit = basket-&gt;TakeFruit();\n  if (HaveKnife()) {\n    TakeKnife();\n    fruit-&gt;Peel();\n    fruit-&gt;Slice();\n    fruit-&gt;Eat();\n    ReplaceKnife();\n  } else {\n    basket-&gt;AddFruit(fruit);\n  }\n}\n<\/pre>\n<p>\nthe response was, &#8220;Hey, that&#8217;s a neat trick.\nI didn&#8217;t realize you could do that.&#8221;\n<\/p>\n<p>\nI wonder if this person also programs loops like this:\n<\/p>\n<pre>\nswitch (limit)\n{\ncase 0:\n  break;\ncase 1:\n  do_something(array[0]);\n  break;\ncase 2:\n  for (int i = 0; i &lt; 2; i++) do_something(array[i]);\n  break;\ncase 3:\n  for (int i = 0; i &lt; 3; i++) do_something(array[i]);\n  break;\ncase 4:\n  for (int i = 0; i &lt; 4; i++) do_something(array[i]);\n  break;\ncase 5:\n  for (int i = 0; i &lt; 5; i++) do_something(array[i]);\n  break;\ncase 6:\n  for (int i = 0; i &lt; 6; i++) do_something(array[i]);\n  break;\n...\ncase 999:\n  for (int i = 0; i &lt; 999; i++) do_something(array[i]);\n  break;\ndefault:\n  FatalError(\"Need more cases to handle larger array\");\n  break;\n}\n<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>Over the years, I&#8217;ve seen a bunch of coding anti-patterns. I figured maybe I&#8217;ll share a few. Today, I&#8217;ll introduce what I&#8217;m calling the unrolled-switch anti-pattern, also known as &#8220;Specialization is always faster, right?&#8221; enum Axis { XAxis, YAxis, ZAxis, }; \/\/ code earlier in the function ensure that \/\/ &#8220;axis&#8221; is always a valid [&hellip;]<\/p>\n","protected":false},"author":1069,"featured_media":111744,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[25],"class_list":["post-7943","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Over the years, I&#8217;ve seen a bunch of coding anti-patterns. I figured maybe I&#8217;ll share a few. Today, I&#8217;ll introduce what I&#8217;m calling the unrolled-switch anti-pattern, also known as &#8220;Specialization is always faster, right?&#8221; enum Axis { XAxis, YAxis, ZAxis, }; \/\/ code earlier in the function ensure that \/\/ &#8220;axis&#8221; is always a valid [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/7943","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/users\/1069"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/comments?post=7943"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/7943\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media\/111744"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media?parent=7943"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=7943"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=7943"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}