またまた久しぶりに竹内関数で JavaScript、Python、Ruby、Scheme と Smalltalk とを戦わせてみる

竹内関数 tak(20, 10, 0) にかかる時間を、手元の環境(Intel Core i9-9880H @ 2.30GHz、Win10 64-bit WSL環境)で計測してみました。前回からちょうど(?)7年ですね^^;

SmalltalkSqueak は Win版をそのまま、Pharo は次のページを参考に WSL にインストールして動かしてみました。

 言語   処理系   結果   対C比   参考:2014年時点 
 Smalltalk   Squeak 5.3   0.596 sec   4.45倍   6.12倍 (Squeak 4.5) 
    Pharo 8.0.0   0.644 sec   4.81倍   — 
    GNU Smalltalk 3.2.5   7.97 sec   59.5倍   65.0倍 (3.1) 
 Python   Python 3.10.0b3+   10.0 sec   74.6倍   52.53倍 (3.2.5) 
 Ruby   Ruby 3.1.0dev   2.97 sec   22.2倍   20.8倍 (2.2.0dev) 
 Scheme   Gauche 0.9.10   2.44 sec   18.2倍   15.1倍 (0.9.5_pre1) 
 JavaScript   Node.js 16.3.0   0.381 sec   2.84倍   2.23倍 (0.10.31) 
 C   gcc 4.8.3   0.134 sec   1.00倍   1.00倍 (4.8.3) 

対C比では7年前とあまり代わり映えしませんが、Rubyが速くなっていないのがちょっと意外ですね。

コードは以前と一緒です。

| tak res |
tak := nil.
tak := [:x :y :z |
   x <= y ifTrue: [z] ifFalse: [
      tak
         value: (tak value: x-1 value: y value: z)
         value: (tak value: y-1 value: z value: x)
         value: (tak value: z-1 value: x value: y)
   ]
].

(Time millisecondsToRun: [res := tak value: 20 value: 10 value: 0]) -> res

Squeak は起動後 Tools → Workspace で、Pharo は ./pharo-ui で起動後、Tools → Playground でプレイグラウンドを呼び出し、コードを貼り付けてから全選択、右クリックメニューや ctrl + p などで print it すると実行と結果を表示できます。

f:id:sumim:20210623184139p:plain
Pharo Smalltalkでの実行画面
f:id:sumim:20210623190457p:plain
Squeak Smalltalk での実行画面

GNU Smalltalk でも動きますが、最後の式を括弧でくくって printNl しないと結果を出力できません。

$ gst -v; gst tak.st
GNU Smalltalk version 3.2.5
...
7976->1

$ cat tak.st
| tak res |
tak := nil.
tak := [:x :y :z |
   x <= y ifTrue: [z] ifFalse: [
      tak
         value: (tak value: x-1 value: y value: z)
         value: (tak value: y-1 value: z value: x)
         value: (tak value: z-1 value: x value: y)
   ]
].

((Time millisecondsToRun: [res := tak value: 20 value: 10 value: 0]) -> res) printNl

その他の言語のコードと出力

$ python -V; python tak.py
Python 3.10.0b3+
10.04528284072876 1

$ cat tak.py
from time import time

def tak(x, y, z):
    if x <= y: return z
    return tak( tak(x-1, y, z), tak(y-1, z, x), tak(z-1, x, y) )

start = time()
res = tak(20, 10, 0)
print( time() - start, res )
$ ruby -v tak.rb
ruby 3.1.0dev (2021-06-23T06:14:21Z master 69ce9e4187) [x86_64-linux]
1
2.9718293

$ cat tak.rb
def tak(x, y, z)
  if x <= y then
    z
  else
    tak(tak(x-1, y, z), tak(y-1, z, x), tak(z-1, x, y))
  end
end

start = Time.now
puts tak(20, 10, 0)
puts Time.now - start
$  gosh -V; gosh tak.scm
Gauche scheme shell, version 0.9.10 [utf-8,pthreads], x86_64-pc-linux-gnu
...
;(time (display (tak 20 10 0)))
; real   2.441
; user   2.440
; sys    0.000
1

$ cat tak.scm
(define (tak x y z)
   (if (<= x y) z
     (tak (tak (- x 1) y z) (tak (- y 1) z x) (tak (- z 1) x y))))

(time (display (tak 20 10 0)))
$ node -v; node tak.js
v16.3.0
381 1

$ cat tak.js
function tak(x, y, z){
   if(x <= y){ return z; }
   return tak( tak(x-1, y, z), tak(y-1, z, x), tak(z-1, x, y) );
}

var start = (new Date()).getTime();
var res = tak(20, 10, 0);
console.log((new Date()).getTime() - start, res)
$ gcc --version
gcc (Ubuntu 7.5.0-3ubuntu1~18.04) 7.5.0
...

$ cat tak.c
#include <stdio.h>

int tak(int x, int y, int z){
   if (x <= y){
      return z;
   } else {
      return tak( tak(x-1, y, z), tak(y-1, z, x), tak(z-1, x, y) );
   }
}

int main(void){
   printf("%d\n", tak(20, 10, 0));
   return 0;
}

$ gcc -O3 tak.c -o tak_O3

$ time ./tak_O3
1

real    0m0.134s
user    0m0.134s
sys     0m0.000s