loading...
Cover image for "Hello World" performance comparison between GraalVM and Go

"Hello World" performance comparison between GraalVM and Go

theodesp profile image Theofanis Despoudis ・3 min read

Go is an established language with a lots of production exposure. Go is infamous for creating small application footprint and fast performance.

As for GraalVM, taken from their Website:

GraalVM is a universal virtual machine for running applications written in JavaScript, Python, Ruby, R, JVM-based languages like Java, Scala, Groovy, Kotlin, Clojure, and LLVM-based languages such as C and C++.

Which means that it combines the JVM ecosystem with the small footprint and performance of native apps. It looks very promising so far.

In this tutorial I wanted to check really quick what is their "Hello World" performance characteristics just to see how the compare. I may be doing more experiments with different apps to explore more about it.

The Program

I've used standard hello world programs for both Kotlin and Go:

package main

import "fmt"

func main() {
    fmt.Println("hello world")
}
package hello

fun main(args: Array<String>) : Unit {
    println("Hello world")
}

I've used the following versions for Go and GraalVM native-image utility:

$ go version      
go version go1.12.9 darwin/amd64

➜ $GRAALVM_HOME/bin/native-image --version 
GraalVM Version 19.3.0 CE

I run the examples in this following system:

  Model Name:   MacBook Pro
  Model Identifier: MacBookPro12,1
  Processor Name:   Intel Core i5
  Processor Speed:  2.9 GHz
  Number of Processors: 1
  Total Number of Cores:    2
  L2 Cache (per Core):  256 KB
  L3 Cache: 3 MB
  Hyper-Threading Technology:   Enabled
  Memory:   16 GB

The Results

First I compiled the Go executable:

$ go build main.go

Disk usage reports only 2mb! Thats great:

$ du -h  main                    
2.0M    main

Time usage via both time and gnu-time commands:

$ gtime -v ./main
hello world
        Command being timed: "./main"
        User time (seconds): 0.00
        System time (seconds): 0.00
        Percent of CPU this job got: 66%
        Elapsed (wall clock) time (h:mm:ss or m:ss): 0:00.00
        Average shared text size (kbytes): 0
        Average unshared data size (kbytes): 0
        Average stack size (kbytes): 0
        Average total size (kbytes): 0
        Maximum resident set size (kbytes): 1960
        Average resident set size (kbytes): 0
        Major (requiring I/O) page faults: 0
        Minor (reclaiming a frame) page faults: 615
        Voluntary context switches: 1
        Involuntary context switches: 75
        Swaps: 0
        File system inputs: 0
        File system outputs: 0
        Socket messages sent: 0
        Socket messages received: 0
        Signals delivered: 0
        Page size (bytes): 4096
        Exit status: 0


$ time ./main          
hello world
./main  0.00s user 0.00s system 60% cpu 0.010 total

Next I compiled the Kotlin example:

I run maven install typically and then the native-image tool:

mvn clean install

$GRAALVM_HOME/bin/native-image -cp ./target/mixed-code-hello-world-1.0-SNAPSHOT.jar -H:Name=helloworld -H:Class=hello.JavaHello -H:+ReportUnsupportedElementsAtRuntime --allow-incomplete-classpath

Disk usage reports 2.8mb! Not bad:

$ du -h ./helloworld 
2.8M    ./helloworld

Time usage via both time and gnu-time commands:

$ gtime -v ./helloworld
Hello world
        Command being timed: "./helloworld"
        User time (seconds): 0.00
        System time (seconds): 0.00
        Percent of CPU this job got: 57%
        Elapsed (wall clock) time (h:mm:ss or m:ss): 0:00.00
        Average shared text size (kbytes): 0
        Average unshared data size (kbytes): 0
        Average stack size (kbytes): 0
        Average total size (kbytes): 0
        Maximum resident set size (kbytes): 2072
        Average resident set size (kbytes): 0
        Major (requiring I/O) page faults: 0
        Minor (reclaiming a frame) page faults: 657
        Voluntary context switches: 0
        Involuntary context switches: 11
        Swaps: 0
        File system inputs: 0
        File system outputs: 0
        Socket messages sent: 0
        Socket messages received: 0
        Signals delivered: 0
        Page size (bytes): 4096
        Exit status: 0


$ time ./helloworld
Hello world
./helloworld  0.00s user 0.00s system 56% cpu 0.006 total

Looking at the results, they almost look comparable but of course this is just a simplified example and does not show a lot of info. I will certainly explore more of the examples given by the GraalVM team.

Let me know what you think.

Posted on by:

theodesp profile

Theofanis Despoudis

@theodesp

Senior Software Engineer @wpengine, Experienced mentor @codeimentor, Technical Writer @fixate.io, Book author

Discussion

markdown guide
 

Thank you for informing me about the existence of GraalVM. I've never heard about it.

The binary size comparison is interesting.

As for measuring time: Doing it once is useless. The rest of the system surely impacted those times more than the programs themselves. Also, they are so tiny, they have no informative value. - All we know is that it does not start a runtime VM, that would take as long as JVM to start.

A more interesting "hello world" benchmark would be maybe something like: allocating a few hundred thousand objects and sorting them - repeating 50 times, or so.

 

Just for fun I have implemented a decent utility in Java, Rust, Go, and then modified Java version to be compile it to native image. Surprisingly: Java based native image outperformed even Rust version. I suspect that it can be attributed to high quality implementation of JSON parser. 93ms vs 125ms

github.com/kgignatyev/hiera-ctp

 
 

both are 2mb I was expecting far more smaller from golang :) I am starting to think graalvm started to just match golang :)

but I must say java still looks nicer to me as language.