8 ms·
These mistakes basically boil down to not using slow scripting languages and making the core buffer loop run fast and undisturbed. For realtime synthesis use c
by iammyIP 10y ago
These mistakes basically boil down to not using slow scripting languages and making the core buffer loop run fast and undisturbed.
For realtime synthesis use c or c++, optimise early, use trigonometry approximations in range -pi / pi, do not use lookup tables, they unnecessarily fill the cache, cpus are fast enough, write the core loop branchless, write for cache, use vectorisation and function pointers.
Do not use anything fancy, simple int32_t, float and vector/array is enough (some filters need double precision though). Do not copy stuff, point to it. Precalculate what can be precalculated, e.g. dont do x / samplerate, do x * samplerate_inverse. Check for denormals.
- Unklejoe 10y ago[do not use lookup tables, they unnecessarily fill the cache...] Good point. A major misconception that a lot of people have is that lookup tables are always faster than actually calculating a value, but cache locality is usually not given as much attention.
- cesarb 10y agoIt used to be true in the past. Back then, processors were slower, and they had no cache, since the processor and memory speeds were roughly matched. A few integer multiplications would be enough to cost more than a single memory access. It should still hold true today for small embedded processors with slow arithmetic and no cache.
- beached_whale 10y agoThey often lack ram too, so that const lookup table could be sitting on flash.
- davedx 10y agoThat's not my takeaway from the article. It's not saying don't use "slow scripting languages", it's saying avoid language features that are not safe for real-time programming and give the audio thread enough time to do its thing. In my experience, writing everything in C/C++ has its own host of problems. What's worse, a GC stutter or a segfault? :)
- friendzis 10y agoI will take segfault over GC stutter any time of the day, because I can at least debug and fix it. GC is not really predictable
- wrl 10y agoWriting everything in C/C++ has its own host of problems, but it's roughly the name of the game in anything realtime. It's easy enough to think about writing one effect or synth in a slower language, but keep in mind that modern digital audio workstation software is expected to run up to hundreds of plug-ins to produce one block of audio, and that's just not possible without being directly on top of the metal. Even vanilla C/C++ often isn't enough; generally, there's a necessity to drop down into assembler or at the very least use SSE intrinsics.
- neoeldex 10y agoRealtime often means: when there's not enough time, dont bother. Not going as fast as possible. Which are 2 separate issues. In my opinion, at least
- wrl 10y agoHow are they two separate issues? If you don't go as fast as possible, you can get less work done before your deadline. Even with today's CPUs and the kind of parallelisation that a good DAW program will do, I still routinely see producers (myself included) hit CPU usage limits. In audio DSP, programmers have responded to the increase in CPU capacity not with higher-level languages but instead with more computationally expensive algorithms (modern filters, for example, use techniques derived from circuit simulators for solving nonlinear equations). Besides, actually writing the DSP code makes up a comparatively tiny portion of the development time. Tuning and tweaking the DSP algorithms takes far more, and designing the user interface dwarfs both. I know multiple audio products which use Lua for their UI layer because the productivity increase is so significant. And there, you don't have to worry about performance to anywhere the degree you need to in the DSP.
- iammyIP 10y ago
- yosefk 10y agoI'd rather configure the FPU to flush denormals to zero. As to slow scripting languages - TFA was talking about determinism. A slowish scripting language could give you deterministic runtime, at least as deterministic as C or assembly (interpreted Forth is a simple example), and on the other hand there's no shortage of ways to get your C or C++ code to stall for an unknown amount of cycles (TFA enumerated some.) Of course slower languages are slower, and in a faster one you're more likely to get away with stalls because you have more slack to begin with.
- iammyIP 10y agoThat's new to me, wouldn't the OS be required to be deterministic to begin with, for a deterministic language to actually be deterministic? Since most OSes are not, the only thing left is raw speed (or dedicated dsp chips).
- squeaky-clean 10y ago> That's new to me, wouldn't the OS be required to be deterministic to begin with, for a deterministic language to actually be deterministic? Since most OSes are not, the only thing left is raw speed (or dedicated dsp chips). You can't control the determinism of the OS or user's environment, sure. But you can make sure you're not adding additional indeterminism. If the OS can't deliver stable timing, no audio app will under those circumstances. But for the times when it does, you don't want to be the only instrument popping and underrunning, while every other effect is glitch-free. Raw speed isn't really that great, because you can't process ahead of the present. If you need to provide a 128 sample buffer every 2.9 ms, it doesn't matter if a slow language takes 2.8ms, and a fast one takes 0.1ms. However, if every 10 seconds the fast language takes 10ms, you've lost 4 buffers! Consistent speed is the aim.
- marcosdumay 10y agoModern OSs have some amount of predictability. If the system is not overloaded or you renice the program, you can expect it to run every couple of ticks. If there's nothing big running in parallel and your data is small enough, you can expect it to stay in cache. If you do no disk access, you can expect it not to stall. It all depends on how much latency you can accept. With enough latency, you can bufferize over anything.
- pierrec 10y ago>do not use lookup tables, they unnecessarily fill the cache, cpus are fast enough Whoa, slow down. Testing will tell you which one is faster, but if you're applying a complicated function to every sample (eg. one that uses pow, log or trig especially more than once), lookup tables are definitely to be considered. The more complex the function, the more of a performance gain it will represent. An arithmetic approximation such as Chebyshev approximation can also work, but may or may not be more efficient depending on the function. And yes, make sure you optimize the size of your table to be as small as possible while maintaining acceptable quality (which also depends on the interpolation you're using to access it). Everything is a tradeoff. New processors are efficient but not magical, and I've seen a marked performance improvement after applying a lookup table on a small plugin a few months ago, all of this on a very modern i7.
- iammyIP 10y agoYou should not use standard pow and log but approximations of these functions. These are usually faster than any lookup table that fills a good part of the cache and then you stall. But there might be some gray area, depending on the system. Or when full precision is needed. In most audio cases approximations work very well. Atleast that was what my testing showed. edit: it might be different for a single plugin, where there isn't much need for cache, but as the core loop grows bigger, you also need the cache more.
- Razengan 10y ago> Whoa, slow down. An unfortunate suggestion, given the context. :) GP and others always mention using C/C++, but what about Rust? Is it viable [enough] for these kind of problems? For high-performance code in game engines etc.? Edit: Ah I should've searched: https://news.ycombinator.com/item?id=11908849 https://news.ycombinator.com/item?id=11908849
- vvanders 10y agoRust would be fine, biggest hurdle is getting all the various libraries that C/C++ already has access too. I was looking into audio modem stuff a while back for APRS and it seems like most of the linux audio libraries already have Rust equivalent wrappers.
- shams93 10y agoWith csound you have a mature, stable audio engine that works everywhere, even works for android, you can call it from various languages, while you can use it as a c/c++ library it has well supported libraries for java on android and objective c and swift on ios. http://csounds.com http://csounds.com. I used it for my psychoflute and moonsynth apps on android.
- vardump 10y ago> and function pointers Ok, everything else made sense, but why on earth would you use function pointers in performance critical code if you can avoid them at all. Same obviously goes for virtual methods.
- iammyIP 10y agoTo be flexible without if / else or switches. If i want a multi-oscillator that can produce different waveforms i use a pointer to different functions depending on the waveform i need. Same goes for different postprocessing / routing. I didn't actually profile this compared to a switch, but for me it's cleaner and more modular. If you got a static effect whose routing stays the same you don't need it of course. edit: i've never used a virtual method, but my impression is that performance-wise it's quite different to pointers to simple functions. most of my audio code is pod-structs to layout the data in the order that it gets acessed and standalone functions that then modify stuff in the structs. it's pretty barebones data oriented programming compared to the other extreme, some baroque meta virtualised oop architecture.
- vardump 10y agoRuntime codegen could make a big difference. You can dynamically build your processing pipes without overhead. No function pointers needed, total flexibility. Check JIT libraries (like GNU lightning) and LLVM. Also LuaJIT might be interesting. "llvmpipe", software rasterizer that uses LLVM for runtime codegen for shaders: http://www.mesa3d.org/llvmpipe.html http://www.mesa3d.org/llvmpipe.html
- iammyIP 10y agosounds intriguing and way more flexible, but the example is for video? i don't know anything about this, but if i had to guess i would say that any runtime code generation is worse performance wise compared to a mix of pod structs and function pointers to functions which are all known at compile time.
- 10y ago
- radarsat1 10y agoActually even C can be very much non-deterministic. You have to be careful with memory-related issues. free() and malloc() can stall your program quite drastically if you're not careful. So no, it's not just a matter of avoiding slow scripting languages. Plenty of "scripting languages" are used in audio, in fact, they just have different memory models. (See, supercollider, chuck, puredata)
- iammyIP 10y agoatleast puredata is written in c, i guess the others also to be similar under the hood.