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.
Top comments (4)
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
Feel free to try it out.
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.