<?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>Performance on afloat.boats</title><link>https://www.afloat.boats/tags/performance/</link><description>Recent content in Performance on afloat.boats</description><generator>Hugo -- gohugo.io</generator><language>en-us</language><lastBuildDate>Fri, 22 Aug 2025 11:55:13 -0700</lastBuildDate><atom:link href="https://www.afloat.boats/tags/performance/index.xml" rel="self" type="application/rss+xml"/><item><title>Man vs Vibes</title><link>https://www.afloat.boats/posts/man-vs-vibes/</link><pubDate>Fri, 22 Aug 2025 11:55:13 -0700</pubDate><guid>https://www.afloat.boats/posts/man-vs-vibes/</guid><description>Got nerd sniped into missing a workout, and improving the performace of rendering 500k rectangles by 10x.</description><content:encoded><![CDATA[<p>Yesterday I got nerd sniped into missing a workout.</p>
<figure>
    <video
        class='video-shortcode max-w-full'
        preload=''
        controls
    >
        <source src='/rectangles.webm' type='video/webm' />
    </video>
</figure>

<p><a href="/frankenpenguin/?rectangles=200000" target="_blank" rel="noopener noreferrer">Frankenpenguin</a> | <a href="https://github.com/tauseefk/frankenpenguin" target="_blank" rel="noopener noreferrer">Repository</a></p>
<p>[2:30 PM] I discovered a blog post about vibe coded performance benchmarks for drawing a large number of rectangles on the HTML canvas.
It was an interesting read and I was nodding along until I saw that all the Typescript benchmarks were better than the Rust ones. That can&rsquo;t be right?</p>
<p>[3:00 PM] My hunch was that most of the time was spent crossing the boundary between Rust and Javascript. This is something I&rsquo;ve been burned by in the past.</p>
<p>[3:30 PM] I decoupled the work scheduling from the sync compute and rendering [1].
Deleted a few lines of code that were creating the render loop and recursively calling it from the <code>request_animation_frame</code>. And then moved the scheduling to Javascript.</p>
<h5 id="before">BEFORE</h5>
<pre tabindex="0"><code>// Rust updates the rectangle positions
//      renders to canvas
//      updates the fps counter
//      recursively schedules next render loop

┌─────────────────┐                       ┌───────────────────────────────────┐
│  Javascript     │                       │  Rust                             │
│                 │                       │                                   │
│                 │                       │  ┌─────────────────────────────┐  │
│                 │                       │  │ start                       │  │
│                 │                       │  │                             │  │
│                 │                       │  │ ┌──────────────────────┐    │  │
│                 │                       │  │ │ start_animation_loop │    │  │
│                 │                       │  │ │                      │◀┐  │  │
│                 │  wasm_bindgen(start)  │  │ └──────┬───────────────┘ │  │  │
│                 │  ──────────────────▶  │  │        │                 │  │  │
│                 │                       │  │        ▼                 │  │  │
│                 │                       │  │ ┌──────────────────────┐ │  │  │
│                 │                       │  │ │ render               │ │  │  │
│                 │                       │  │ │                      │ │  │  │
│                 │                       │  │ └──────┬───────────────┘ │  │  │
│                 │                       │  │        │                 │  │  │
│                 │                       │  │        └─────────────────┘  │  │
│                 │                       │  └─────────────────────────────┘  │
└─────────────────┘                       └───────────────────────────────────┘
</code></pre><h5 id="after">AFTER</h5>
<pre tabindex="0"><code>// JavaScript calls the `frankenpenguin.tick()`

// Rust       updates the rectangle positions
//            renders to canvas

// JavaScript updates the fps counter
//            recursively schedules the ticks

┌────────────────────────────────────┐                   ┌────────────────────┐
│  JavaScript                        │                   │ Rust               │
│                                    │                   │                    │
│  ┌────────────────────────────┐    │                   │                    │
│  │ render                     │◀─┐ │                   │                    │
│  │                            │  │ │                   │                    │
│  │ ┌───────────────────────┐  │  │ │                   │ ┌──────────────┐   │
│  │ │                       │  │  │ │                   │ │ tick         │   │
│  │ │                       │  │  │ │                   │ │              │   │
│  │ │                       │  │  │ │                   │ └──┬───────────┘   │
│  │ │                       │  │  │ │                   │    ▼               │
│  │ │ frankenPenguin.tick() │────────────────────────▶  │ ┌──────────────┐   │
│  │ │                       │  │  │ │                   │ │ render       │   │
│  │ │ requestAnimationFrame │─────┘ │                   │ │              │   │
│  │ │                       │  │    │                   │ └──────────────┘   │
│  │ └───────────────────────┘  │    │                   │                    │
│  └────────────────────────────┘    │                   │                    │
└────────────────────────────────────┘                   └────────────────────┘
</code></pre><p>[3:45 PM] I run the benchmarks. Hmmmm, it&rsquo;s 2x faster, not bad. I was mistakenly comparing the updated Rust + WebGL implementation to the TS + WebGL version.</p>
<p>[4:00 PM] The train is unusually late.</p>
<p>[4:38 PM] I get to the gym, but the person turns me away as I&rsquo;m too late [2].</p>
<p>[5:21 PM] I push the repository to GitHub.</p>
<p>[This morning] I realize that I was comparing the wrong benchmarks and the diff resulted in a 10x bump in perf.</p>
<p>Aside 1:
To schedule async work in the browser you can pass a callback to the <code>requestAnimationFrame</code> API.
As Rust doesn&rsquo;t ship with a runtime, scheduling work requires using the closest one avaiable. In the browser it ends up being&hellip; the browser. So you have to use a crate that would provide the bindings to the <code>requestAnimationFrame</code> API.</p>
<p>Aside 2:
I ended with playing Table Tennis afterwards so it&rsquo;s all good.</p>
]]></content:encoded></item></channel></rss>