Optimising for Concurrency: Comparing the BEAM and JVM virtual machines
81–90 of 114 posts
Re: Optimising for Concurrency: Comparing the BEAM and JVM virtual machines
#82Earlier quoted context omitted.
Per thread GC is definitely a different approach than Java takes. The trade-off is that shared memory between Java threads is nearly free. Basically the same approach C++ uses, except Java has better concurrency primitives because its VM. Not sure about Erlang but data sharing between processes on JS and Python is very expensive and a frequent criticism of those languages. You can achieve zero garbage per request in…
In Erlang processes need to send messages to each other. And those messages are copies (nothing is shared). This is less efficient than in Java where everything is shared, but it also means that process a cannot change something that process b is looking at. So locks in Erlang, aren't necessary. It also enables easy distribution. When all processes share data by messaging, it doesn't matter if those processes are run…
Erlang's model with fibers and message passing sounds close to Golang. Java has decent support for immutable objects with immutable collections, Lombok, the FreeBuilder library, both build-time code generators, and Java 14 record types. Automatic passing between machines is unique to Erlang
Per process GC isn't anything like Java does, but the new GC's are probably fast enough that it doesn't matter in practice. For any sane sized heaps the GC pauses are around 0.5 millisecond. This wasn't true until a few years ago, and in production most people don't know or care enough to use the new GC's.
You are right about thread safety in objects. Thankfully the JDK surface is fully documented. Third party libraries usually are. Internal code is a crapshoot. It requires discipline, but I still find it rare in practice because the normal patterns lend themselves to thread safety.
I think its safe to say that Java is a lower level language than Erlang which enables many of the same patterns with less convenience. You can probably get better performance with Java, but your fault tolerance completely depends on how good your coders are. Java will not save you from doing stupid things between threads.
Re: Optimising for Concurrency: Comparing the BEAM and JVM virtual machines
#83the author leaves out Kotlin which adds support for coroutines on the language level and still compiles to java bytecode. These are not classic continuations because they cannot be cancelled, but they're still very useful and true fibers. There's also the Quasar library that adds fiber support to existing Java projects, but its mostly unmaintained since the maintainers were pulled in to work on Project Loom. Then the…
Fair point - all this is true. As a counter-point, I've been working on a platform for the last few years which uses Kotlin and Quasar in production. Quasar was cool at first but now it's just a nightmare and I wish we never opted to use it. It leaks abstractions all over the place with @Suspendable annotations and users of the platform find the quasar related errors super confusing. Debugging is also very difficult…
Why didn't you use Kotlin coroutines? My understanding is that they achieve the same as Quasar without the insanity.
You may also want to look at Vert.X. Its evolved into a lot more than a REST framework. It uses thread-per-core and nonblocking to achieve high performance instead of green threads. It theoretically performs better because there's not a lot of stacks hanging around and only 1 thread per core. There's a lot of callbacks though, so if you're not used to RxJava style chaining its hard to get used to. Its very much like Node.
Erlang or Go would be the easiest if you need a lot of threads. If you just need high performance with a lot of connections, Vert.X may suffice. Java IO in recent years is fully non-blocking so you don't need a lot of threads for high concurrency. Vert.X can handle millions of concurrent clients, enough that you will need to adjust your kernel to hit its limits. And its built on Netty which is rock solid.
Re: Optimising for Concurrency: Comparing the BEAM and JVM virtual machines
#84the author leaves out Kotlin which adds support for coroutines on the language level and still compiles to java bytecode. These are not classic continuations because they cannot be cancelled, but they're still very useful and true fibers. There's also the Quasar library that adds fiber support to existing Java projects, but its mostly unmaintained since the maintainers were pulled in to work on Project Loom. Then the…
Non-preemptive concurrency doesn't invalidate any argument. Erlang's GC is per user thread, even a primitive GC per user thread will have lower latency than Java's GC.
Java's new garbage collectors, ZGC and Shenandoah, have average pause times of 0.3 milliseconds on heaps less than 4GB. I find it unlikely that another language has pause times shorter than that given the sheer amount of work put into Java GC over the years
Re: Optimising for Concurrency: Comparing the BEAM and JVM virtual machines
#85Re: Optimising for Concurrency: Comparing the BEAM and JVM virtual machines
#86Earlier quoted context omitted.
So I describe the shortcomings of CompletableFutures, and you point out that there's a java.util.concurrent package? Is this method one of its gems? A cancel() which doesn't cancel? https://docs.oracle.com/javase/8/docs/api/java/util/concurre... > Synchronized primitives don't compose. You can safely `synchronized get(...)` and safely `synchronized put(...)`. But their composition put(get(...)+1) isn't synchronized.…
What do you mean the cancel method doesn't cancel ? It does cancel with a CancellationException stored in the future and an optional interrupt send to the thread running the blocking operation.
Try it.
@Test
public void cannotCancelARunningFuture() {
// Future that just sleeps
CompletableFuture sleeping =
CompletableFuture.supplyAsync(() -> {
int i = 0;
while (true) {
System.out.println("zzzz: " + i++);
threadSleep(300);
}
});
// Make sure it started
threadSleep(1000);
sleeping.cancel(true);
System.out.println("Sleep was cancelled");
threadSleep(1000);
System.out.println("Haha no it wasn't");
}
=============================== zzzz: 0
zzzz: 1
zzzz: 2
zzzz: 3
Sleep was cancelled
zzzz: 4
zzzz: 5
zzzz: 6
Haha no it wasn'tRe: Optimising for Concurrency: Comparing the BEAM and JVM virtual machines
#87Earlier quoted context omitted.
So I describe the shortcomings of CompletableFutures, and you point out that there's a java.util.concurrent package? Is this method one of its gems? A cancel() which doesn't cancel? https://docs.oracle.com/javase/8/docs/api/java/util/concurre... > Synchronized primitives don't compose. You can safely `synchronized get(...)` and safely `synchronized put(...)`. But their composition put(get(...)+1) isn't synchronized.…
What do you mean the cancel method doesn't cancel ? It does cancel with a CancellationException stored in the future and an optional interrupt send to the thread running the blocking operation.
I'm not 100% sure on what you mean by an optional interrupt, but I'm guessing it's the boolean flag on the cancel(); Let's look!
public boolean cancel(boolean mayInterruptIfRunning) {
boolean cancelled = this.result == null && this.internalComplete(new CompletableFuture.AltResult(new CancellationException()));
this.postComplete();
return cancelled || this.isCancelled();
}
Doesn't look like it's used.Re: Optimising for Concurrency: Comparing the BEAM and JVM virtual machines
#88> Programming with concurrency primitives is a difficult task because of the challenges created by its shared memory model. I never understood this often repeated point. As junior / mid-level developer I had the privilege to run self written .jar files on government scale systems with more than 50 cores. I used Java thread pools and concurrent data structures to do heavy cross thread caching. It was all pretty simple…
> When is concurrency in Java ever hard? Potentially-racey stuff: * Synchronized primitives don't compose. You can safely `synchronized get(...)` and safely `synchronized put(...)`. But their composition put(get(...)+1) isn't synchronized. And it's hard to mentally revisit it at the end of the day: if you have a class with some methods marked synchronized, nothing will tell whether you've synchronized the right metho…
You can achieve arbitrary non-blocking delays by using the cruft scheduled thread executor or doing it sanely with RxJava. Really its just dangerous to do nonblocking stuff in Java without a wrapper like RxJava. That's not a good thing, I look forward to the day there's real fibers
Re: Optimising for Concurrency: Comparing the BEAM and JVM virtual machines
#89Earlier quoted context omitted.
https://docs.oracle.com/javase/8/docs/api/java/util/concurre... And given that you didn't know that, you really need to study them. java.util.concurrent is one of the greatest gems of software ever written.
So I describe the shortcomings of CompletableFutures, and you point out that there's a java.util.concurrent package? Is this method one of its gems? A cancel() which doesn't cancel? https://docs.oracle.com/javase/8/docs/api/java/util/concurre... > Synchronized primitives don't compose. You can safely `synchronized get(...)` and safely `synchronized put(...)`. But their composition put(get(...)+1) isn't synchronized.…
You can get a lot better nonblocking support with third party libraries. Like RxJS in javascript, RxJava is almost a requirement when doing non-blocking code.
You are right that true green threads would allow thread cancellation one day. Right now, Java can't because it relies on OS threads which aren't safe to cancel. Userspace threads don't have that problem
Re: Optimising for Concurrency: Comparing the BEAM and JVM virtual machines
#90Earlier quoted context omitted.
In Erlang processes need to send messages to each other. And those messages are copies (nothing is shared). This is less efficient than in Java where everything is shared, but it also means that process a cannot change something that process b is looking at. So locks in Erlang, aren't necessary. It also enables easy distribution. When all processes share data by messaging, it doesn't matter if those processes are run…
Different strokes I guess. Erlang's model with fibers and message passing sounds close to Golang. Java has decent support for immutable objects with immutable collections, Lombok, the FreeBuilder library, both build-time code generators, and Java 14 record types. Automatic passing between machines is unique to Erlang Per process GC isn't anything like Java does, but the new GC's are probably fast enough that it doesn…
Just wanted to touch on one point. Golang also has shared memory, even though it encourages sharing by communicating. In Erlang you don't have a choice. Golang also doesn't have something like supervision trees (threads that die and restart together). So in practice golang and erlang concurrency is very different.