diff --git a/.gitignore b/.gitignore index 8f8a35f..f07ade2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,12 @@ C +C_MUSL +Crystal +Crystal_MUSL C++ Cython Cython3 D +Dart Go Go_GCC Haskell @@ -18,3 +22,8 @@ Rust *.exe *.hi *.o +*.swp +hello-world.rb18 +hello-world.mrb +hello-world.qjs +hello-world.djs diff --git a/Makefile b/Makefile index 62dad0c..b89e330 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,21 @@ -COMPILED_LANGS := C C++ Cython Cython3 D Go Go_GCC Haskell OCaml Pascal Rust -INTERPRETED_LANGS := Bash CShell Lua Perl PHP Python-S Python Python3-S Python3 PyPy Ruby Shell ZShell +# EXTRA_COMPILED_LANGS and EXTRA_INTERPRETED_LANGS are not in debian or ubuntu (as of 2020). +# Include them only if they already exist on this system: +EXTRA_COMPILED_LANGS := Crystal Crystal_MUSL Dart +EXTRA_INTERPRETED_LANGS := QJS Ruby18 + +lc = $(subst A,a,$(subst B,b,$(subst C,c,$(subst D,d,$(subst E,e,$(subst F,f,$(subst G,g,$(subst H,h,$(subst I,i,$(subst J,j,$(subst K,k,$(subst L,l,$(subst M,m,$(subst N,n,$(subst O,o,$(subst P,p,$(subst Q,q,$(subst R,r,$(subst S,s,$(subst T,t,$(subst U,u,$(subst V,v,$(subst W,w,$(subst X,x,$(subst Y,y,$(subst Z,z,$1)))))))))))))))))))))))))) + +OTHER_COMPILED_LANGS := $(foreach lang,$(EXTRA_COMPILED_LANGS),\ + $(if $(shell which $(call lc,$(lang))),$(lang),)) + +OTHER_INTERPRETED_LANGS := $(foreach lang,$(EXTRA_INTERPRETED_LANGS),\ + $(if $(shell which $(call lc,$(lang))),$(lang),)) + +COMPILED_LANGS := C C_MUSL C++ Cython Cython3 D Go Go_GCC Haskell OCaml Pascal Rust $(OTHER_COMPILED_LANGS) +INTERPRETED_LANGS := Awk Bash CShell DuktapeJS Lua MRuby NodeJS Perl PHP Python-S Python Python3-S Python3 PyPy Ruby Shell ZShell $(OTHER_INTERPRETED_LANGS) + +GENERATED_SCRIPT_LANGUAGES := qjs djs rb18 mrb +GENERATED_INTERPRETED_SCRIPTS := $(foreach script,$(GENERATED_SCRIPT_LANGUAGES), hello-world.$(script)) Bash_EXT := bash CShell_EXT := csh @@ -12,16 +28,24 @@ Python3_EXT := py3 Python3-S_EXT := py3-S PyPy_EXT := pypy Ruby_EXT := rb +Ruby18_EXT := rb18 +MRuby_EXT := mrb Shell_EXT := sh ZShell_EXT := zsh +Awk_EXT := awk +NodeJS_EXT := js +QJS_EXT := qjs +DuktapeJS_EXT := djs C_COMPILER := gcc +C_MUSL_COMPILER := musl-gcc C++_COMPILER := g++ Csharp_COMPILER := mcs Cython_COMPILER := cython Cython3_COMPILER := cython3 MONO := $(shell which mono) D_COMPILER := gdc +Dart_COMPILER := dart2native Go_COMPILER := go Go_GCC_COMPILER := gccgo Haskell_COMPILER := ghc @@ -30,18 +54,31 @@ Java_COMPILER := javac Pascal_COMPILER := fpc Rust_COMPILER := rustc Scala_COMPILER := scalac +Crystal_COMPILER := crystal +Crystal_MUSL_COMPILER := crystal_musl + +MACHINE_ARCH := $(shell uname -m) +MUSL_VERSION := $(shell /lib/ld-musl-$(MACHINE_ARCH).so.1 2>&1 |grep '^Version' |cut -d" " -f2) +Awk_VERSION = ($(shell awk --version | head -n 1 | cut -f1 -d",")) Bash_VERSION = $(shell bash --version | head -n 1 | cut -d " " -f 4 | sed 's/-release$$//') C_VERSION = $(shell $(C_COMPILER) --version | head -n 1 | cut -d " " -f 4) +C_MUSL_VERSION = $(shell $(C_COMPILER) --version | head -n 1 | cut -d " " -f 4) / musl $(MUSL_VERSION) C++_VERSION = $(shell $(C++_COMPILER) --version | head -n 1 | cut -d " " -f 4) Csharp_VERSION = $(shell $(Csharp_COMPILER) --version | head -n 1 | cut -d " " -f 5) CShell_VERSION = $(shell dpkg-query --showformat='$${Version}' --show csh | sed 's/-[a-z0-9~.]\+$$//') +Crystal_VERSION = $(shell $(Crystal_COMPILER) --version |head -n 1 | cut -d " " -f 2) +Crystal_MUSL_VERSION = $(shell $(Crystal_MUSL_COMPILER) --version |head -n 1 | cut -d " " -f 2) / musl $(MUSL_VERSION) Cython_VERSION = $(shell $(Cython_COMPILER) --version 2>&1 | head -n 1 | cut -d " " -f 3) Cython3_VERSION = $(shell $(Cython3_COMPILER) --version 2>&1 | head -n 1 | cut -d " " -f 3) D_VERSION = $(shell $(D_COMPILER) --version | head -n 1 | cut -d " " -f 4) +Dart_VERSION = $(shell dart --version 2>&1 |sed -n 's/.*\([0-9]\+[.][0-9]\+[.][0-9]\+\).*/\1/p') Go_VERSION = $(shell $(Go_COMPILER) version | head -n 1 | cut -d " " -f 3) Go_GCC_VERSION = $(shell $(Go_GCC_COMPILER) --version | head -n 1 | cut -d " " -f 4) Haskell_VERSION = $(shell $(Haskell_COMPILER) --version | head -n 1 | cut -d " " -f 8) +NodeJS_VERSION = $(shell /usr/bin/nodejs --version) +QJS_VERSION = (quickjs $(shell qjs -h |head -n1 |cut -d" " -f3)) +DuktapeJS_VERSION = (duktape js $(shell duk -e 'var maj=Math.floor(Duktape.version / 10000), min=Math.floor(Duktape.version % 10000 / 100); pat=(Duktape.version % 100); console.log([maj,min,pat].join("."))')) Lua_VERSION = $(shell lua -v 2>&1 | head -n 1 | cut -d " " -f 2) Java_VERSION = $(shell $(Java_COMPILER) -version 2>&1 | head -n 1 | cut -d " " -f 2) OCaml_VERSION = $(shell $(OCaml_COMPILER) -version) @@ -53,7 +90,9 @@ Python-S_VERSION = $(Python_VERSION) Python3_VERSION = $(shell python3 --version 2>&1 | head -n 1 | cut -d " " -f 2) Python3-S_VERSION = $(Python3_VERSION) PyPy_VERSION = $(shell pypy --version 2>&1 | tail -n 1 | cut -d " " -f 2) -Ruby_VERSION = $(shell ruby --version | head -n 1 | cut -d " " -f 2) +Ruby_VERSION = $(shell /usr/bin/ruby --version | head -n 1 | cut -d " " -f 2) +Ruby18_VERSION = $(shell ruby18 --version | head -n 1 | cut -d " " -f 2) +MRuby_VERSION = $(shell /usr/bin/mruby --version | head -n 1 | cut -d " " -f 2) Rust_VERSION = $(shell $(Rust_COMPILER) --version | head -n 1 | cut -d " " -f 2) Scala_VERSION = $(shell $(Scala_COMPILER) -version 2>&1 | head -n 1 | cut -d " " -f 4) Shell_VERSION = $(shell printf "(%s %s)" $(shell readlink /bin/sh) $(shell dpkg-query --showformat='$${Version}' --show $(shell readlink /bin/sh) | sed 's/-.*$$//')) @@ -86,6 +125,7 @@ PACKAGES := \ cython \ cython3 \ default-jdk \ + duktape \ fp-compiler \ gcc \ gccgo \ @@ -94,6 +134,8 @@ PACKAGES := \ golang \ lua5.2 \ mono-mcs \ + musl \ + nodejs \ ocaml-nox \ perl-base \ php-cli \ @@ -106,12 +148,18 @@ PACKAGES := \ zsh \ $(NULL) +define generate_script_from_template + @echo "#!$(shell which $(3))" > $(2) + @sed -n '2,$$p' $(1) >> $(2) + @chmod +x $(2) +endef + run_lang = \ - @printf "%-25s " "$(1):" $(\n)\ + @printf "%-55s " "$(1):" $(\n)\ @$(TIME) $(RUN) $(2) 2>&1 | tr -d '\n'$(\n)\ @printf " ms\n" -all: $(COMPILED_LANGS) hello-world.exe HelloWorld.class HelloWorldScala.class run +all: $(COMPILED_LANGS) $(GENERATED_INTERPRETED_SCRIPTS) hello-world.exe HelloWorld.class HelloWorldScala.class run @echo 'Run on: $(shell sed -n "s/^model name\t: //p" /proc/cpuinfo | head -n 1) | $(shell . /etc/os-release; echo $$PRETTY_NAME) | $(shell date +%Y-%m-%d)' $(foreach lang,$(COMPILED_LANGS),$(call run_lang,$(lang) ($($(lang)_COMPILER) $($(lang)_VERSION)),./$(lang))$(\n)) $(foreach lang,$(INTERPRETED_LANGS),$(call run_lang,$(lang) $($(lang)_VERSION),./hello-world.$($(lang)_EXT))$(\n)) @@ -125,6 +173,9 @@ install: C: hello-world.c $(C_COMPILER) $(CFLAGS) -o $@ $^ +C_MUSL: hello-world.c + $(C_MUSL_COMPILER) -static $(CFLAGS) -o $@ $^ + run: run.c $(C_COMPILER) $(CFLAGS) -o $@ $^ @@ -132,7 +183,7 @@ hello_cython.c: hello.pyx $(Cython_COMPILER) --embed -o $@ $^ Cython: hello_cython.c - $(C_COMPILER) $(CFLAGS) -o $@ $^ $(shell pkg-config --cflags --libs python-$(shell echo $(Python_VERSION) | cut -d. -f1-2)) + $(C_COMPILER) $(CFLAGS) -fno-strict-aliasing -o $@ $^ $(shell pkg-config --cflags --libs python-$(shell echo $(Python_VERSION) | cut -d. -f1-2)) hello_cython3.c: hello.pyx3 $(Cython3_COMPILER) --embed -o $@ $^ @@ -146,6 +197,27 @@ C++: hello-world.cpp D: $(wildcard *.d) $(D_COMPILER) $(DFLAGS) -o $@ $^ +hello-world.mrb: hello-world.rb + $(call generate_script_from_template,$^,$@,mruby) + +hello-world.rb18: hello-world.rb + $(call generate_script_from_template,$^,$@,ruby18) + +hello-world.qjs: hello-world.js + $(call generate_script_from_template,$^,$@,qjs) + +hello-world.djs: hello-world.js + $(call generate_script_from_template,$^,$@,duk) + +Crystal: hello-world.cr + $(Crystal_COMPILER) build --release -Dno_gc -o $@ $^ + +Crystal_MUSL: hello-world.cr + $(Crystal_MUSL_COMPILER) build --release -Dno_gc -o $@ $^ + +Dart: hello-world.dart + $(Dart_COMPILER) -o $@ $^ + Go: hello-world.go $(Go_COMPILER) build -o $@ $^ diff --git a/README.md b/README.md index 79556e7..9e8c3c8 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ Debian/Ubuntu releases. This script is called in a bash completion for pbuilder- ubuntu-dev-tools package). An Ubuntu user experience a [bash startup of 4 seconds](https://launchpad.net/bugs/796317) with cold cache due to the multiple invocations of `distro-info`. This made me search a suitable programming language with a fast -startup time and I wrote this man page to measure the startup time of many programming languages. +startup time and I wrote this project to measure the startup time of many programming languages. This project consists of many hello world programs in different languages and an ugly Makefile that compiles the programs and runs the benchmark. Each program is run 1000 times in a row (using a tiny @@ -27,8 +27,62 @@ Usage ===== Before running the benchmark, install all relevant compilers. On Debian/Ubuntu you can run -`make install` to install the compilers. Then start the benchmark by calling `make`: +`make install` to install the compilers. Then start the benchmark by calling `make`. +There are some optional interpreters/compilers present in this project that are +not present in Debian or Ubuntu (~2020) that will be included only if the +compilers or interpreters are present on your system. These include: + +* Crystal +* Crystal (compiled with the MUSL C library to support static binary compilation) +* Ruby 1.8 -- an old version with an older interpreter architecture that had a + faster startup time. +* QuickJS -- a lightweight, embeddable JS interpreter + +Here is output from 2020 on an Intel Core i7-5600U @2.60 GHz processor on a laptop: +``` +$ make +Run on: Intel(R) Core(TM) i7-5600U CPU @ 2.60GHz | Debian GNU/Linux 10 (buster) | 2020-11-27 +C (gcc 8.3.0): 0.25 ms +C_MUSL (musl-gcc 8.3.0 / musl 1.1.21): 0.05 ms +C++ (g++ 8.3.0): 0.73 ms +Cython (cython 0.29.2): 8.34 ms +Cython3 (cython3 0.29.2): 11.37 ms +D (gdc 8.3.0): 2.46 ms +Dart (dart2native 2.10.4): 3.92 ms +Go (go go1.11.6): 0.63 ms +Go_GCC (gccgo 8.3.0): 11.36 ms +Haskell (ghc 8.4.4): 0.71 ms +OCaml (ocamlc 4.05.0): 0.95 ms +Pascal (fpc 3.0.4): 0.07 ms +Rust (rustc 1.41.1): 0.47 ms +Crystal (crystal 0.35.1): 1.28 ms +Crystal_MUSL (crystal_musl 0.35.1 / musl 1.1.21): 0.39 ms +Awk (GNU Awk 4.2.1): 0.97 ms +Bash 5.0.3(1): 0.65 ms +CShell 20110502-4+deb10u1: 1.79 ms +DuktapeJS (duktape js 2.3.0): 1.04 ms +Lua 5.3.3: 0.60 ms +MRuby 2.0.0: 2.05 ms +NodeJS v10.21.0: 445.47 ms +Perl 5.28.1: 0.90 ms +PHP 7.3.19: 9.64 ms +Python-S 2.7.16: 2.78 ms +Python 2.7.16: 7.99 ms +Python3-S 3.7.3: 7.23 ms +Python3 3.7.3: 10.66 ms +PyPy 7.0.0: 24.72 ms +Ruby 2.5.5p157: 46.39 ms +Shell (dash 0.5.10.2): 0.30 ms +ZShell 5.7.1: 1.27 ms +QJS (quickjs 2020-11-08): 0.74 ms +Ruby18 1.8.7: 1.57 ms +C# (mcs 5.18.0.240): 14.22 ms +Java (javac 11.0.8): 54.67 ms +Scala (scalac 2.11.12): 766.54 ms +``` + +Here is older output from the original author of this project: ``` $ make Run on: Intel(R) Core(TM) i5-2400S CPU @ 2.50GHz | Ubuntu 17.10 | 2018-02-10 @@ -95,8 +149,51 @@ in `Makefile` (and maybe edit the `all` target to not depend on the .exe and .cl Note: The Makefile should be replaced by something else that is simpler to read and can produce nicer looking output. -Results -======= +Newer Results +============= + +| Language | Version | Intel Core i7-5600U | +|:----------------------------|:---------------|---------------------:| +| C-MUSL (musl-gcc) | 1.1.21 | 0.05 ms | +| Pascal (fpc) | 3.0.4 | 0.07 ms | +| C (gcc) | 8.3.0 | 0.25 ms | +| Shell (dash) | 0.5.10.2 | 0.30 ms | +| Crystal-MUSL (crystal-musl) | 1.1.21 | 0.39 ms | +| Rust (rustc) | 1.41.1 | 0.47 ms | +| Lua | 5.3.3 | 0.60 ms | +| Go (go) | 1.11.6 | 0.63 ms | +| Bash | 5.0.3 | 0.65 ms | +| Haskell (ghc) | 8.4.4 | 0.71 ms | +| C++ (g++) | 8.3.0 | 0.73 ms | +| QJS (quickjs) | 2020-11-08 | 0.74 ms | +| Perl | 5.28.1 | 0.90 ms | +| OCaml (ocamlc) | 4.05.0 | 0.95 ms | +| Awk (GNU) | 4.2.1 | 0.97 ms | +| DuktapeJS (duktape) | 2.3.0 | 1.04 ms | +| ZShell | 5.7.1 | 1.27 ms | +| Crystal (crystal) | 0.35.1 | 1.28 ms | +| Ruby18 | 1.8.7 | 1.57 ms | +| CShell | 20110502-4 | 1.79 ms | +| MRuby | 2.0.0 | 2.05 ms | +| D (gdc) | 8.3.0 | 2.46 ms | +| Python-S | 2.7.16 | 2.78 ms | +| Dart (dart2native) | 2.10.4 | 3.95 ms | +| Python3-S | 3.7.3 | 7.23 ms | +| Python | 2.7.16 | 7.99 ms | +| Cython (cython) | 0.29.2 | 8.34 ms | +| PHP | 7.3.19 | 9.64 ms | +| Python3 | 3.7.3 | 10.66 ms | +| Go-GCC (gccgo) | 8.3.0 | 11.36 ms | +| Cython3 (cython3) | 0.29.2 | 11.37 ms | +| C# (mcs) | 5.18.0.240 | 14.22 ms | +| PyPy | 7.0.0 | 24.72 ms | +| Ruby | 2.5.5p157 | 46.39 ms | +| Java (javac) | 11.0.8 | 54.67 ms | +| NodeJS | 10.21.0 | 445.47 ms | +| Scala (scalac) | 2.11.12 | 766.54 ms | + +Older Results +============= | Language | version | Intel Core i5 2400S | Raspberry Pi 3 | | ------------------|---------------------- | ------------------: | -------------: | @@ -130,7 +227,7 @@ Results Evaluation ========== -I categorize the programming languages in four categories: fast, okay, slow, takes ages. +I categorize the startup time for programming languages in four categories: fast, okay, slow, takes ages. Fast ---- @@ -138,50 +235,121 @@ Fast Fast startup times are below 1 ms on my desktop (Intel Core i5 2400S) and below 10 ms on slow hardware (Raspberry Pi 3): +* Awk * Bash -* C +* C (either dynamically-linked with glibc or statically linked with musl) * C++ +* Crystal (statically linked with musl) * D (gdc) * Go (go) * Haskell * Lua * Pascal * Perl +* QuickJS * Rust * Shell (dash) Okay ---- -Okayish startup times are between 1 and 5 ms on my desktop (Intel Core i5 2400S) and between +Okayish startup times are between 1 and 5 ms on my laptop Intel Core i7-5600U and between 10 and 50 ms on slow hardware (Raspberry Pi 3): * CShell +* Crystal (dynamically linked with glibc) +* Dart (aot compiled to a single binary with dart2native) +* Duktape JS +* MRuby * Python 2 (with -S) +* Ruby 1.8 * ZShell Slow ---- -Slow startup times are between 5 and 50 ms on my desktop (Intel Core i5 2400S) and between +Slow startup times are between 5 and 50 ms on my laptop Intel Core i7-5600U and between 50 and 500 ms on slow hardware (Raspberry Pi 3): * C# (mcs) * Cython (Python 2) * Cython3 (Python 3) +* Go (gccgo) * PHP * Python 2 * Python 3 * Python 3 (with -S) * PyPy (Python 2) -* Ruby +* Ruby (all descendents of YARV >= Ruby 1.9) Takes ages ---------- -Some programming languages take ages to start up. The startup times are above 50 ms on my desktop -(Intel Core i5 2400S) and over 500 ms on slow hardware (Raspberry Pi 3): +Some programming languages take ages to start up. The startup times are above +50 ms on an Intel Core i7-5600U and over 500 ms on slow hardware +(Raspberry Pi 3): * Java -* Go (gccgo) +* NodeJS * Scala + +Caveats +======= + +Some misc notes on performance: + +* The results are a good illustration of why we should think of runtime + characteristics like startup performance as a function of the implementation + of a language toolchain rather than as a function of the language itself. + NodeJS, which is built on top of the V8 JIT JavaScript engine, had a startup + performance of 445 ms on my system, while QuickJS came in at 0.74 ms and + Duktape started up in 1.05 ms. The js language itself shares many + similarities with lua, and the QuickJS and Duktape implementations are both + lightweight and embeddable, like lua, with fast startup times for their + interpreter. NodeJS has superior runtime performance, but its JIT engine is + much slower to start up. This startup latency is noticeable in the startup + latency of nodejs-based build tools and command-line utilities. + +* I tested Ruby 1.8 because I was curious to see how its startup times compared + with Ruby 2.5.x. In Ruby 1.9, the ruby interpreter switched from the original + MRI architecture to YARV, which implemented a bytecode interpreter. I believe + that the interpreter in Ruby <= 1.8 operated like Perl 5 by walking the AST + rather than by generating bytecode (like python). In general, bytecode-based + VMs tend to have better runtime performance to AST-walking interpreters, but + they also tend to have inferior startup performance because more work needs + to be done at startup to generate bytecode. Therefore, the performance of + long-running ruby processes is much better in 2.5 vs 1.8, but the startup + performance of 1.8 was significantly faster than 2.5. + +* The absolte best startup performance went to a C program statically-compiled + against musl rather than glibc. The next-fastest was freepascal, which also + generates a small, statically-compiled binary. I have seen many benchmarks + that generally show that C programs compiled and linked with glibc have + superior runtime performance to programs compiled with musl, but a small + program statically compiled with musl will have faster startup time, which + offers advantages in some use-cases. Still, the difference on a bare-iron + modern laptop or desktop will be < 1ms. Simplified portability and distribution + in special-purpose environments like single-purpose embedded environments or + containers can be a good reason to statically-link with musl, but startup + performance is probably not a great reason to choose musl over glibc unless + your utility is running on some type of embedded system or another + resource-constrained environment. + +* The abysmal startup performance of Scala is partly due to the fact that + distributions of scala aren't optimized for startup time at all. On Debian, + the ``scala`` executable is a Bash script (not dash) that does quite a bit of work + before it invokes a scala program with the jvm. This could probably + be optimized a bit so the startup performance is closer to java's, but + it would still be orders of magnitude slower than the startup performance + of a fast, lightweight interpreter like perl, awk or dash or a compiled + language likg C, C++, Go or Rust. Anything running on a standard JVM will not + have fast startup performance. + +* The benchmarks invoke interpreted languages by executing executable + scripts directly. This means that the OS has to read the shebang line of the + scripts and then invoke the interpreter. I would imagine that this results in + a slight performance penalty for all interpreted languages in these + benchmarks, which is arguably unfair. However, executable scripts are + generally invoked directly rather than by passing the script filename to the + interpreter binary, and in this sense it's probably fair to compare them with + this typical usage style. diff --git a/format_results.rb b/format_results.rb new file mode 100755 index 0000000..12cf7fa --- /dev/null +++ b/format_results.rb @@ -0,0 +1,79 @@ +#!/usr/bin/ruby + +require 'bigdecimal' + +# We want to generate a markdown table like this: +# +#| Language | version | Intel Core i7-5600U | +#| ------------------|---------------------- | ------------------: | +#| Pascal (fpc) | 3.0.2 / 3.0.4 | 0.08 ms | +#| C (gcc) | 7.2.0 | 0.26 ms | +#| Shell (dash) | 0.5.8 | 0.33 ms | +#| Go (go) | 1.8.3 / 1.9.3 | 0.41 ms | + +# From Makefile output like this: +# +#Run on: Intel(R) Core(TM) i7-5600U CPU @ 2.60GHz | Debian GNU/Linux 10 (buster) | 2020-11-27 +#C (gcc 8.3.0): 0.25 ms +#C_MUSL (musl-gcc 8.3.0 / musl 1.1.21): 0.05 ms +#C++ (g++ 8.3.0): 0.73 ms + +input_file = ARGV[0] + +if !File.exists?(input_file) + script_name = File.basename($0) + puts "Usage #{script_name} MAKEFILE_OUTPUT" + exit 0 +end + +class Result + attr_reader :lang_impl, :version, :time_ms + def initialize(line) + parts = line.split(/\s+/) + @lang_impl = parts.shift + parts.pop + @time_ms = BigDecimal.new(parts.pop) + if parts.first.start_with?(/\([a-zA-Z]+/) + sub_impl = parts.first + ')' + @lang_impl += ' ' + sub_impl + end + @lang_impl = @lang_impl.tr('_','-') + m = parts.join(' ').scan(/[-.0-9p]+/) + @version = '' if m.nil? + + ver = m.select {|s| s.size > 2}.last + @version = ver + end + + def <=>(other) + self.time_ms <=> other.time_ms + end + + def time_str + sprintf('%.2f ms', time_ms) + end + + def to_s + "#{lang_impl} #{version} #{time_str}" + end +end + +lines = File.readlines(input_file).select {|l| l =~ /[0-9]\s+ms$/} + +results = lines.map {|l| Result.new(l)}.sort + +longest_lang = results.map {|r| r.lang_impl.size}.sort.last +longest_ver = results.map {|r| r.version.size}.sort.last + 4 + +fmt = "| %-#{longest_lang}s | %-#{longest_ver}s | %20s |\n" + +printf(fmt, "Language", "Version", "Intel Core i7-5600U") + +printf("|%s|%s|%s|\n", + ':' + ('-' * (longest_lang+1)), + ':' + ('-' * (longest_ver+1)), + ('-' * 21) + ':') + +results.each do |r| + printf(fmt, r.lang_impl, r.version, r.time_str) +end diff --git a/hello-world.awk b/hello-world.awk new file mode 100755 index 0000000..62b8c13 --- /dev/null +++ b/hello-world.awk @@ -0,0 +1,3 @@ +#!/usr/bin/awk -f + +BEGIN { print "Hello World!" } diff --git a/hello-world.cr b/hello-world.cr new file mode 100644 index 0000000..01a178a --- /dev/null +++ b/hello-world.cr @@ -0,0 +1 @@ +puts "Hello World!" diff --git a/hello-world.dart b/hello-world.dart new file mode 100644 index 0000000..01fe643 --- /dev/null +++ b/hello-world.dart @@ -0,0 +1,11 @@ +import 'dart:io'; + +// I do not know why, but as-of Dart version 2.10.4, if we do not add an explicit +// exit(0) then the executable will hang for about 1 second if we try to execute +// it repeatedly with ./run. The explicit exit(0) seems to eliminate this issue, +// but it may be worth exploring why this is happening to see if there is an +// issue with the internals of run.c that is causing this. +void main(List arguments) { + print('Hello World!'); + exit(0); +} diff --git a/hello-world.js b/hello-world.js new file mode 100755 index 0000000..0a5f0d4 --- /dev/null +++ b/hello-world.js @@ -0,0 +1,3 @@ +#!/usr/bin/nodejs + +console.log("Hello World!");