Skip to main content

Playing with fire: Tone mapping and color

· 7 min read
Bradlee Speice

So far, our plot() function has been fairly simple: map a fractal flame coordinate to a specific pixel, and color in that pixel. This works well for simple function systems (like Sierpinski's Gasket), but more complex systems (like the reference parameters) produce grainy images.

In this post, we'll refine the image quality and add color to really make things shine.

The webpack industrial complex

· 5 min read
Bradlee Speice

This started because I wanted to build a synthesizer. Setting a goal of "digital DX7" was ambitious, but I needed something unrelated to the day job. Beyond that, working with audio seemed like a good challenge. I enjoy performance-focused code, and performance problems in audio are conspicuous. Building a web project was an obvious choice because of the web audio API documentation and independence from a large Digital Audio Workstation (DAW).

The project was soon derailed trying to sort out technical issues unrelated to the original purpose. Finding a resolution was a frustrating journey, and it's still not clear whether those problems were my fault. As a result, I'm writing this to try making sense of it, as a case study/reference material, and to salvage something from the process.

Release the GIL

· 9 min read
Bradlee Speice

Complaining about the Global Interpreter Lock (GIL) seems like a rite of passage for Python developers. It's easy to criticize a design decision made before multi-core CPU's were widely available, but the fact that it's still around indicates that it generally works Good Enough. Besides, there are simple and effective workarounds; it's not hard to start a new process and use message passing to synchronize code running in parallel.

Still, wouldn't it be nice to have more than a single active interpreter thread? In an age of asynchronicity and M:N threading, Python seems lacking. The ideal scenario is to take advantage of both Python's productivity and the modern CPU's parallel capabilities.

Binary format shootout

· 9 min read
Bradlee Speice

I've found that in many personal projects, analysis paralysis is particularly deadly. Making good decisions in the beginning avoids pain and suffering later; if extra research prevents future problems, I'm happy to continue procrastinating researching indefinitely.

So let's say you're in need of a binary serialization format. Data will be going over the network, not just in memory, so having a schema document and code generation is a must. Performance is crucial, so formats that support zero-copy de/serialization are given priority. And the more languages supported, the better; I use Rust, but can't predict what other languages this could interact with.

Given these requirements, the candidates I could find were:

On building high performance systems

· 13 min read
Bradlee Speice

Prior to working in the trading industry, my assumption was that High Frequency Trading (HFT) is made up of people who have access to secret techniques mortal developers could only dream of. There had to be some secret art that could only be learned if one had an appropriately tragic backstory.

Making bread

· 2 min read
Bradlee Speice

Having recently started my "gardening leave" between positions, I have some more personal time available. I'm planning to stay productive, contributing to some open-source projects, but it also occurred to me that despite talking about bread pics, this blog has been purely technical. Maybe I'll change the site title from "The Old Speice Guy" to "Bites and Bytes"?

Allocations in Rust: Summary

· 2 min read
Bradlee Speice

While there's a lot of interesting detail captured in this series, it's often helpful to have a document that answers some "yes/no" questions. You may not care about what an Iterator looks like in assembly, you just need to know whether it allocates an object on the heap or not. And while Rust will prioritize the fastest behavior it can, here are the rules for each memory type:

Allocations in Rust: Compiler optimizations

· 4 min read

Up to this point, we've been discussing memory usage in the Rust language by focusing on simple rules that are mostly right for small chunks of code. We've spent time showing how those rules work themselves out in practice, and become familiar with reading the assembly code needed to see each memory type (global, stack, heap) in action.

Throughout the series so far, we've put a handicap on the code. In the name of consistent and understandable results, we've asked the compiler to pretty please leave the training wheels on. Now is the time where we throw out all the rules and take off the kid gloves. As it turns out, both the Rust compiler and the LLVM optimizers are incredibly sophisticated, and we'll step back and let them do their job.