1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Mandelbrot ベンチマーク(C, PHP, HHVM, Ruby, Python, PyPy, and Kinx)

Last updated at Posted at 2020-06-08

Mandelbrot ベンチマーク

  • 対象 ... C, PHP, HHVM, Ruby, Python, PyPy, and 我らが Kinx

PyPy できるようになったので追加してみた。その結果はいかに?!

はじめに

この記事 【PHP8】PHPでJITが使えるようになる でのベンチマークを拝見させていただきまして。

PHP8 に JIT が入るとかで Mandelbrot のベンチマークをしているのですが、は!、これは我らが Kinx 君も native メソッドでブイブイ言わせられるやつでは!、との気持ちから試しにベンチマークしてみよう、という趣旨のイベントです。

記事にしたということは、勝負になった ということでもあります。というか予想以上。やったね!

ちなみに、Kinx って何?という方は以下をご参照ください。

前提条件

ベンチマークの内容

ここ(https://gist.github.com/dstogov/12323ad13d3240aee8f1)に各種ベンチマークプログラムが掲載されていますが、これをベースにします。

環境が異なるため同じ結果にはなりませんので、いくつかの言語で全部計測します。各種言語のバージョンはオリジナル記事に正確には合わせられませんでしたが、近いもので計測してオリジナルの記事と比較してみます。

ただし、元記事 でも触れられているように、PHP だけ ズル してます。それを避けるとともに、実際問題として I/O 性能も計測の邪魔なので、出力部分はざっくり削除することにします。しかし(逆説が続く)、C に関しては出力を無くすとどうも最適化しすぎてベンチマークにならないので、 C は最適化無しでいきます。一種のハンディキャップ的な。それでも速い。

計測時間について

実行開始後に、各言語で用意されたタイマーを使って実行時間を計測しています。で、気づいたのですが、つまりソース解析してコンパイルする時間が含まれていません。C はしょうがないけど。

何が違和感だったかというと、HHVM は体感的に遅いのに、最終的に出力される実行時間が速い のです。具体的には、HHVM は体感的には PHP より遅いけど数値は PHP より良い、という結果です。

出力について

出力はさせた場合は全部以下の形が出力されることを確認済み。我が Kinx(native) 君も期待値通りの働きをしてくれましたよ。

                                       *
                                       *
                                       *
                                       *
                                       *
                                      ***
                                     *****
                                     *****
                                      ***
                                       *
                                   *********
                                 *************
                                ***************
                             *********************
                             *********************
                              *******************
                              *******************
                              *******************
                              *******************
                            ***********************
                              *******************
                              *******************
                             *********************
                              *******************
                              *******************
                               *****************
                                ***************
                                 *************
                                   *********
                                       *
                                ***************
                            ***********************
                         * ************************* *
                         *****************************
                      * ******************************* *
                       *********************************
                      ***********************************
                    ***************************************
               *** ***************************************** ***
               *************************************************
                ***********************************************
                 *********************************************
                 *********************************************
                ***********************************************
                ***********************************************
              ***************************************************
               *************************************************
               *************************************************
              ***************************************************
              ***************************************************
         *    ***************************************************    *
       *****  ***************************************************  *****
       ****** *************************************************** ******
      ******* *************************************************** *******
    ***********************************************************************
    ********* *************************************************** *********
       ****** *************************************************** ******
       *****  ***************************************************  *****
              ***************************************************
              ***************************************************
              ***************************************************
              ***************************************************
               *************************************************
               *************************************************
              ***************************************************
                ***********************************************
                ***********************************************
                  *******************************************
                   *****************************************
                 *********************************************
                **** ****************** ****************** ****
                 ***  ****************   ****************  ***
                  *    **************     **************    *
                         ***********       ***********
                         **  *****           *****  **
                          *   *                 *   *

ベンチマーク

さて、本題のベンチマークです。
まずはプログラムコードから。

C

gcc のバージョン。

$ gcc --version
gcc (Ubuntu 7.4.0-1ubuntu1~18.04.1) 7.4.0
Copyright (C) 2017 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

C はこんな感じ。

mandelbrot.c
#include <stdio.h>
#include <sys/time.h>

#define BAILOUT 16
#define MAX_ITERATIONS 1000

int mandelbrot(double x, double y)
{
        double cr = y - 0.5;
        double ci = x;
        double zi = 0.0;
        double zr = 0.0;
        int i = 0;

        while(1) {
                i ++;
                double temp = zr * zi;
                double zr2 = zr * zr;
                double zi2 = zi * zi;
                zr = zr2 - zi2 + cr;
                zi = temp + temp + ci;
                if (zi2 + zr2 > BAILOUT)
                        return i;
                if (i > MAX_ITERATIONS)
                        return 0;
        }

}

int main (int argc, const char * argv[]) {
        struct timeval aTv;
        gettimeofday(&aTv, NULL);
        long init_time = aTv.tv_sec;
        long init_usec = aTv.tv_usec;

        int x,y;
        for (y = -39; y < 39; y++) {
                //printf("\n");
                for (x = -39; x < 39; x++) {
                        volatile int i = mandelbrot(x/40.0, y/40.0);
                        //if (i==0)
                        //      printf("*");
                        //else
                        //      printf(" ");
                }
        }
        //printf ("\n");

        gettimeofday(&aTv,NULL);
        double query_time = (aTv.tv_sec - init_time) + (double)(aTv.tv_usec - init_usec)/1000000.0;
        printf ("C Elapsed %0.3f\n", query_time);
    return 0;
}

PHP/HHVM

PHP のバージョン。

$ php --version
PHP 7.2.24-0ubuntu0.18.04.6 (cli) (built: May 26 2020 13:09:11) ( NTS )
Copyright (c) 1997-2018 The PHP Group
Zend Engine v3.2.0, Copyright (c) 1998-2018 Zend Technologies
    with Zend OPcache v7.2.24-0ubuntu0.18.04.6, Copyright (c) 1999-2018, by Zend Technologies

HHVM のバージョン。

$ hhvm --version
HipHop VM 3.21.0 (rel)
Compiler: 3.21.0+dfsg-2ubuntu2
Repo schema: ebd0a4633a34187463466c1d3bd327c131251849

PHP と HHVM は同じソースコードです。

mandelbrot.php
<?php
define("BAILOUT",16);
define("MAX_ITERATIONS",1000);

class Mandelbrot
{
    function Mandelbrot()
    {
        $d1 = microtime(1);
        for ($y = -39; $y < 39; $y++) {
            for ($x = -39; $x < 39; $x++) {
                $this->iterate($x/40.0,$y/40.0);
            }
        }
        $d2 = microtime(1);
        $diff = $d2 - $d1;
        printf("PHP Elapsed %0.3f\n", $diff);
    }

    function iterate($x,$y)
    {
        $cr = $y-0.5;
        $ci = $x;
        $zr = 0.0;
        $zi = 0.0;
        $i = 0;
        while (true) {
            $i++;
            $temp = $zr * $zi;
            $zr2 = $zr * $zr;
            $zi2 = $zi * $zi;
            $zr = $zr2 - $zi2 + $cr;
            $zi = $temp + $temp + $ci;
            if ($zi2 + $zr2 > BAILOUT)
                return $i;
            if ($i > MAX_ITERATIONS)
                return 0;
        }

    }


}

$m = new Mandelbrot();
?>

Ruby

Ruby のバージョン。

$ ruby --version
ruby 2.5.1p57 (2018-03-29 revision 63029) [x86_64-linux-gnu]

Ruby のソースコード。

mandelbrot.rb
BAILOUT = 16
MAX_ITERATIONS = 1000

class Mandelbrot

        def initialize
                #puts "Rendering"
                for y in -39...39 do
                        #puts
                        for x in -39...39 do
                                i = iterate(x/40.0,y/40.0)
                                #if (i == 0)
                                #       print "*"
                                #else
                                #       print " "
                                #end
                        end
                end
        end

        def iterate(x,y)
                cr = y-0.5
                ci = x
                zi = 0.0
                zr = 0.0
                i = 0

                while(1)
                        i += 1
                        temp = zr * zi
                        zr2 = zr * zr
                        zi2 = zi * zi
                        zr = zr2 - zi2 + cr
                        zi = temp + temp + ci
                        return i if (zi2 + zr2 > BAILOUT)
                        return 0 if (i > MAX_ITERATIONS)
                end

        end

end

time = Time.now
Mandelbrot.new
#puts
puts "Ruby Elapsed %f" % (Time.now - time)

Python/PyPy

Python のバージョン。

$ python --version
Python 2.7.15+

PyPy のバージョン。

$ pypy --version
Python 2.7.13 (5.10.0+dfsg-3build2, Feb 06 2018, 18:37:50)
[PyPy 5.10.0 with GCC 7.3.0]

Python のソースコード。PyPy も一緒です。

mandelbrot.py
import sys, time
stdout = sys.stdout

BAILOUT = 16
MAX_ITERATIONS = 1000

class Iterator:
  def __init__(self):
    #print 'Rendering...'
    for y in range(-39, 39):
      #stdout.write('\n')
      for x in range(-39, 39):
        i = self.mandelbrot(x/40.0, y/40.0)

        #if i == 0:
          #stdout.write('*')
        #else:
          #stdout.write(' ')

  def mandelbrot(self, x, y):
    cr = y - 0.5
    ci = x
    zi = 0.0
    zr = 0.0
    i = 0

    while True:
      i += 1
      temp = zr * zi
      zr2 = zr * zr
      zi2 = zi * zi
      zr = zr2 - zi2 + cr
      zi = temp + temp + ci

      if zi2 + zr2 > BAILOUT:
        return i
      if i > MAX_ITERATIONS:
        return 0

t = time.time()
Iterator()
print 'Python Elapsed %.02f' % (time.time() - t)

Kinx/Kinx(native)

Kinx のバージョン。

$ kinx -v
kinx version 0.9.2

ただし、native 内に const 定数が正しく伝搬されないバグが 0.9.2 リリースにあったので、一部修正したものを利用しました...。コミットは済です。

Kinx 通常版のソースコード。

mandelbrot.kx
const BAILOUT = 16;
const MAX_ITERATIONS = 1000;

function mandelbrot(x, y) {
    var cr = y - 0.5;
    var ci = x;
    var zi = 0.0;
    var zr = 0.0;
    var i = 0;

    while (true) {
        i++;
        var temp = zr * zi;
        var zr2 = zr * zr;
        var zi2 = zi * zi;
        zr = zr2 - zi2 + cr;
        zi = temp + temp + ci;
        if (zi2 + zr2 > BAILOUT)
            return i;
        if (i > MAX_ITERATIONS)
            return 0;
    }
}


var tmr = new SystemTimer();
var x,y;
for (y = -39; y < 39; y++) {
    #System.print("\n");
    for (x = -39; x < 39; x++) {
        var i = mandelbrot(x/40.0, y/40.0);
        #if (i==0)
        #    System.print("*");
        #else
        #    System.print(" ");
    }
}
#System.print("\n");
System.print("Kinx Elapsed %0.3f\n" % tmr.elapsed());

Kinx(native) のソースコード。計算結果から型が分かる場合は書かなくても良いので、引数に :dbl と付けるだけでいけたよ。

mandelbrotn.kx
const BAILOUT = 16;
const MAX_ITERATIONS = 1000;

native mandelbrot(x:dbl, y:dbl) {
    var cr = y - 0.5;
    var ci = x;
    var zi = 0.0;
    var zr = 0.0;
    var i = 0;

    while (true) {
        i++;
        var temp = zr * zi;
        var zr2 = zr * zr;
        var zi2 = zi * zi;
        zr = zr2 - zi2 + cr;
        zi = temp + temp + ci;
        if (zi2 + zr2 > BAILOUT)
            return i;
        if (i > MAX_ITERATIONS)
            return 0;
    }
}


var tmr = new SystemTimer();
var x,y;
for (y = -39; y < 39; y++) {
    #System.print("\n");
    for (x = -39; x < 39; x++) {
        var i = mandelbrot(x/40.0, y/40.0);
        #if (i==0)
        #    System.print("*");
        #else
        #    System.print(" ");
    }
}
#System.print("\n");
System.print("Kinx(native) Elapsed %0.3f\n" % tmr.elapsed());

結果

結果表示です。10回やった平均。速かった順に並べなおす。

すみません、計測プログラムに問題があり、再度計測しなおしました。

言語 バージョン 計測時刻(秒,10回平均) time(real),10回平均
C 7.4.0 0.018 0.046
PyPy 5.10.0 0.020 0.122
Kinx(native) 0.9.2 0.048 0.107
HHVM 3.21.0 0.068 0.552
PHP 7.2.24 0.182 0.241
Ruby 2.5.1 0.365 0.492
Kinx 0.9.2 0.393 0.457
Python 2.7.15 0.564 0.601

やった! 体感では PHP より遅い HHVM よりも速い結果が出たぜ。あと、通常版のほうでも スゲー速いと思っていた Ruby VM にも匹敵する速さ だったので単純に嬉しい。

というか、PyPy が異常に速ぇ。格の違いを感じたよ。素晴らしい。

HHVM の体感的な遅さも表に表れている。コンパイル時間が長いのでしょう。言語仕様上、仕方のないことです。Kinx(native) もコンパイル時間分のペナルティは見えている。

さて、元記事 の結果と比較してみよう。今回のベンチマークでは出力も抑止しているのだが、環境の差が大きい。比率的な傾向は似ていると思っ...たのだが、HHVM だけなんか変だな。デフォルトで JIT on のはずなので、JIT on と比較。逆に元記事の JIT off 版の結果は遅すぎて何が正しいのかよくわからん。他は総じて環境的に今のほうが 約2倍 速いという結果に。

言語 バージョン 計測時刻(秒,10回平均) 元記事結果 元記事バージョン
C 7.4.0 0.018 0.022 4.9.2
PyPy 5.10.0 0.020
Kinx(native) 0.9.2 0.048
HHVM 3.21.0 0.068 0.030 3.5.0
PHP 7.2.24 0.182 0.281 7
Ruby 2.5.1 0.365 0.684 2.1.5
Kinx 0.9.2 0.393
Python 2.7.15 0.564 1.128 2.7.8

おわりに

ベンチマーク楽しいですね、勝負になっているときは。native はなかなか手がついていませんが、こうしてみると Kinx の一つの特徴(個性)でもあるので、大切に育てていきたいですね。頑張ろう。

ではまた次回。


P.S.
ちなみに、計測に使ったスクリプトはこれです。最近実装した Process を使ってみました。上記の数値は average の所に出てきた数値です。

mandbench.kx
using Process;

var count = 10;
var command = [$$[1], $$[2]];
var r = [];
var re = /[0-9]+\.[0-9]+/;

for (var i = 0; i < count; ++i) {
    var result = "";
    var [r1, w1] = new Pipe();
    var p1 = new Process(command, { out: w1 }).run();
    w1.close();
    while (p1.isAlive() || r1.peek() > 0) {
        var buf = r1.read();
        if (buf.length() < 0) {
            System.println("Error...");
            return 1;
        } else if (buf.length() > 0) {
            result += buf;
        } else {
            // System.println("no input...");
        }
    }

    re.reset(result);
    if (re.find()) {
        r.push(Double.parseDouble(re.group[0].string));
    }
}
var total = r.reduce(&(r, e) => r + e);
System.println("total  : %8.3f" % total);
System.println("count  : %8d" % r.length());
System.println("average: %8.3f" % (total / r.length()));
1
1
4

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?