News for hackers. Stuff that matters.
Re:The Billion Row Challenge (1BRC) – Step-by-Step from 71s ... by scottlamb on Thursday February 22, 2024 @02:08PM (#39471571)
Fun read. I haven't used Java in a long time, so even the "idiomatic Java code that would pass muster with any seasoned Java developer" was a bit shocking.

I know it's a cliche that someone brings up Rust in any programming thread, but I can't help myself. ;-) Several of the techniques here are easy enough in Rust that I just do them routinely.

* Rayon makes it easy to process line-wise in reasonable-sized chunks without allocating a string per line.

* The standard integer parsing stuff takes a slice, not an owned string.

* The standard hash map doesn't have the same expectation "that we pass in an instance of the key class that is equal to an existing instance"; you can call HashMap<String>::get with a &str. (One limitation is that std::collections::HashMap::entry does expect a K, so there's some redundancy if you need to insert afterward, but you could drop to hashbrown::HashMap::raw_entry_mut to avoid that.)

Re:The Billion Row Challenge (1BRC) – Step-by-Step from 71s ... by haxen on Thursday February 22, 2024 @02:45PM (#39472058)
I actually wrote a Rust version as well, and yes, it was far easier to write, far less code (although not incorporating all the tricks), completely safe, and pretty fast -- but still 2x slower than my end result in Java.

HashMap was quite a bottleneck in Rust as well, for many reasons, not just the key allocation problem. But it was very easy to implement the same kind of custom hashtable, which almost by default ends up having a better memory layout than in Java.

https://github.com/mtopolnik/rust-1brc/blob/main/src/main.rs

I bet I could improve on it just a bit and match the Java time. Not sure about topping it, though.

Re:The Billion Row Challenge (1BRC) – Step-by-Step from 71s ... by scottlamb on Thursday February 22, 2024 @03:25PM (#39472678)
> HashMap was quite a bottleneck in Rust as well, for many reasons, not just the key allocation problem.

I'd be curious to hear more. Other than the default hash function being quite expensive, and occasionally wanting to use the more expressive hashbrown API, I've been happy with Rust's std::collections::HashMap.

Re:The Billion Row Challenge (1BRC) – Step-by-Step from 71s ... by haxen on Thursday February 22, 2024 @03:38PM (#39472855)
The reason the custom hashtable wins out isn't something generally applicable. For the very specific dataset used in the challenge, the hash function could be radically simplified, to just a single multiply and rotate left.

To be fair, I didn't try out the hashbrown API. Maybe that, together with FxHash, would have been enough for the official dataset with 97% of keys < 16 bytes. But, I was optimizing for the "10k" dataset as well, with a lot of longer keys, and hashing just the first 8 bytes was the winning idea.

Re:The Billion Row Challenge (1BRC) – Step-by-Step from 71s ... by scottlamb on Thursday February 22, 2024 @04:55PM (#39473845)
> For the very specific dataset used in the challenge, the hash function could be radically simplified, to just a single multiply and rotate left.

That's just a custom hash function, though; couldn't you do that with the standard std::collections::HashMap?

Re:The Billion Row Challenge (1BRC) – Step-by-Step from 71s ... by haxen on Thursday February 22, 2024 @05:29PM (#39474220)
I didn't bother to try, so not sure. There would probably be some challenges and I don't see how I'd accomplish it without some branch instruction in the custom hasher.
Re:The Billion Row Challenge (1BRC) – Step-by-Step from 71s ... by jasonjmcghee on Thursday February 22, 2024 @03:25PM (#39472677)
I thought everyone used hashbrown now

https://github.com/rust-lang/hashbrown

Re:The Billion Row Challenge (1BRC) – Step-by-Step from 71s ... by IshKebab on Thursday February 22, 2024 @04:17PM (#39473384)
They do because the standard library implementation was changed to use Hashbrown. However the standard library API has a slight limitation in that you can't get say "I have a reference to a string. If there's an entry for it, give me that. Otherwise clone the string and insert a new entry. Also only do one key lookup."

You end up either having to do two lookups or always cloning the string even if you ended up not needing it.

Re:The Billion Row Challenge (1BRC) – Step-by-Step from 71s ... by GrumpySloth on Friday February 23, 2024 @07:06AM (#39479571)
Re:The Billion Row Challenge (1BRC) – Step-by-Step from 71s ... by IshKebab on Friday February 23, 2024 @08:39AM (#39480427)
You can't. `entry()` takes they key by value, not by reference:

https://doc.rust-lang.org/std/collections/hash_map/struct.Ha...

You might be getting confused because in that example the `HashMap` keys are references.

Re:The Billion Row Challenge (1BRC) – Step-by-Step from 71s ... by GrumpySloth on Friday February 23, 2024 @09:01AM (#39480645)
My bad. That’s not been an issue in my code, as I use arenas to manage memory and never put an owning container as key in a HashMap.