Live data from Hacker News

Optimising for Concurrency: Comparing the BEAM and JVM virtual machines

erlang-solutions.com

81–90 of 114 posts

Re: Optimising for Concurrency: Comparing the BEAM and JVM virtual machines

#81
They mentioned this for the beam virtual machine but not for the JVM, the JVM actually can also do hot code loading as long as call site signatures are not changed or added. In some cases you you can make major changes to the current stack and restart the frame which is a pretty handy feature for developer. Some commercial extensions to the JVM get around all of these limitations.

Re: Optimising for Concurrency: Comparing the BEAM and JVM virtual machines

#82
post #76

Earlier 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…

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'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

#83
post #71

the 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…

I have actually heard the same about Quasar so I have avoided using it. It hacks up the bytecode so evil bugs appear common based on my glances at issue tracker.

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

#84
post #67

the 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.

I want to see a source on this. Golang's GC is often touted as better than Java's but every real world benchmark I've seen shows that it sacrifices a lot of throughput for low pause times, essentially by running much more often.

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

#86
post #80
post #75

Earlier 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.

> What do you mean the cancel method doesn't cancel?

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't

Re: Optimising for Concurrency: Comparing the BEAM and JVM virtual machines

#87
post #80
post #75

Earlier 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.

> 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
post #23

> 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…

original Futures can't be cancelled. CompleteableFuture can be, but only if the thread agrees to die.

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

#89
post #75
post #65

Earlier 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.…

Java does not allow one thread to kill another due to its shared memory concurrency model. It actually used to, but this feature was removed because it caused so many deadlocks. The reason is that killing threads won't always release monitors and locks. Lack of the feature is intentional.

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

#90
post #76

Earlier 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…

Sounds about right :)

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.

Post reply on HN