Today we're shining the indie Sega Dreamcast development spotlight on @8bitProdigy 's new raylib-backed, Faceball 2000-inspired engine, Face-Off! Here's a direct hardware capture of it running on my DC.
Not only is it quite impressive that a first-time DC homebrewer was able to get such an engine up and running so quickly at solid 60fps (it started out on Linux), but his codebase also serves as an extremely well-written, clean example of Dreamcast development with raylib and the C programming language.
Despite what it looks like, the engine is technically NOT a raycaster, as he's determining visibility through a recursive portal algorithm. He's also just finished implementing proper collision detection, and it also compiles natively for Linux, for easy testing/debugging.
In-game objects and their behaviors are written with clean, object-oriented C code (I know, SO controversial), demonstrating how to elegantly implement the 3 main-pillars of OO: encapsulation, inheritance, and polymorphism, without built-in language support for any of them.
He also does linked lists CORRECTLY (I know, even more controversy) by making them intrusive and circularly linked, as in the Linux kernel.
Source code can be found here:
https://t.co/bF6onsqwaB
My digital book "Understanding the Odin Programming Language" is OUT NOW! ✨
If you want to learn Odin and demystify low-level programming, then this book is for you!
Read more or buy at: https://t.co/No76UDZrQs
📢 It's here! The second edition of my book "Performance Analysis and Tuning on Modern CPUs" is out NOW!
Dive into low-level optimizations and learn to write fast code like a pro.
Grab your copy: https://t.co/E5wGVuuV36
Please RT to spread the word!
#NewBook#BookRelease
🧵1/3
If you don't know how to do a removal of a node from a singly linked list like this, you don't understand pointers well enough (with explicit derefs to be clearer)
p := &list
for p^ != node { p = &p^^.next }
p^ = node^.next
Mnist neural network visualization written from scratch in Odin and Raylib.
repo:
https://t.co/fn6yjWWHmS
I'm still new to Odin so if you see a way to imporve something, let me know :)
Exploiting the power of the multi-core and many-core processors requires writing parallel algorithms and data structures. Moreover, the implementation also needs to be cache friendly. But these aspects are typically not covered in the introductory courses.
I came across this book (freely available online) which covers sequential algorithms and data structures (which you learn in school), and then extends the discussion to talk about how cache friendly the implementation is, and then also covers the parallel implementations of those data structures.
Every year the Oregon Programming Language Summer School covers advanced topics in programming languages, such as type systems, formal verification, category theory, proof theory.
Most of the lectures are recorded and available online (link in the reply).
Scan HTML even faster with SIMD instructions (C++ and C#)
Earlier this year, both major Web engines (WebKit/Safari and Chromium/Chrome/Edge/Brave) accelerated HTML parsing using SIMD instructions. These ‘SIMD’ instructions are special instructions that are present in all our processors that can process multiple bytes at once (e.g., 16 bytes).
The problem that WebKit and Chromium solve is to jump to the next target character as fast as possible: one of <, &, \r and \0. In C++, you can use std::find_first_of for this problem whereas in C#, you might use the SearchValues class. But we can do better with a bit of extra work.
The WebKit and Chromium engineers use vectorized classification (Langdale and Lemire, 2019): we load blocks of bytes and identify the characters you seek using a few instructions. Then we identify the first such character loaded, if any. If no target character is found, we just move forward and load another block of characters. I reviewed the new methods from the Web engines in C++ and C# along with minor optimizations. The results are good, and it suggests that WebKit and Chromium engineers were right to adopt this optimization.
But can we do better?
Let us consider an example to see how the WebKit/Chromium approach works. Suppose that my HTML file is as follows:
<!doctype html><html itemscope="" itemtype="http://schema.o'...
We load the first 16 bytes…
<!doctype html><
We find the target characters: the first character and the 16th character (<!doctype html><). We move to the first index. Later we start again from this point, this time loading slightly more data…
!doctype html><h
And this time, we will identify the 15th character as a target character, and move there.
Can you see why it is potentially wasteful? We have loaded the same data twice, and identified the same character again as a target character.
Instead, we can adopt an approach similar to that used by systems like simdjson. We load non-overlapping blocks of 64 bytes. Each block of 64 bytes is turned into a 64-bit register where each bit in the word correspond to a loaded character. If the character is a match, then the corresponding bit is set to 1. The computed 64-bit word serves as an index for the 64 characters. Once we have used it up, we can load another block and so forth.
Let us build a small C++ structure to solve this problem. We focus on ARM NEON, but the concept is general. ARM NEON is available on your mobile phone, on some servers and on your macBook.
Our constructor initializes the neon_match64 object with a character range defined by start and end. We define three public methods:
get(): Returns a pointer to the current position within the character range.
consume(): Increments the offset and shifts the matches value right by 1.
advance(): Advances the position within the range
To iterate over the target characters, we might use the class as follows:
The class code might look like the following:
The most complicated function is the update function because ARM NEON makes it a bit difficult. We use use vld1q_u8(buffer) loads 16 bytes (128 bits) of data from the memory location pointed to by buffer into the data1 variable. Similarly, data2, data3, and data4 load subsequent 16-byte chunks from buffer + 16, buffer + 32, and buffer + 48, respectively. The expression vandq_u8(data1, v0f) performs a bitwise AND operation between data1 and a vector v0f (which contains the value 0xF in each lane). The expression vqtbl1q_u8(low_nibble_mask, ...) uses the low_nibble_mask vector to permute the low nibbles of the result of the AND operation. The result is stored in lowpart1, lowpart2, lowpart3, and lowpart4. The expressionvceqq_u8(lowpart1, data1): Compares each lane of lowpart1 with data1. If equal, the corresponding lane in the result is set to all ones (0xFF). They correspond to target characters. You repeat the same computation four times. We then use bitwise AND with bit_mask(...) followed by several applications of pairwise sums vpaddq_u8(...) to compute the 64-bit word.
I have added this C++ structure to an existing benchmark. Furthermore, I have ported it to C# for good measure.
How well does it do?
I use a C++ small benchmark where I scan the HTML of the Google home page. I run the benchmark on my Apple M2 laptop (LLVM 15).
The number speaks for themselves: the 64-bit vectorized classification approach is the clear winner. It requires few instructions and it allows retiring many instructions per cycle on average.
What about C#?
Again, the 64-bit vectorized-classification approach is the clear winner. The SearchValues class provided by .NET gets an honorable mention for its good performance.
I am not exactly sure why the C# code is running at half the speed of the corresponding C++ code in this instance. But 15 GB/s is already fantastic.
I have not yet ported the code for x64 processors, but I expect equally good results.
*Alice goes to a differentiable wonderland!* 🔥
I published a short free book on the design of neural networks, from convolutions to transformers, SSMs, and a few other topics.
As a bonus, I tried to make it looking nice - any feedback is appreciated! https://t.co/fBVqrTofug
Last Saturday I talked at @h2hconference and shared the results of working with the Rust community to add LLVM CFI and cross-language LLVM CFI to the Rust compiler...
Wrote up a more detailed "6.10 io_uring" changes than what the pull requests generally contain, and more geared towards actual users. I'll make more of an effort at doing this for every new release. Find it here:
https://t.co/l7OTPNxVze
Feedback appreciated!
RELEASE DAY
After almost 10 years of hard work, tireless research, and a dive deep into the kernels of computer science, I finally realized a dream: running a high-level language on GPUs. And I'm giving it to the world!
Bend compiles modern programming features, including:
- Lambdas with full closure support
- Unrestricted recursion and loops
- Fast object allocations of all kinds
- Folds, ADTs, continuations and much more
To HVM2, a new runtime capable of spreading that workload across 1000's of cores, in a thread-safe, low-overhead fashion. As a result, we finally have a true high-level language that runs natively on GPUs!
Here's a quick demo:
More ALGOL68 fun. Here is an example from Wikipedia. Note how with a few different syntax changes, this could literally be any modern imperative language.
In fact, each line could become valid Odin code excluding the scope-capture nested procedure "no multiple".