Let The Compiler Do The Work

(Series Overview)

In this series so far, we’ve taken a C program and converted it into a faster, smaller, and reasonably robust Rust program. The Rust program is a recognizable descendant of the C program, and that was deliberate: my goal was to compare and contrast the two languages for optimized code.

In this bonus section, I’ll walk through how we’d write the program from scratch in Rust. In particular, I’m going to rely on compiler auto-vectorization to produce a program that is shorter, simpler, portable, and significantly faster… and without any unsafe.

Can it be? Read on…

Making Safe Things From Unsafe Parts

(Series Overview)

In part 4, we took the unsafe code that deals with treating arrays of f64 as arrays of vectors, and we corralled it into a safe API.

In this installment, we’ll look at the remaining reasons why advance is an unsafe fn, and make it safe — not by removing all the unsafe, but by narrowing it down.

This one’s a doozy — the remaining changes to advance are hard to separate, so I’ve packed them all into one section. Now is probably a good time to refill your coffee.

A More Perfect Union

(Series Overview)

In part 3 we found that our use of uninitialized memory was a premature optimization that didn’t actually improve performance. This left us with only one remaining unsafe function, but, boy, is it a doozy.

In this part, I’ll begin the process of corralling its unsafe optimizations into more clearly safe code, by replacing arbitrary pointer casting with a lightweight abstraction.

Measure What You Optimize

(Series Overview)

In part 2 we introduced Rust references, and this was enough to convert one of our inner functions into safe Rust.

The others are still unsafe. There are several reasons for this. In this, the briefest of sections, we’ll tackle the easiest one: deliberate use of uninitialized memory.

References Available Upon Request

(Series Overview)

In the first part of this tutorial we took an optimized C program and translated it to an equivalent Rust program, complete with all the unsafe weirdness of the original: uninitialized variables, pointer casting and arithmetic, etc.

In this section, we’ll begin using Rust’s features to make the program incrementally more robust, while keeping performance unchanged.

Specifically, we’ll begin by introducing references.

You Can't Write C in Just Any Ol' Language

(Series Overview)

In this part of the series, we’ll take a grungy optimized C program and translate it, fairly literally, into a grungy optimized unsafe Rust program. It’ll get the same results, with the same performance, as the original.

Learn Rust the Dangerous Way

LRtDW is a series of articles putting Rust features in context for low-level C programmers who maybe don’t have a formal CS background — the sort of people who work on firmware, game engines, OS kernels, and the like. Basically, people like me.

I’ve added Rust to my toolbelt, and I hope to get you excited enough to do the same.

  1. Why Learn Rust the Dangerous Way? Introduction and ground rules.

  2. You can’t write C in just any ol’ language: translating a grungy optimized C program into grungy optimized unsafe Rust.

  3. References available upon request: how Rust references are different from pointers, how they are the same, and why we care.

  4. Measure what you optimize: taking a hard look at an optimization based on uninitialized memory, and converting it to safe code that’s just as fast.

  5. A more perfect union: considering alternatives to pointer casting, and how to write safe wrappers for unsafe operations.

  6. Making safe things from unsafe parts: finally converting most of the program to safe code, and making sure that the unsafe bits are safe-ish.

  7. Let the compiler do the work: a bonus section that looks at how we’d write the program idiomatically in native Rust, and rely on auto-vectorization to make it fast.

Rewriting m4vgalib in Rust

If this isn’t your first time visiting my blog, you may recall that I’ve spent the past several years building an elaborate microcontroller graphics demo using C++.

Over the past few months, I’ve been rewriting it — in Rust.

This is an interesting test case for Rust, because we’re very much in C/C++’s home court here: the demo runs on the bare metal, without an operating system, and is very sensitive to both CPU timing and memory usage.

The results so far? The Rust implementation is simpler, shorter (in lines of code), faster, and smaller (in bytes of Flash) than my heavily-optimized C++ version — and because it’s almost entirely safe code, several types of bugs that I fought regularly, such as race conditions and dangling pointers, are now caught by the compiler.

It’s fantastic. Read on for my notes on the process.

Prefer Rust to C/C++ for new code.

This is a position paper that I originally circulated inside the firmware community at X. I’ve gotten requests for a public link, so I’ve cleaned it up and posted it here. This is, obviously, my personal opinion. Please read the whole thing before sending me angry emails.

tl;dr: C/C++ have enough design flaws, and the alternative tools are in good enough shape, that I do not recommend using C/C++ for new development except in extenuating circumstances. In situations where you actually need the power of C/C++, use Rust instead. In other situations, you shouldn’t have been using C/C++ anyway — use nearly anything else.

Racing the Beam

This post is the fourth in a series looking at the design and implementation of my Glitch demo and the m4vgalib code that powers it.

In part three we took a deep dive into the STM32F407’s internal architecture, and looked at how to sustain the high-bandwidth flow that we set up in part two.

Great, so we have pixels streaming from RAM at a predictable rate — but we don’t have enough RAM to hold an entire frame’s worth of 8-bit pixels! What to do?

Why, we generate the pixels as they’re needed, of course! But that’s easier said than done: generate them how, and from what?

In this article, I’ll take a look at m4vgalib’s answer to these questions: the rasterizer.