4 ms·
I think in general these types of benchmarks makes boxed numbers look bad only because they're being compared to adding integers, which is a ridiculously fast o
by marginalia_nu 4y ago
I think in general these types of benchmarks makes boxed numbers look bad only because they're being compared to adding integers, which is a ridiculously fast operation to start with. Accomplishing half that speed despite "dereferencing a pointer" is kinda insane in the light of 'Latency Numbers Every Programmer Should Know'.
(The reason it works is because Java doesn't actually allocate new Longs for small numbers, it fetches them from a table of constants; it's always the same 256 objects that are being dereferenced. I don't know their memory layout, but I'd half expect them to be sequential in memory as that's would be much a low hanging fruit optimization. Optional<Long>'s performance is what you'd expect without these optimizations. Also in this scenario you really should use OptionalLong instead of Optional<Long> but that's beside the point ;-)
- ramblerman 4y agoThe table of constants leads to this fun example: System.out.println(Integer.valueOf(22) == Integer.valueOf(22)); // true System.out.println(Integer.valueOf(2200) == Integer.valueOf(2200)); // false Which is a bit confusing to say the least. I realize one should never be using == with objects, but still.
- matsemann 4y agoThat's another quality of life improvement in Kotlin, == calls the equals method on the objects, not comparing their references. Which is what most java programmers want in 99% of cases. And makes the code more readable, a.equals(b) is harder to read than a == b.
- marginalia_nu 4y agoIn most cases I'd use equals(a, b) in Java (with a static import of Objects.equals). It's both null safe and more readable.
- bzzzt 4y agoI kind of like that behaviour. You're comparing object references and get exactly the expected output instead of some magically overloaded equality operator. If you want that, just use 'equals' ;)
- n4r9 4y agoSimilar thing happens in Python (assuming my knowledge hasn't gone out of date) x = 2 print(x is 2) // true x = 200 print(x is 200) // false
- masklinn 4y agoI think a big difference is Java you have to switch between `==` and `Object.equals` depending whether your type is a primitive (e.g. int) or a reference (e.g. Integer). In Python the cases where you’d be using `is` are a lot more restricted, and a bit of an optimisation (although most style guides will require it in the cases where it makes sense). There’s basically 3 cases where you’d use `is` in Python: - the standard singletons None, True, and False (and for the latter two you’d usually use truthiness anyway) - placeholder singletons (e.g. for default values when you need something other than None) - actual identity check between arbitrary objects (which very rarely happens IME)
- kaba0 4y agoThis will also be “fixed” once Valhalla arrives.
- weego 4y agoMoreso, non-highly specialised use cases for optionals in this class of language are mostly commonly used around IO ops of some kind (DB, streams, messaging, API etc) so we're into "make sure there's no flies on the elephant when we weight it" territory.
- jillesvangurp 4y agoI think the real issue is that you probably should worry about this overhead only in the context of really tight loops where you are basically wasting a lot of memory allocation and garbage collection for no good reason. But otherwise, Java is actually pretty good dealing with short lived small objects. The garbage collector is pretty good for that. Normal usage of this stuff is not going to cause any more issues than trigger the OCD of people that obsess about this stuff. And in the rare case that you do have an issue, you can do something more optimal indeed.
- pkolaczk 4y agoIf you're writing a database engine, and you accidentally wrap each table cell in a Java object, you'll very quickly hit a GC performance wall, particularly when using a low pause GC. So we try to write Java without objects. Guess how easy that is. ;)
- marginalia_nu 4y agoYeah I deal with that a lot with my search engine index too. Honestly it's not that bad once you get used to it. You can get away with referencing the data through (mutable and reusable) pointer objects that reference memory mapped areas yet provide a relatively comfortable higher level interface. This gets rid of object churn while keeping a relatively sane interface.
- CHY872 4y agoYes, this is the way. Surprising how high level you can make this work - I’ve seen Collection implementations and all sorts.
- tsss 4y agoCan you give an example how to do that? I've only seen IO and parsing libraries that copy around byte arrays, but you can't cast byte arrays to arbitrary objects, which means you usually have to manipulate the bytes directly (unless you're lucky to work with only strings). At that point I imagine it would be far easier to just use C++ or Rust.
- jnellis 4y agoThe cached range is -128 to 127. I just reran these benchmarks with ints instead of longs and the mask changed to 127(0x7F) and sumNulls and sumSimple results are exactly the same: 0.6 ns/op. As for the sumOptional method, changing Optional<Integer> for the primitive variant OptionalInt, doesn't change the result much, its the actual creation of the Optional object itself that dominates the time. In nutshell, on my old i5 2500k: ints 0.6ns/op cached Integer 0.6ns/op boxed Integer 1.3ns/op OptionalInt 3.5ns/op Optional<Integer> 4.2ns/op (time includes boxing the int) Where an op is getting the number, checking it, then an addition. For hot loops inside a jmh benchmark, you can use @OperationsPerInvocation(MAX) and it will spit out the results in this more readable format for the time just inside the loop.
- Groxx 4y agoBenchmark structure is one thing I hope more languages copy from Go - hard-coding iterations is pretty silly, doomed to need repeated changes or become problematically imprecise as hardware and runtime changes occur.