anematode

Joined 2 years ago

Karma
766
Submissions
256
Member since
May 22
Opinions expressed here are solely my own. Website: https://anemato.de
View on Hacker News

Recent submissions

Comment · 7 days ago
The author claimed that it was an honest mistake, but looking closer at the original PR removing it, his explanation doesn't make sense.

Maybe what's happened is that their bots reward hacked their way into special-casing the benchmarks without a proper audit from the humans involved, and now they're doing vibed damage control....

Comment · 10 days ago
Well said.
Comment · 11 days ago
Hasn't Apple themselves partnered with Google? Why not switch to one of the Gemini coding offerings? (Yes, they may be inferior for now, but that's not guaranteed to persist.)
Comment · 13 days ago
Comment · 25 days ago
Dear lord that website is laggy
Comment · 1 month ago
lgtm!

... Oh by the way, did you know that Claude Code is actually a mini game engine? /s

Comment · 1 month ago
That makes very little sense when you can just use WebGPU and have fine-grained control...

for something like LLM inference, having this kind of abstraction seems like quite a hindrance

Comment · 2 months ago
Even worse, in a brownfield codebase that was once fairly light with comments, that's now being subject to these modifications, the insane amounts of commentary around the parts newly touched by AI lead to an excessive emphasis on those parts, for both human and AI readers (who think, well if this one part is commented so thoroughly, it must be unusually subtle)
Comment · 2 months ago
JSON. /s
Comment · 2 months ago
Yes, that's one approach. Another reasonable approach is to get out a mask from the comparison using `vmovmskpd` and use that to look up a shuffle constant, since there are only 16 possibilities. This also works well on NEON, although I wonder there whether it'd make more sense to find the shuffle dynamically rather than loading it.
Comment · 2 months ago
Great link, nice find!
Comment · 2 months ago
Good question. I personally doubt that the compress instruction is easy to coax compilers into generating, as there are many edge cases to consider.

For example, you'll notice here that we perform a full vector store of 8 elements unconditionally, even if only a few of the elements are active. This is safe, though, because the output buffer is as large as the input buffer, and we're chunking by 8, so we'll never trash memory past the end; but this is a tricky analysis. Performance-wise, we rely on the CPU's store buffer to make these overlapping stores cheap.

Instead, you might think that you could just store the elements which are actually active, using a masked store. In fact there is also an intrinsic for this purpose (_mm512_mask_compressstoreu_pd), but it is extremely slow on some CPUs, namely Zen 4, so it's dangerous to use unless you know exactly what CPU you're using. (In my testing, there also seems to be some weird hazard on Zen 5 where multiple memory-destination compress instructions to nearby, even non-overlapping, addresses are serialized. But I haven't looked closer at this.)

Comment · 2 months ago
Nice post!

You can do even a bit better if you're willing to use intrinsics. In particular this kind of operation is well-suited for compress-type operations, available as a first-class operation in at least AVX512, SVE and RVV; you can also emulate them reasonably quickly on NEON and AVX2.

Here's an example, building on the OP's work:

    pub fn filter_compress(input: &[f64], threshold: f64) -> Vec<f64> {
        use std::arch::x86_64::*;
    
        let mut out = vec![0.0; input.len()]; 
        let mut n = 0usize;
    
        let (head, tail) = input.as_chunks::<8>();
    
        for chunk in head {
            unsafe {
                let p = _mm512_loadu_pd(chunk.as_ptr());
                let m = _mm512_cmpnle_pd_mask(p, _mm512_set1_pd(threshold));
        
                let compress = _mm512_maskz_compress_pd(m, p); 
                _mm512_storeu_pd(out.as_mut_ptr().wrapping_add(n), compress);
                n += m.count_ones() as usize;
            }   
        }   
    
        for &x in tail {
            out[n] = x;
            n += (x > threshold) as usize;
        }   
        out.truncate(n);
        out 
    }
For me it's about 25% less time than the branchless version with 1,000,000 elements, and 60% less with 10,000 elements where memory bandwidth effects are less relevant.
Comment · 2 months ago
That's awesome :) Do you have any links to your guys' work that I could check out?

Live from the official Hacker News API

A modern reader for Hacker News · SvelteKit + Skeleton