<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Duff's Device | FoxTechWorld</title><link>https://foxtechworld.github.io/tags/duffs-device/</link><description>Conteúdo recente em Duff's Device no FoxTechWorld.</description><language>pt-BR</language><copyright>KitsuneSemCalda (CC BY 4.0)</copyright><lastBuildDate>Sat, 15 Aug 2026 00:00:00 -0300</lastBuildDate><generator>Hugo</generator><atom:link href="https://foxtechworld.github.io/tags/duffs-device/" rel="self" type="application/rss+xml"/><item><title>Duff's device: desenrolamento de loops em C</title><link>https://foxtechworld.github.io/algoritmos-duff-device-for-loop-unrolling/</link><guid isPermaLink="true">https://foxtechworld.github.io/algoritmos-duff-device-for-loop-unrolling/</guid><pubDate>Sun, 26 Oct 2025 18:20:07 -0300</pubDate><description>Entenda como o Duff's device combina switch e do-while para desenrolar loops em C, por que funciona e quando essa otimização faz sentido.</description><category>Programação</category><category>Algoritmos</category><category>Estrutura De Dados</category><category>Otimização</category><category>Duff's Device</category><category>Loop Unrolling</category><content:encoded><![CDATA[<h2 id="como-os-compiladores-conseguem-otimizar-loops">Como os compiladores conseguem otimizar loops?</h2>
<p>A princípio isso não parece possível. Se um loop executa uma ação N vezes, o tempo total deveria ser basicamente o custo dessa ação multiplicado por N. Só que na prática, quando compilamos com -O3, o resultado é bem diferente: o mesmo loop roda mais rápido, mesmo fazendo o mesmo trabalho.</p>
<p>E a culpa (ou o mérito) é de um pequeno trecho de código que se tornou quase uma lenda na história da otimização em C:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="k">register</span> <span class="kt">int</span> <span class="o">*</span><span class="n">to</span><span class="p">,</span> <span class="o">*</span><span class="n">from</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">register</span> <span class="kt">int</span> <span class="n">count</span> <span class="o">=</span> <span class="p">(</span><span class="n">n</span> <span class="o">+</span> <span class="mi">7</span><span class="p">)</span> <span class="o">/</span> <span class="mi">8</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">switch</span> <span class="p">(</span><span class="n">n</span> <span class="o">%</span> <span class="mi">8</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"><span class="k">case</span> <span class="mi">0</span><span class="o">:</span> <span class="k">do</span> <span class="p">{</span> <span class="o">*</span><span class="n">to</span> <span class="o">=</span> <span class="o">*</span><span class="n">from</span><span class="o">++</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">case</span> <span class="mi">7</span><span class="o">:</span>      <span class="o">*</span><span class="n">to</span> <span class="o">=</span> <span class="o">*</span><span class="n">from</span><span class="o">++</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">case</span> <span class="mi">6</span><span class="o">:</span>      <span class="o">*</span><span class="n">to</span> <span class="o">=</span> <span class="o">*</span><span class="n">from</span><span class="o">++</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">case</span> <span class="mi">5</span><span class="o">:</span>      <span class="o">*</span><span class="n">to</span> <span class="o">=</span> <span class="o">*</span><span class="n">from</span><span class="o">++</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">case</span> <span class="mi">4</span><span class="o">:</span>      <span class="o">*</span><span class="n">to</span> <span class="o">=</span> <span class="o">*</span><span class="n">from</span><span class="o">++</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">case</span> <span class="mi">3</span><span class="o">:</span>      <span class="o">*</span><span class="n">to</span> <span class="o">=</span> <span class="o">*</span><span class="n">from</span><span class="o">++</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">case</span> <span class="mi">2</span><span class="o">:</span>      <span class="o">*</span><span class="n">to</span> <span class="o">=</span> <span class="o">*</span><span class="n">from</span><span class="o">++</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">case</span> <span class="mi">1</span><span class="o">:</span>      <span class="o">*</span><span class="n">to</span> <span class="o">=</span> <span class="o">*</span><span class="n">from</span><span class="o">++</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span> <span class="k">while</span> <span class="p">(</span><span class="o">--</span><span class="n">count</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Este pequeno frame é o <a href="https://www.gnu.org/software/c-intro-and-ref/manual/html_node/Duffs-Device.html"><strong><em>Duff Device</em></strong></a> criado por <a href="https://en.wikipedia.org/wiki/Tom_Duff"><strong>Tom Duff</strong></a> em 1983.</p>
<h2 id="o-que-é-o-desenrolamento-de-loops">O que é o desenrolamento de loops?</h2>
<p>Loop Unrolling (ou desenrolamento de loop) é uma técnica de otimização onde o compilador duplica o corpo do loop várias vezes para reduzir o custo do controle de iteração.</p>
<p>Em vez de checar a condição e incrementar o contador a cada passo, ele faz operações por iterações, diminuindo o número total de saltos no código.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">n</span><span class="p">;</span> <span class="o">++</span><span class="n">i</span><span class="p">){</span>
</span></span><span class="line"><span class="cl">    <span class="n">copia</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="n">origem</span><span class="p">[</span><span class="n">i</span><span class="p">];</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>se transforma em algo conceitualmente similar:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">n</span><span class="p">;</span> <span class="n">i</span> <span class="o">+=</span> <span class="mi">4</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">copia</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="n">origem</span><span class="p">[</span><span class="n">i</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">    <span class="n">copia</span><span class="p">[</span><span class="n">i</span><span class="o">+</span><span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="n">origem</span><span class="p">[</span><span class="n">i</span><span class="o">+</span><span class="mi">1</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">    <span class="n">copia</span><span class="p">[</span><span class="n">i</span><span class="o">+</span><span class="mi">2</span><span class="p">]</span> <span class="o">=</span> <span class="n">origem</span><span class="p">[</span><span class="n">i</span><span class="o">+</span><span class="mi">2</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">    <span class="n">copia</span><span class="p">[</span><span class="n">i</span><span class="o">+</span><span class="mi">3</span><span class="p">]</span> <span class="o">=</span> <span class="n">origem</span><span class="p">[</span><span class="n">i</span><span class="o">+</span><span class="mi">3</span><span class="p">];</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Menos branches, mais instruções seguidas, mais chance de o processador aproveitar o pipeline e executar tudo de forma quase branchless, parecido com o que o <code>SIMD</code> faz.</p>
<p>O Duff Device foi a primeira vez que alguém usou essa ideia manualmente, antes de os compiladores modernos fazerem isso sozinhos.</p>
<p>É uma daquelas otimizações que parecem mágica até você perceber que o compilador só está tentando evitar o que humanos mais odeiam: repetições desnecessárias.</p>
<h2 id="por-que-isso-funciona-em-c">Por que isso funciona em C?</h2>
<p>A lógica do Duff Device não é muito diferente de acessar vários elementos de um array de uma vez. Se copia e origem têm o mesmo tamanho, você consegue fazer a mesma operação em uma única iteração, processando múltiplas posições do vetor ao mesmo tempo. Isso reduz o overhead de controle do loop e deixa o processador mais livre para executar instruções em sequência, aumentando a eficiência geral.</p>
<p>O <code>do-while</code> sozinho é simples, executa a iteração uma vez e faz o teste da condição garantindo pelo menos uma execução.</p>
<p>O <code>switch</code> é um salto condicional que usa <a href="https://en.wikipedia.org/wiki/Branch_table">jump-table</a> para ter tempo de execução O(1).</p>
<p>No truque do <strong>Duff&rsquo;s device</strong>, os <code>breaks</code> são <strong>removidos de propósito</strong> e o <code>switch</code> é combinado com o <code>do-while</code>.</p>
<p>Isso cria efeitos simultâneos:</p>
<ol>
<li>O <code>switch</code> escolhe em qual ponto do loop começar</li>
<li>O <code>do-while</code> continua repetindo o bloco inteiro várias vezes até completar todas as iterações.</li>
</ol>
<p>Na prática, isso significa que você começa no case certo (alinhando as iterações que sobraram) e depois &ldquo;cai&rdquo; pelos demais cases sem saltos adicionais, repetindo tudo até terminar.</p>
<p>Cada execução <code>do-while</code> processa vários elementos do array, e o switch inicial só garante que você comece na posição correta.</p>
<h2 id="conclusão">Conclusão</h2>
<p>Pode ser uma técnica útil em alguns casos, mas não na maioria.</p>
<p>É feio, parece gambiarra e compiladores modernos já conseguem realizar desenrolamento de loops automaticamente com opções como <code>-O3</code> ou <code>-funroll-loops</code>.</p>
<p>Porém, se você estiver estudando programação, otimização de código-fonte ou algoritmos, vale a pena conhecer a técnica.</p>
]]></content:encoded></item></channel></rss>