Category: Research

  • Loomiere: a post-mortem of my favorite code

    Loomiere: a post-mortem of my favorite code

    Fifteen years ago I wrote the code I’m still proudest of, for a product category that no longer exists. This is its story, told now that I’ve finally given the repo a dignified closing README instead of letting it rot quietly.

    2010, when video was hard

    If you’re younger than the problem, here’s the world: web video meant Flash players doing progressive download over plain HTTP — and I do mean your own Flash player, because every video site had to write one; there was no HTML5 <video> coming to your help. Seeking worked by asking the server for the file from an offset (?start=). There was no HLS, no DASH, no adaptive bitrate; CDNs existed but were priced for companies with sales departments.

    If you ran a video site, bandwidth was your business model — every wasted byte was money, and every rebuffer was a lost viewer.

    I ran into this the honest way. My company operated peteava.ro, a Romanian video-sharing site (now defunct, like most of that era), and I had already written one streamer for it — psstream, a PHP extension from 2008. It did the job until it didn’t: none of our servers could hold more than about 150 simultaneous streams without the machine practically freezing.

    That wall is where Loomiere begins.

    First pass: the micro-streamers

    The first Loomiere (0.x) was an exercise in minimalism: a set of tiny micro-streamers — one per format — written in C against dietlibc, statically linked, the MP4 streamer weighing about 21 KB on disk and 120 KB in memory, spawned one-process-per-request under ipsvd/runit.

    It entered production on all of peteava’s streaming servers on Friday, January 29, 2010, at 14:31 (some moments you write down). The same day, a random check on the most strained server showed 653 simultaneous clients where the old streamer had managed 150 — and total bandwidth had dropped by roughly a third, because of the throttle I’ll get to in a moment.

    Two months thinking, two weeks writing

    The 0.x design had a ceiling — one process per connection is honest but profligate — so I rebuilt it. I spent about two months just thinking about Loomiere 2.0’s internals before writing it, and then wrote the whole thing in about two weeks.

    Think long, write short — not always possible, but I’ve been trying to work like that ever since; the thinking is the work, the typing is transcription.

    What I remember from those two months isn’t code at all: it’s the old whiteboard, so full of sketches that at some point I was defending it with my own body from the cleaning lady’s sponge. Improbably, a photo of it survives — the exact spot where the Lua and C universes cross inside the code, coroutines yielding on one side of the line, the event loop deciding on the other:

    What came out: a C core on libev with SMP worker threads (“the hardware should be the only limit”), an in-memory cache, and — the part I’d still defend in any design review — Lua as the configuration and scripting surface. Virtual hosts, URL routing, rewrite logic: all Lua, so wiring Loomiere into an existing site was an afternoon, not a migration.

    And that whiteboard sketch is exactly the machinery being born: each stream lived as a Lua coroutine — a blocking operation yielded it across the line to the C event loop and resumed it on readiness — with spinlocks at the few seams where worker threads had to touch. Thousands of streams juggled in parallel on one machine, in 2010, when Node.js was a year-old curiosity and the async-everything movement it would ignite hadn’t happened yet. It was, and I say this with the full bias of authorship, truly beautiful.

    The throttle (the part worth stealing)

    The economics lived in one mechanism: the VBR-aware bandwidth-conservation throttle. Serve at full speed only until the client holds N seconds of playback ahead of the play-head (default: 20), then pace delivery to the stream’s own bitrate. The subtlety is “seconds”: with variable-bitrate video a byte target is wrong at exactly the moments that matter, so the buffer was tracked in playback time, not bytes. Smooth playback, instant seeks, and — since most viewers don’t finish most videos — dramatically less bandwidth bought for content nobody watched.

    On day one it cut peteava’s streaming bandwidth from roughly 1300 to 900 megabits while serving more clients.

    The numbers

    The run I still grin about was a single SSD-based machine in January 2011 — a 3 GHz Core i7, 12 GB RAM, six 256 GB SSDs, three gigabit NICs:

    6500+ active concurrent streams, 2841 Mbps of traffic, with the SSDs under 20% load.

    Cacti graph from January 2011: 2771.71 Mbps effective bandwidth
    2771.71 Mbps effective bandwidth — the original Cacti capture.
    Monitoring graph from January 2011: 6500+ active concurrent streams
    6500+ active concurrent streams on one machine.

    At that point we hit a wall that wasn’t Loomiere: somewhere between the I/O path and the sockets — interrupts, kernel, mainboard — the CPU saturated while memory and disks idled. \

    My note from that night reads, in its entirety: “Hmm… this is a deep one.” I never did get to the bottom of it; the wall was comfortably above what any single machine needed to do.

    CPU activity graph showing saturation while memory and disks idled
    The wall: CPU saturating while everything else idled. “Hmm… this is a deep one.”

    In steady production the fleet told the real story: Loomiere 2.0 served all of peteava.ro’s video — about 800 TB every month, from 14 servers — and did so for over five years in total.

    And none of it ran in a cloud, because there was no cloud worth the name: every one of those servers was physical iron that someone had to rack, cable, and keep alive (shout-out to Bogdan, who has since turned that craft into his own hosting business).

    Even the best things end

    Loomiere stood on libev, whose documentation contains a section titled — beautifully — “ev_cleanup — even the best things end”. Somewhere in those docs (I’ve never managed to find the exact passage again, but never managed to forget it either) its author left a thought along these lines: you always have the API to kill whatever needs killing — but there is something about a program written so well that, at end-time, all its threads close gracefully on their own… something close to perfect.

    Loomiere delivered on that spectacularly: at one point it ran for three years without a restart — no leaks, and no creeping memory fragmentation either, which at that kind of uptime stops being an implementation detail and becomes a discipline.

    And when asked to stop, it stopped the way the docs dreamed: every watcher, every worker, every connection closing in order, all the way down.

    And the child in me will forever cherish that.

    What happened

    The category was absorbed, the way categories are. nginx shipped its mp4/flv modules and pseudo-streaming became a config flag on a server everyone already ran. Then adaptive streaming (HLS, DASH) and CDNs finished the job. Loomiere’s last release was December 2011 — right about when the ground finished moving.

    Could I revive it? No. The industry has advanced enormously and I’d be fighting monsters — nginx, CDNs, twenty years of protocol evolution — over territory nobody disputes anymore. Reviving it would honor the code less than closing it well does.

    What it left behind

    Three things survived the product, all of them still in use:

    The method: think long, write short. Not always possible — but I’ve been trying ever since, and Krbn, the pencil-rendering engine I released recently, was built exactly that way, fifteen years later.

    The taste: performance as a design property, not an optimization pass; the conviction that software’s job is to get out of the hardware’s way.

    And the lesson I’d offer anyone building infrastructure products: a product built on a protocol era ends with the era. Platforms absorb categories. If your product is a feature of the platform’s future self, your moat is time — spend it accordingly.

    The repo is preserved at github.com/vpalos/loomiere, readable and honest about what it is. Start with options.lua — the whole design philosophy fits in its comments.

    Rest well, old friend. You were fast.

  • Genesis imperfecta (or: I finally built the pencil)

    Genesis imperfecta (or: I finally built the pencil)

    This is not a scan of a pencil drawing. It’s SVG — computed, deterministic, and, in a sense I’ll get to, exact.

    A sphere resting in a gravity well, the sheet's hatching flowing into the dent

    For the past few months I’ve been building Krbn, a small open-source rendering engine that draws 3D scenes the way a technical sketch artist would. It’s a childhood idea. Back then I wanted to call it genesis imperfecta, because what fascinated me was — and still is — the idea of going against photorealism: that a human being conveys more meaning in a drawing than any photorealistic render, precisely by renouncing detail, dropping precision, deliberately introducing imperfections. The catch, of course, is the question hiding inside that sentence: which detail do you drop? Which imperfections do you introduce? That question turns out to be an engineering problem, and a lovely one.

    The inversion

    Most rendering answers “what color is this pixel?” A pencil drawing answers “which lines would an artist draw — and which would they leave out?” So Krbn has no shading model. It derives strokes from geometry and then applies policies to the stroke set:

    • Silhouettes of spheres, cylinders, cones are computed in closed form — they are exact conics; a torus yields its true quartic. No sampling, no meshes pretending to be curves.
    • Hidden lines aren’t z-buffered away; each contour is split analytically into visible and ghosted runs, the way a draftsman keeps the far edge of a box faintly alive.
    • There is no alpha channel. Cross-hatching is inherently see-through — the gaps between strokes reveal what’s behind, exactly like on paper. And the hatching follows each surface’s own curvature field, so form comes from direction, not gradients.
    Two trefoil knots, one hatched along its curvature field, one hatched flat

    Exactness was a deliberate value, not an optimization: intersections are roots of low-degree polynomials, and the degenerate cases — tangent lines, coincident conics, grazing cusps — are treated as the spec, not as edge cases. The reward is output you can trust: the same scene always emits the same, byte-identical, diffable SVG.

    The part stills can’t show

    Hand-drawn wobble is easy. Hand-drawn wobble that survives animation is not: re-randomize per frame and the whole drawing “boils”. In Krbn the wobble is seeded on stable stroke identity — each line carries its jitter with it across frames — so an orbiting camera slides the silhouettes while the lines stay calm:

    A camera orbit of a mixed scene — the hand-drawn lines stay calm, no boiling

    Getting to that calm — persistent stroke identity, hatch that pans with surfaces instead of re-dealing, detail that fades instead of popping — was the hardest part of the project, and my favorite.

    The other experiment

    I’ll say this plainly, because I put it in the README too: Krbn was built with heavy AI assistance, unapologetically. It doubled as an experiment — how far can a carefully directed human–AI collaboration get on a hard rendering problem? “Carefully directed” is the operative phrase: the architecture, the taste, and the standards are mine, written down and enforced (the working brief and the numerical-robustness rules ship in the repo — judge for yourself). The code was reviewed and tested like any other code. The answer to the experiment, as far as I’m concerned: far. Farther than I expected.

    What’s next

    Nothing on a schedule — this is a break-time project and I intend to keep it joyful. But the ideas corner is already growing (stippling as an alternate hatch strategy, colored pencils that stay stroke color and never become a fill model, deliberate temporal decoherence à la “Take on Me” by A-ha), and the repo has an example gallery.

    If you make technical figures, drive a pen plotter, or just share the fascination — come say hello. Or don’t; all is good. The “pencil” exists now, and 12-yer-old me is happy.

  • Loomiere 2.0.1-beta finally out!

    All right! I finally got around to releasing this code to the public. I won’t say much now (since I’m tired) but I will say that this software has been functioning in production environment for over 1 year and a half serving all the video content available on the romanian website http://peteava.ro (about 800 TB of data each month out of 14 servers).

    Some (old) statistics showing it’s power can be found here.

    Source code: https://github.com/valeriupalos/loomiere.

    Warning:
    Any software downloaded from this website is *never* to be associated in any form with pornographic or erotic content! I.E. “Loomiere” must *never* be used to stream pornographic or erotic videos! There are plenty alternatives if you can’t help it.

  • A simple(r) approach for class-based OOP in JavaScript.

    Like others before me, I found JavaScript’s prototype-based OOP support – while very powerful – quite cumbersome in some situations where I needed a certain structure or hierarchy of objects, situations in which one quickly comes to the conclusion that the best answer would be the concept of a Class.

    However, while I am well aware that the web is filled with various implementations of classical (i.e. class-based) object orientation in JavaScript, I can not help the feeling that they are over-engineered and over-complicated without need, quite often attempting to provide more functionality than is actually necessary. In short, I don’t feel they’re “classical” at all.

    This is why I started working on my own implementation of classical OOP  in JavaScript, which is shown below. Currently this is only a proposal, although I already started using it in one of my projects. Anyway, let’s look at it first then I’ll try and explain how it works.

    The Code

    /** Class factory. */
    function Class(members) {
    
        // proxy constructor
        var Proxy = function() {
            for (var item in members) {
                this[item] = members[item];
            }
        };
    
        // proxy inheritance
        Proxy.prototype = (members.base || Class).prototype;
    
        // class constructor
        var Build = members.init || function() {};
    
        // class inheritance
        Build.prototype = new Proxy();
        Build.prototype.base = Proxy.prototype;
        Build.prototype.constructor = Build;
    
        // ready
        return Build;
    }

    That is all, and this little function can have a whole world of nifty consequences like deep inheritance or access to the super-class via this.base (and others). But here is what happens: the basic principle at work here is the Proxy object, which essentially, contains all the members of our new class (the ones that are passed to the Class factory function as the members parameter). (more…)

  • Case study: fixed number of iterations with LPeg

    Words seem to fail to describe just how awesome LPeg is. Designed as a Lua implementation of the PEG concept, it is a true programming gem! Please, if you dont’t know what it is, take some time to familiarize yourself with it! It’s not the easiest thing to grasp, but you will not regret it! It is certainly one of the most worthwhile learning efforts you can make in generic parsing.

    One great feature of LPeg is that it’s binary-safe, meaning that (unlike regular expressions) it can be safely used to parse binary data! This makes it an excellent tool for parsing binary protocols, especially network communication protocols, such as the Action Message Format (used by Adobe Flash for making remote calls and even in FLV movie files). I’ll leave it to you to explore the possibilities…

    Beware that from here on, I assume that you know your way around Lua, LPeg and how they work.

    The problem

    That being said, this article is actually about an unusual roadblock I hit while using LPeg to build a Lua-based AMF parser, and the various solutions I found and/or came up with to overcome it (you didn’t think that I mentioned AMF before by accident, did you?).

    The issue is LPeg’s implementation of repetitive patterns: in particular, its inability to match (or capture) a fixed number of occurrences of a certain pattern, although it can match a minimum or a maximum number of such occurrences, which is perfect for stream-oriented parsing (such as parsing programming languages) but insufficient for binary data.

    Just to clarify, here’s a small list of LPeg patterns which correspond to the typical PCRE repetitive constructs (in each case we’re trying to match the string ‘cloth’):

    Matching occurrences of ‘cloth’ PCRE pattern LPeg pattern
    0 or more (at least 0) /(cloth)*/ lpeg.P'cloth'^0
    1 or more (at least 1) /(cloth)+/ lpeg.P'cloth'^1
    X or more (at least X) /(cloth){X,}/ lpeg.P'cloth'^X
    1 or less (at most 1) /(cloth)?/ lpeg.P'cloth'^-1
    X or less (at most X) /(cloth){,X}/ lpeg.P'cloth'^-X
    A: precisely X (no more, no less) /(cloth){X,X}/ -- unavailable --
    B: anywhere between X and Y /(cloth){X,Y}/ -- unavailable --

    For the last two cases (i.e. A and B), LPeg does not offer any simple constructs so we have to find a complex one. Let’s put aside the case B for now, and try to tackle A… (more…)