{"id":112378,"date":"2026-06-03T07:00:00","date_gmt":"2026-06-03T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=112378"},"modified":"2026-06-03T21:04:31","modified_gmt":"2026-06-04T04:04:31","slug":"20260603-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20260603-00\/?p=112378","title":{"rendered":"Rotation revisited: A shocking discovery about gcc&#8217;s unidirectional rotation algorithm"},"content":{"rendered":"<p>Last time, we looked at <a title=\"Rotation revisited: Another unidirectional algorithm\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20260602-00\/?p=112376\"> the rotation algorithm used by gcc libstdc++ for random-access iterators<\/a>, and I concluded by noting that we&#8217;re going to make a shocking discovery.<\/p>\n<p>As with all shocking discoveries, this one will <span style=\"text-decoration: line-through;\">shock<\/span> <span style=\"border: solid 1px currentcolor;\">disappoint<\/span> you.<\/p>\n<p>The discovery is that the gcc libstdc++ algorithm is the same as <a title=\"How can you swap two non-adjacent blocks of memory using only forward iterators?\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20260105-00\/?p=111962\"> the forward-iterator algorithm<\/a>!<\/p>\n<p>Let&#8217;s run both algorithms on a problem where the two blocks are A1, A2, A3, B1, B2, B3, B4, B5. I&#8217;ll put the old forward iterator algorithm on top and the new gcc libstdc++ algorithm below.<\/p>\n<table style=\"border-collapse: collapse; text-align: center;\" title=\"A1, A2, A3, B1, B2, B3, B4, B5\" border=\"0\" cellspacing=\"0\" cellpadding=\"1\">\n<tbody>\n<tr>\n<td colspan=\"2\" align=\"left\">first<\/td>\n<td>&nbsp;<\/td>\n<td colspan=\"2\" align=\"left\">mid<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td align=\"left\">last<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">\u2193<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td align=\"left\">\u2193<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td align=\"left\">\u2193<\/td>\n<\/tr>\n<tr>\n<td style=\"width: 2em; border: solid 1px currentcolor; background: lch(from currentcolor l c h \/ .1);\">A1<\/td>\n<td style=\"width: 2em; border: solid 1px currentcolor; background: lch(from currentcolor l c h \/ .1);\">A2<\/td>\n<td style=\"width: 2em; border: solid 1px currentcolor; background: lch(from currentcolor l c h \/ .1);\">A3<\/td>\n<td style=\"width: 2em; border: solid 1px currentcolor;\">B1<\/td>\n<td style=\"width: 2em; border: solid 1px currentcolor;\">B2<\/td>\n<td style=\"width: 2em; border: solid 1px currentcolor;\">B3<\/td>\n<td style=\"width: 2em; border: solid 1px currentcolor;\">B4<\/td>\n<td style=\"width: 2em; border: solid 1px currentcolor;\">B5<\/td>\n<\/tr>\n<tr>\n<td align=\"left\">\u2191<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td align=\"left\">\u2191<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td align=\"left\">\u2191<\/td>\n<\/tr>\n<tr>\n<td colspan=\"2\" align=\"left\">first<\/td>\n<td>&nbsp;<\/td>\n<td colspan=\"2\" align=\"left\">mid<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td align=\"left\">last<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>We swap at <code>first<\/code> and <code>mid<\/code>, then advance both pointers. The two algorithms agree until <code>first<\/code> reaches the end of the original A block.<\/p>\n<table style=\"border-collapse: collapse; text-align: center;\" title=\"A1, A2, A3, B1, B2, B3, B4, B5\" border=\"0\" cellspacing=\"0\" cellpadding=\"1\">\n<tbody>\n<tr>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td colspan=\"2\" align=\"left\">first<\/td>\n<td>&nbsp;<\/td>\n<td>mid<\/td>\n<td>&nbsp;<\/td>\n<td>last<\/td>\n<\/tr>\n<tr>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td align=\"left\">\u2193<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td align=\"left\">\u2193<\/td>\n<td>&nbsp;<\/td>\n<td align=\"left\">\u2193<\/td>\n<\/tr>\n<tr>\n<td style=\"width: 2em; border: solid 1px currentcolor;\">B1<\/td>\n<td style=\"width: 2em; border: solid 1px currentcolor;\">B2<\/td>\n<td style=\"width: 2em; border: solid 1px currentcolor;\">B3<\/td>\n<td style=\"width: 2em; border: solid 1px currentcolor; background: lch(from currentcolor l c h \/ .1);\">A1<\/td>\n<td style=\"width: 2em; border: solid 1px currentcolor; background: lch(from currentcolor l c h \/ .1);\">A2<\/td>\n<td style=\"width: 2em; border: solid 1px currentcolor; background: lch(from currentcolor l c h \/ .1);\">A3<\/td>\n<td style=\"width: 2em; border: solid 1px currentcolor;\">B4<\/td>\n<td style=\"width: 2em; border: solid 1px currentcolor;\">B5<\/td>\n<\/tr>\n<tr>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td align=\"left\">\u2191<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td align=\"left\">\u2191<\/td>\n<td>&nbsp;<\/td>\n<td align=\"left\">\u2191<\/td>\n<\/tr>\n<tr>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td colspan=\"2\" align=\"left\">first<\/td>\n<td>&nbsp;<\/td>\n<td>mid<\/td>\n<td>&nbsp;<\/td>\n<td>last<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>The old algorithm recurses in order to exchange A1, A2, A3 with B4, B4. This happens by exchanging A1 with B4 and A2 with B5.<\/p>\n<p>The new algorithm just keeps swapping <code>first<\/code> with <code>mid<\/code>, which also exchanges A1 with B4 and A2 with B5.<\/p>\n<table style=\"border-collapse: collapse; text-align: center;\" title=\"A1, A2, A3, B1, B2, B3, B4, B5\" border=\"0\" cellspacing=\"0\" cellpadding=\"1\">\n<tbody>\n<tr>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td colspan=\"2\" align=\"left\" valign=\"bottom\">first<\/td>\n<td>&nbsp;<\/td>\n<td>mid<br \/>\nlast<\/td>\n<\/tr>\n<tr>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td align=\"left\">\u2193<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td align=\"left\">\u2193<\/td>\n<\/tr>\n<tr>\n<td style=\"width: 2em; border: solid 1px currentcolor;\">B1<\/td>\n<td style=\"width: 2em; border: solid 1px currentcolor;\">B2<\/td>\n<td style=\"width: 2em; border: solid 1px currentcolor;\">B3<\/td>\n<td style=\"width: 2em; border: solid 1px currentcolor;\">B4<\/td>\n<td style=\"width: 2em; border: solid 1px currentcolor;\">B5<\/td>\n<td style=\"width: 2em; border: solid 1px currentcolor; background: lch(from currentcolor l c h \/ .1);\">A3<\/td>\n<td style=\"width: 2em; border: solid 1px currentcolor; background: lch(from currentcolor l c h \/ .1);\">A1<\/td>\n<td style=\"width: 2em; border: solid 1px currentcolor; background: lch(from currentcolor l c h \/ .1);\">A2<\/td>\n<\/tr>\n<tr>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td align=\"left\">\u2191<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td align=\"left\">\u2191<\/td>\n<\/tr>\n<tr>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td colspan=\"2\" align=\"left\" valign=\"baseline\">first<\/td>\n<td>&nbsp;<\/td>\n<td>last<br \/>\nmid<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>The old algorithm now recurses to swap the A3 block with the A1+A2 block. And that&#8217;s what the new algorithm does, too.<\/p>\n<p>So it&#8217;s the same algorithm, just with a different point of view. It&#8217;s another case of <a title=\"The geeky thrill of discovering that two things are really the same thing, just with different labels\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20140414-01\/?p=1253\"> the geeky thrill of discovering that two things are really the same thing, just with different labels<\/a>.<\/p>\n<p>Now, the two algorithms are not identical. The new algorithm is symmetric and performs its swaps from right to left if the larger block is on the right. The old algorithm always operates from left to right.<\/p>\n<p>But the similarity is striking.<\/p>\n<p>Next time, we&#8217;ll look at how clang performs rotation by decomposing into cycles.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>We&#8217;ve seen this before.<\/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-112378","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>We&#8217;ve seen this before.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/112378","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=112378"}],"version-history":[{"count":1,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/112378\/revisions"}],"predecessor-version":[{"id":112381,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/112378\/revisions\/112381"}],"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=112378"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=112378"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=112378"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}