I was looking at gRPC benchmarks the other day (https://github.com/LesnyRumcajs/grpc_bench/wiki/2021-08-30-b...) and 6 of the top 7 performing gRPC implementations on 3 core CPUS were on the JVM.
I was looking at gRPC benchmarks the other day (https://github.com/LesnyRumcajs/grpc_bench/wiki/2021-08-30-b...) and 6 of the top 7 performing gRPC implementations on 3 core CPUS were on the JVM.
The gRPC libraries are primarily developed by Google. Google pours most of its development resources on its core languages: Java, Javascript, Go, C++, and Python. Google then allocates a developer to port the implementation to other languages and platforms. C++, unlike Java, is not typically used for cloud development, so I don't think a lot of resources were allocated for the language.
You can see how the difference in implementation effort can impact performance by comparing dotnet_grpc (Microsoft's fork) against csharp_grpc (Google's original implementation). There's more than a sevenfold improvement in req/s in Microsoft's implementation in the 1 CPU server case (35070 vs 5337), outperforming nearly all the Java benchmarks.
Also, many of those top performing JVM implementations are the same code running under a different garbage collector. .NET has two GCs each with a parallel option, but we only see one benchmark using likely the slower GC (Workstation GC instead of ServerGC).
Scala still runs on the JVM.
I though most of Google's infrastructure (such as Borg or things like that) was in C++.
I think actually its the opposite, and the C++ implementation had seen most activity. Some other implementations (e.g. the node.js one, the initial version of .NET support, and also the rust_grpcio flavor) had been built on top of Googles C++ gRPC core library. I am not sure however if some of those now have moved to more native implementations recently, since I haven't followed the development closely.
Disclaimer: contributor to the gRPC Java benchs.
Say what you will about Java (the language), the JVM is a seriously good piece of kit.
It may lack a lot of the fancier features of some newer languages but its still a very effective language.
Coming from JavaScript, TypeScript, Python and Go back to Java I can tell you that’s actually a good thing.
Cleverness in code starts working against you as a code base and organizations scale.
What do you find magical in terms of Python in particular?
> What do you find magical in terms of Python in particular?
Lots of libraries are magical. SQLAlchemy, most of the data science libraries, even the standard library returns different types based on the values of inputs (e.g., open() returns a binary file or a text file depending on the string you pass into it). Additionally, people often go crazy with various dynamic typing features (rather than refactoring into a common interface, people will do a bunch of `if isinstance(...)`es all over the place. Similarly people tend to go crazy with metaclasses and so on. I could go on and on.
We have code like (stupid example)
something.map( {a, b, ...rest} => ({ ...rest, ...a, ...[b, c, d]}))
I know what it does and how it works, but I argue that's hard to understand if you haven't written it and know the context.My Python experience is limited to only a handful of projects, but I looked into meta classes at the beginning of learning Python and that was some crazy stuff.
What are some things thing you can do in Java with less code than in Javascript?
We also do not use XML to configure DI. Just add a annotation to a class and you can inject it automatically in any constructor without any additional config/annotations.
If this is too much "magic" for someone's taste, I will not argue even if I have a different view. But please do not make absolute statements based on a bad experience in some companies.
Modern Java and frameworks have evolved a lot in recent years.
I understand the need for DI, sometimes the order of instantiation is necessary but for everything else I get lost in the DI constructor injection stuff.
In a functional language you could use a reader or environment monad to abstract the dependencies, but you don’t have that ability in a language like Java (and it’s not worth torturing the language to do it because it’s not idiomatic). So DI in Java ends up constructor-injecting service dependencies and using the method parameters to declare data dependencies.
Edit: another benefit of DI is that it allows for multiple lifetime scopes beyond application and method call. Spring has request and transaction scopes, for instance. Managing all that in Java without DI is a nightmare.
Now, I can go ahead and make the concrete implementation dependent on configuration, e.g. by providing multiple implementations annotating them with a condition that is evaluated using the application configuration. I can also switch to a factory method that creates and sets up the concrete implementation for the interface, I have not to change any place in the application that is using that piece, though.
The same should be possible in Go with reflect and an IoC container, but I am not sure if there's a solid implementation for that out there.
"null checks are ugly and error handling is messy" are your opinions, not objective facts. I actually prefer Rust-like enums, but short of that, I think Go's error and nil handling seems quite a lot nicer in practice than Java's `Optional` facilities. Note that these are my opinions, and I'm not posing them as objective facts.
The rest of them I've been enthusiastic about for a few weeks until reality catches up with me and I realize what I am missing out on.
foo{ bar=true, baz=true }
Another thing I'd like to see is opt-in null safety. The Optional type is a very good mechanism, but when every reference is potentially null, its ability to enhance language safety is limited.
Other than that, it has everything I want. And of course, the ecosystem, tooling, etc. is simply the best in class.
My personal preference is for ML-style languages, so Java will never be my true love. But as far as languages go, you can do much worse.
Even then we won't have fully reified generics, but some of the stuff they're doing with type projections is insane. I'm pretty sure it's going to be a game changer.
If we're considering other JVM languages, Scala and Clojure are also on the table.
Additionally, Spring Boot exists and is lightweight and super easy to understand, jooQ is an ORM/query builder that makes Hibernate look like a toy (and it super simple).
Just because Java gets tainted by overengineering doesn't mean the language itself isn't simple.
Even using the built-in JDBC support is an option with modern Java, PreparedStatement and ResultSet implement AutoClosable, which means you can write code like this:
try (
final var rs = conn.prepareStatement("""
<SQL here>
""").executeQuery();
) {
while (rs.next()) {
// do your thing
}
} catch (SQLException e) {
// your code here
}
Which is just the standard library and no more verbose than most other frameworks.As for web frameworks, there's a million of them. Pick a non-magical one if you don't like magic.
Spring had good documentation that you can read on an ereader. It's also very mature, so there's lots of help on Stackoverflow.
Hibernate was a bit more difficult, but Vlad Mihalcea has improved the documentation side a lot.
Hibernate also allows you to drop down to native queries, which coupled with Spring Data's "projections" are useful.
Also, I think it is overblown how magical those annotations/code generation are. It is simply class path scanning and code generation under the hood, so that a proxy object is used instead of the interface. I would take this path any number of times instead of what abomination n developer hacked up over the years with no proper abstraction and documentation.
There are good things and bad ones across both of them.
Both also provide enough rant material. :)
You know that thing, which WebAssembly is trying to clone.
I've recently written a pretty compute-heavy app in .NET 5 that generated some garbage (mostly nursery stuff), and I tried to run it on a 16-core CPU.
The results were disappointing, even with the Server GC, mainly due to the fact that the GC still does a ton of stop-the-world pauses, and the amount of garbage basically scaled linearly with the number of threads.
My benchmarking results indicated that adding more than 8 threads were basically pointless, since the increased garbage meant that more time was spent in STW GC mode.
I 'fixed' the perf issue, by eliminating all GC allocations, which alone resulted in a 2x speedup per thread, and allowed basically perfect scaling across cores.
In the day and age where you can rent 64 core CPU VMs for $2/hour, I wish they would spend more engineering effort on fixing this.
You can see this in practice when following up which tech gets out and how it evolves across its lifetime.
I bet many teams before the whole C# 7 effort, would just write a couple of COM stuff in ATL, call them, from .NET and be done with it.
This is also why WinRT was designed the way it was, given that Windows Dev was driving it.