HNHacker News
TopNewBestAskShowJobs

dzaima

1,748 karma · joined September 30, 2019

https://github.com/dzaima/

https://codeberg.org/dzaima/

submissionscomments
dzaima··on V8 Garbage Collector
A sanitizer or static analysis or any other tool can unconditionally give you a warning/error on signed integer overflow. Whereas that's invalid for unsigned integers as they have well-defined behavior, and things depend on said overflow (hashing, bitwise magic, temporary wrapping that unwraps later, etc).

Ideally there'd be a third type for unsigned-non-wrapping-integer (and llvm even supports a UB-on-unsigned-wrapping flag for arith ops in its IR that largely goes unused for C/C++), but alas such doesn't exist. Half-relatedly, this previously appeared as a discussion point on Linux (though Linus really did not like the concept of multiple unsigned types and as such it didn't go anywhere iirc).

dzaima··on The state of SIMD in Rust in 2025
Well, not really "you don't care about safe rounding behavior", more just "you have specified a specific operation order that happens to be more susceptible to being vectorizable". Implementing a float sum that way has the completely-safe completely-well-defined portable behavior of summing strides for any given size.

Both float multiplication and float addition are equally bad for optimizations though - both are non-associative: https://play.rust-lang.org/?version=stable&mode=debug&editio... ; and indeed changing the aligned-sum example to f64, neither .sum() nor .product() get vectorized.

And e.g. here's a plain rust loop autovectorizing both addition and multiplication (though of course not a reduction): https://rust.godbolt.org/z/6hEcj8zfx

dzaima··on The state of SIMD in Rust in 2025
For vectorizing, that quote is only true for loops with dependencies between iterations, e.g. summing a list of numbers (..that's basically the only case where this really matters).

For loops without such dependencies Rust should autovectorize just fine as with any other element type.

dzaima··on The state of SIMD in Rust in 2025
Scalable vectors as in RVV & SVE aren't available in rust corrently; see https://github.com/rust-lang/rust/issues/145052

(that said autovectorization should work, and fixed-width SIMD should map to RVV as best as possible, though of course missing out on perf if ran on wider-than-minimum hardware not on a native build)

dzaima··on The state of SIMD in Rust in 2025
You can indeed use intrinsics to make a SIMD library in plain safe stable rust today to some extent; that just isn't what core::simd does; rather, on the Rust-side it's all target-agnostic and LLVM (or whatever other backend) handles deciding how to lower any given op to the target architecture.

e.g. all core::simd addition ends up invoking the single function [1] which is then directly handled by rustc. But these architecture-agnostic intrinsics are unstable[2] (as they're only there as a building block for core::simd), and you can't manually use "#[rustc_intrinsic]" & co in stable rust either.

[1]: https://github.com/rust-lang/rust/blob/b01cc1cf01ed12adb2595...

[2]: https://github.com/rust-lang/rust/blob/b01cc1cf01ed12adb2595...

dzaima··on The state of SIMD in Rust in 2025
The nightly built-in core::simd makes use of a bunch of intrinsics to "implement" the SIMD ops (or, rather, directly delegate the implementation to LLVM which you otherwise cannot do from plain Rust), which are as much if not more volatile than core::simd itself (and also nightly-only).
dzaima··on The state of SIMD in Rust in 2025
Linux of course does have an interface for RISC-V extension probing via hwprobe. And there's a C interface[1] for probing that's OS-agnostic (though it's rather new).

[1]: https://github.com/riscv-non-isa/riscv-c-api-doc/blob/main/s...

dzaima··on State of Terminal Emulators in 2025: The Errant Champions
But that doesn't go into clipboard history. And severely restricts what you can do between copying and pasting in general (very importantly makes it a pain to do replace (i.e. select+(implicit-delete+)paste)) as any intermediate selection before pasting destroys your Primary Selection. And if you realize you did that, recopying takes manually reselecting the text, or the otherwise-never-used ctrl+insert to recopy, instead of just repeating the same old ctrl+c as you always do with the clipboard in any sane application.

Of course still a nice option (esp. in terminals where the proper copy/paste are nerfed and selecting for editing is annoyingly not a thing), but far from a replacement for the proper clipboard.

dzaima··on When O3 is 2x slower than O2
Screw handedness is something that's basically always fine either way if planned for, and with largely no benefit for either option, so standardizing that is easy enough.

But, like, even then, there's screw drive (JIS vs phillips vs pozidriv vs a ton more) to worry about with screws. (never mind the obvious aspects of diameter & length; and whatnot else I happen to not know about)

The significant thing I think is that such trivial impactless questions just do not exist for programming languages. Even like comment starting characters is a messy question - "//" nicely aligns with "/* */" using the slash char, but "#" is a char shorter and interpreted languages need to handle "#!" as a comment for shebangs anyways. And things only get more complicated from there.

And, unlike with physical things, it's trivial to work with non-standardized things in software. Not like you're gonna copy-paste arbitrary code from one language into another and just expect it's gonna work anyway (else we wouldn't have more than one language).

dzaima··on When O3 is 2x slower than O2
It's of course a matter of perspective, goals, and assumed knowledge. Is it choosing to not follow best practices, or leaving outdated practices with unfixable downsides behind? Is it inventing new things or instead actually just taking already-existing things that you just happened to not know about? Is it making arbitrary changes, or necessary ones for a useful goal? Is an extra bit of temporary pain for existing engineers worth making things better for all future engineers? (not to say that Rust has necessarily made all the right decisions; but alas basically nothing is perfect, and things certainly won't get better if noone tries)
dzaima··on "Return YouTube Dislike" Chrome Extension Injecting Ads
Unfortunately, nice as it would be, your level of perfectionism is unfortunately not particularly common; indeed, it's possible to do things much more efficiently, but for most purposes "it works" is enough; and when it starts to not be you already have terabytes of db and just adding more disk is much easier than the hassle of migrating the entire thing to something different.
dzaima··on "Return YouTube Dislike" Chrome Extension Injecting Ads
That'd easily result in accidental re-votes if a user watches the same video every couple months (and such re-watchers would likely be ones that.. like the video, thereby skewing data away from dislikes over time).

Especially if the extension just sends the vote status instead of only reacting on a press (which'd allow it to send forward a vote done originally on, say, mobile; don't know if it does this, but it seems like a useful thing to do).

dzaima··on "Return YouTube Dislike" Chrome Extension Injecting Ads
That's assuming all of those are stored in packed formats, but even then it's not that low.

I already mentioned that the user IDs are 36 base64 chars, or 27 bytes if you store them max-packed; YT video IDs are 11 base64 chars, so 66 bits, doesn't quite fit in 8 bytes (not to mention that trying to pack the video IDs would mean your db becomes useless if youtube suddenly added a new video ID format). IP needs another bit somewhere for ipv4 vs ipv6, so likely 24 bytes (or just a string could be used).

Then you have some overhead for padding and string field lengths, and whatever overhead for packing the data for the disk storage (padding for all entires to stay within a page? maintaining a percentage of free space to ensure the B-tree property?). Then you have a copy of the fields in indexes, with whatever overhead those come with.

Granted, even that's probably around 200 bytes, not 500, in a reasonable db, but who's to say that the db used used is a reasonable & well-configured one; of course it's possible that a bunch more metadata is stored for user trustworthiness statistics or something, or duplicated tables where relations would work.

dzaima··on "Learn APL" Notes
Custom encodings, as was standard (or, well, mandatory) before Unicode (1991). Hell, Dyalog APL to this day supports its classic 1-byte-per-char encoding (not even ASCII-compatible! Nor EBCDIC!) in addition to Unicode.

Looks like the APL chars were added in Uncicode 1.1 (1993), two years after 1.0, which is quick enough.

dzaima··on "Return YouTube Dislike" Chrome Extension Injecting Ads
Also a user ID, which seems to be 36 base64 characters (can't have one user count for multiple votes).

Round up to 500 raw bytes per row (perhaps including time/ip and other random garbage, plus indexes), 3x replication/redundancy or something, for 6 million users each having voted on 500 videos, and you're at 6TB; still some ways off from 15TB, but not insurmountably far.

(votes/user is rather tricky to get; but, as a bit of random garbage statistics math: YT gets ~5B views/day and has ~3B users; 6M downloads of the extension means ~0.2% of users use it, so 10M extension-user views/day = 15B over 4 years, or 2.5K/user; assuming 20% vote rate (rather high but lets say extension users care more for voting and/or watch YT more than an average person), that's 500 votes/user)

dzaima··on "Return YouTube Dislike" Chrome Extension Injecting Ads
Video creators can freely delete comments, which makes relying on them for much sketchy.
dzaima··on Don't Force Your LLM to Write Terse [Q/Kdb] Code: An Information Theory Argument
I'd perhaps generalize that to "it's useful to have visual grouping correlate with semantic grouping"; applies to separating words with spaces, sentences with punctuation, paragraphs with newlines, lines of code, UIs, and much more.

An important question for this also is what qualifies for "single thing"; you can look at a "for (int i = 0; i < n; i++) sum += arr[i]" as like 5 or more things (separate parts), 2 things (loop, body), or just one thing ("sum"). What array languages enable is squeezing quite a bit into the space of "single thing" (though I personally don't go as far as the popular k examples of ultra-terseness).

dzaima··on Don't Force Your LLM to Write Terse [Q/Kdb] Code: An Information Theory Argument
Unless the interpreter is capable of pattern-recognizing that whole pattern, that will be less efficient, e.g. having to work with 16-bit integers for x in the range 128..32767, whereas the direct version can construct the array directly (i.e. one byte or bit per element depending on whether kdb has bit booleans). Can't provide timings for kdb for licensing reasons, but here's Dyalog APL and CBQN doing the same thing, showing the fast version at 3.7x and 10.5x faster respectively: https://dzaima.github.io/paste/#0U1bmUlaOVncM8FGP5VIAg0e9cxV...
dzaima··on Mysterious Intrigue Around an x86 "Corporate Entity Other Than Intel/AMD"
If the scalable VLEN is the same as the ALU width, which should generally be the target, small vectors would still perform optimally.

Of course if you need less than a VLEN-sized vector you're wasting throughput, but that applies just as much when using 128-bit vectors on AVX-capable hardware, and even worse so on AVX-512-capable (which, while double-pumped or equivalent to some extent on most impls, still has 512-bit-exclusive throughput on most).

dzaima··on Mysterious Intrigue Around an x86 "Corporate Entity Other Than Intel/AMD"
And even that only helps with the length problem, and doesn't help with doing new operations.

For SIMD, baseline x86-64 (i.e. SSE + SSE2) didn't have dynamic shuffles & shifts & blend, float floor/ceil, integer conversions & min/max & 64-bit comparisons & 32-bit mul, just to name things useful for even very boring SIMD; then in AVX2 we also get gather/masked load/store, FMA, and in AVX-512 we get a bunch of neat mask stuff, integer narrowing & rotates, compress.

(much of those things RVV has in its base extension, but RISC-V already has a good number of extensions on top of base RVV for things like like float16/bfloat16, expanded bitwise stuff (Zvbb - rotates/popcount/lzcnt/widening shift), clmul, and a bunch of crypto things; and presumably in a decade there'll be a bunch more things that people will want in their CPUs that'll have no choice but to be new extensions)

dzaima··on Mysterious Intrigue Around an x86 "Corporate Entity Other Than Intel/AMD"
SSE3+ & AVX{,2,-512} & co improve on SSE in pretty much the same way that x86-64 improves on x86 - the old thing still works just fine, but the new one is wider, adds new (very useful!) instructions, doesn't copy over others, and (at least partly) uses different encodings.

And an important thing to remember is that there is and never as a single "x86" before x86-64; both Intel and AMD added new instructions as was seen useful in new generations. AVX & co just continue the pattern that's been going on for four decades.

dzaima··on Garbage collection for Rust: The finalizer frontier
Same as with GC, neither need be a fixed choice; having a GC library/feature in Rust wouldn't mean that everything will be and must be GC'd; and it's still possible to add unleakable types were it desired: https://rust-lang.github.io/keyword-generics-initiative/eval... while keeping neat things like Rc<T> available for things that don't care. (things get more messy when considering defaults and composability with existing libraries, but I'd say that such issues shouldn't prevent the existence of the options themselves)
dzaima··on RISC-V Conditional Moves
dav1d is still in the "some very-important software" group; the vast vast majority of software doesn't and will not write everything in assembly. If you think RISC-V is gonna in any way change that, you're.. just trivially simply plain wrong, 99.999% of people will not bother learning assembly regardless of how good of an idea you think that'd be. (never mind that even if more people learned assembly, 99.999999% of said assembly would be full of system-killing bugs)

RVA23 doesn't in any way ease porting, no clue what you're on about there; besides vectorizable code, where you necessarily do simply just need RVV or similar to get good performance, base RV64G does just cover everything needed for software to be able to run. All RVA23 does is just provide a baseline with extensions to be able to achieve good performance, and cheap instructions that software should've been already utilizing for decades but hasn't generally been able to due to legacy hardware not supporting them (importantly clz/ctz/cpop, but also to a smaller extent min/max, zicond, bit rotates).

Granted, RVA23 does have some more questionable (though still actually useful) inclusions like the cache block ones that force 64-byte cache lines to not be dysfunctional, but that also brings up the massive wart of mandatory 4K pages in base RISC-V, not even RVA23, that's explicitly chosen for ease-of-portability.

Maybe RV64 is the end-of-line for CPUs, but, for all we know, RV64 today might be what Intel 8086 was in 1978, and RV64 is to be extended and grown and eventually just replaced in the future.

dzaima··on Ladybird passes the Apple 90% threshold on web-platform-tests
While it is kinda unfortunate to have one unbalanced test suite as the major external progress indicator, there are.. like no other good options that don't involve someone manually going through like the top 1000 sites or something and checking whether they look good. That leaves having no progress indication whatsoever, which is also pretty bad.

And, in any case, implementing more of the standards is just simply good, and would need to be done at some point anyway.

dzaima··on 86 GB/s bitpacking with ARM SIMD (single thread)
More specifically, basically the only absent 8-bit ops that have 32-bit equivalents in AVX2 are shifts and multiplies. Shifts are quite annoying (though, with a uniform shift they can be emulated on AVX-512 via GFNI abuse in 1 instr), multiplies are rather rare (though note that there is vpmaddubsw for an 8-bit→16-bit multiply-add). There's even a case of the opposite - saturating add/sub exist for 8-bit and 16-bit ints, but not wider.
dzaima··on RISC-V Conditional Moves
> Nowadays, programs requiring floating point hardware acceleration for reasonable performance use vector machine instructions.

While some very-important software like video codecs, and various sporadic projects where some drive-by open-source dev decided to add an optimized path will use vector, that's, like, on the order of 0.001% (number out of my ass) of all software that runs slowly enough to be noticable, and the remaining 99.999% remains slow. Much as I like working with SIMD/vector, it's a very tiny minority of people that do.

RVV does also actually make good use of scalar FP, with .vf instruction variants which take one operand from the scalar registers, allowing storing constants in the scalar registers instead of wasting vector registers (and with LMUL it's very easy to exhaust the entire RVV register file). Especially important in matmul kernels.

> What I am very sure of: nobody wants to design a clean and modern ISA to handle the "bad" programs

And yet that's what RVA23 & co basically have to be, and are. And as such they have scalar FP, vector FP, and basically everything else that's potentially useful (other than 3-source-operand instructions).

I do wonder how much compressed actually benefits perf-wise, but it's very clearly true that, at least icache-wise, reducing code size by, say, 20%, is equivalent to adding 20% more icache; and 20% of a typical L1 icache is quite a lot of area to save.

dzaima··on RISC-V Conditional Moves
Avoiding flags is indeed a decision backed by reason; but to do so, you don't necessarily need to have `beq a0, a1, label`, you can just do `xor t0, a0, a1; beqz t0, label`. Having full `beq` instead of just `beqz` is exactly as unnecessary as `sh3add` from Zba, except some mild difference in frequency of those, depending on codebase. Having just beqz would even have the benefit that the label could be 17-bit instead of 12-bit!

Indeed, most sane software doesn't need most extensions to be "reasonably performant"; in fact, most sane software is reasonably-performant even on two decades old hardware!

But, unfortunately, there's a ton of software doing things quite inefficiently, and it will continue to exist forever unless something crazy happens like a non-insignificant amount of humans starting to care (impossible) or LLMs becoming functional enough to rewrite entire codebases (more possible than humans caring, at least).

You're extremely-heavily underestimating software doing random garbage in floating point (using it to compute a square root or multiplying an integer by 0.4 or something; ad-hoc game logic/physics that isn't written in a vectorizable way; doing a bunch of things where integers would do in FP (esp. languages which expose floats as the main datatype, esp. JavaScript))

It may be neat to dream about a hypothetical world where none of that garbage exists, but that dream isn't coming true today, nor is there any sign that it will at any point in the future. Basing architecture/compiler/configuration decisions around this hypothetical is just purely entirely stupid.

And even in that dream world a lot of code would benefit from sh1add/sh2add/sh3add from Zba, Zbb's min/max is useful in a ton of places, memory managers might want clz for computing bucket from size, anything doing bitwise stuff would benefit from andn and much of Zbs. And of course ideally the vast majority of code would be running in RVV instead of scalar code.

dzaima··on RISC-V Conditional Moves
There's not much sign that RISC-V will be extremely-fusion-focused; indeed it'd be good for the base ISA, but Zba, Zbb, Zicond add a bunch of common patterns as distinct instructions, and things often fused on other architectures (compare + branch) is a single instruction in even the base RV64I. That largely leaves fusing constant computation as a fusable thing, and.. that's kinda it, to achieve what current x86 & ARM cores do. (there's then of course doing crazier things like fusing multiple bitwise/arith ops together, but at that point having a too-minimal base ISA comes back to bite you again, meaning that some should-be-cheap fusions would actually need to fuse ≥3 instrs instead of just two)

In any case, "force hardware to do an extremely-stupid amount of fusion to get back the performance lost from intentionally not adding/using useful instructions" isn't a sane thing to target in any universe no matter ones goals; you're just wasting silicon & hardware development time that would be better spent actually doing useful things. Fusion is neat (esp. for fixing past mistakes or working around fixed-size instructions (i.e. all x86 & ARM use fusion for, but a from-scratch designed ISA with variable-length instrs (e.g. RISC-V) should need neither)), but it's still very unquestionably strictly worse than just having more actual instructions and using them.

dzaima··on RISC-V Conditional Moves
gcc, via -m32, still defaults to no SSE, i.e. pre-1999, i.e. ≥26 years and counting for increasing default.

clang, as far back as Compiler Explorer goes (i.e. clang 3.0.0, i.e. 2011), always assumes SSE for -m32; presumably because there's nothing to be backwards-compatible to, unlike gcc.

Doesn't look particularly good for "default will just change at some point", though we can hope.

dzaima··on RISC-V Conditional Moves
"3R1W integer pipeline" is kinda ambiguous; I think it'd be extremely-stupid for any core to have all their ALUs be 3R. Much more sane is having ~half be such (if even that), and the rest at 2R.

Or, better yet, have the 3R extra port come from some of the 2R being split up; e.g. for a block of 3×2R1W ALUs, be able to split one up for its read ports, reusing it as 2×3R1W when needed, thereby being able to do 3R1W at 66% the throughput of 2R1W without any extra register ports (i.e. 1.3x throughput benefit of 3R1W over two 2R1W instrs). Probably has some extra costs from scheduling & co needing to handle 3R though.

← PreviousPage 5 of 32Next →