torstai 16. kesäkuuta 2016

Timewasting: Sieve benchmarks

A benchmark in various languages to get feel of the speed differences of modern language implementations when running straightforward integer and array code. The classic Sieve. Yes, I know very well that one should not draw too many conclusions from test like this, but it was interesting and educational, especially since I am not very familiar with all of the languages. As the header comments say, these are ported versions of http://rsb.info.nih.gov/nih-image/java/benchmarks/Sieve.java. The blog has mangled some of the indentation (Blogger does not seem to have a "preformatted" or "code" paragraph style), but I'm not going to beautify these here manually.
The benchmarks time themselves so that they run for 10 seconds, and report the number of rounds (so bigger number is better). I ran these about 5 times and took the median. The surprising result was that Java beat C++ (JDK 1.7 vs GCC 4.8.3), but try for yourself! I'm intentionally not giving my exact scores.

Java:

// Sieve benchmark, based on the code at
// http://rsb.info.nih.gov/nih-image/java/benchmarks/Sieve.java
// but modified to run without GUI.
// Also used "do {} while" loop instead of "while(true){...if() break;}".
// Surprisingly, the "do {} while" is noticeably faster.
// It also matters is SIZE is marked "final" or not. The "final" is
// about 30% faster!

public class sieve {

static void runSieve() {
    final int SIZE = 8190;
    boolean flags[] = new boolean[SIZE+1];
    int i, prime, k, count;
    int iterations = 0;
    double seconds = 0.0;
    int score = 0;
    long startTime, elapsedTime;

    startTime = System.currentTimeMillis();
    do {
        count=0;
        for(i=0; i<=SIZE; i++) flags[i]=true;
        for (i=0; i<=SIZE; i++) {
        if(flags[i]) {
        prime=i+i+3;
        for(k=i+prime; k<=SIZE; k+=prime)
            flags[k]=false;
        count++;
        }
        }
        iterations++;
        elapsedTime = System.currentTimeMillis() - startTime;
    } while (elapsedTime < 10000);
    seconds = elapsedTime / 1000.0;
    score = (int) Math.round(iterations / seconds);
    System.out.println(iterations + " iterations in " + seconds + " seconds");
    if (count != 1899)
        System.out.println("Error: count <> 1899");
    else
        System.out.println("Sieve score = " + score);
}

public static void main(String[] args) {
    runSieve();
}

}


C++:

// Sieve benchmark, based on the code at
// http://rsb.info.nih.gov/nih-image/java/benchmarks/Sieve.java
// but modified to run as a C++ program (for comparison with Java and
// JavaScript. Trying to keep the program as similar as possible to the
// language versions.
// Function round() avoided because Visual C++ (at least older versions)
// does not have it.

#include <stdio.h>
#include <time.h>
#include <math.h>

class sieve {

public:
static void runSieve() {
    const int SIZE = 8190;
    bool *flags = new bool[SIZE+1];
    int i, prime, k, count;
    int iterations = 0;
    double seconds = 0.0;
    int score = 0;
    time_t startTime, elapsedTime, t0;

    // Due to only 1s resolution, wait until the clock ticks before test
    t0 = startTime = time(NULL);
    while (startTime == t0)
    startTime = time(NULL);
    do {
        count=0;
        for(i=0; i<=SIZE; i++) flags[i]=true;
        for (i=0; i<=SIZE; i++) {
        if(flags[i]) {
        prime=i+i+3;
        for(k=i+prime; k<=SIZE; k+=prime)
            flags[k]=false;
        count++;
        }
        }
        iterations++;
        elapsedTime = time(NULL) - startTime;
    } while (elapsedTime < 10);
    seconds = (double)elapsedTime;
    score = floor(iterations / seconds + 0.5);
    printf("%d iterations in %6.3f seconds\n", iterations, seconds);
    if (count != 1899)
        printf("Error: count <> 1899\n");
    else
        printf("Sieve score = %d\n", score);
}

};


int main() {
    sieve::runSieve();
    return 0;
}


JavaScript (run in Node.js):

// http://rsb.info.nih.gov/nih-image/java/benchmarks/Sieve.java
// but modified to run in Javascript, inside node/js ("node sieve.js")

function runSieve() {
    var SIZE = 8190;
    var flags = new Array(SIZE);
    var i, prime, k, count;
    var iterations = 0;
    var seconds = 0.0;
    var score = 0;
    var startTime, elapsedTime;

    //startTime = System.currentTimeMillis();
    startTime = Date.now();
    do {
        count=0;
        for(i=0; i<=SIZE; i++) flags[i]=true;
        for (i=0; i<=SIZE; i++) {
        if(flags[i]) {
        prime=i+i+3;
        for(k=i+prime; k<=SIZE; k+=prime)
            flags[k]=false;
        count++;
        }
        }
        iterations++;
        elapsedTime = Date.now() - startTime;
    } while (elapsedTime < 10000);
    seconds = elapsedTime / 1000.0;
    score = Math.round(iterations / seconds);
    console.log(iterations + " iterations in " + seconds + " seconds");
    if (count != 1899)
        console.log("Error: count <> 1899");
    else
        console.log("Sieve score = " + score);
}

runSieve();

Python (2.7):

import time

def runSieve():
    SIZE = 8190
    flags = [None] * (SIZE+1)
    i = 0;
    prime  = 0;
    k = 0
    count = 0
    iterations = 0
    seconds = 0.0
    score = 0
    startTime = 0
    elapsedTime = 0
    t0 = 0


    # Due to only 1s resolution, wait until the clock ticks before test
    startTime = time.clock();
    t0 = startTime
    while startTime == t0:
        startTime = time.clock()
    while elapsedTime < 10:
            count=0;
            #for i in range(SIZE):
        #    flags[i]=True
        # This is faster array init than the above loop
        flags = [True] * (SIZE+1)
            for i in range(SIZE):
                if flags[i]:
                prime=i+i+3
                k=i+prime
                while k<=SIZE:
                        flags[k]=False
                    k+=prime
                count+=1;
            iterations+=1
        elapsedTime = time.clock() - startTime

    seconds = elapsedTime
    score = round(iterations / seconds)
    print "%d iterations in %6.3f seconds" % (iterations, seconds)
    if count != 1899:
        print "Error: count <> 1899"
    else:
            print "Sieve score = %d" % score

runSieve()


Perl:

## Sieve benchmark, based on the code at
## http://rsb.info.nih.gov/nih-image/java/benchmarks/Sieve.java
## but modified to run as perl program (for comparison with Java and
## JavaScript. Trying to keep the program as similar as possible to the
## language versions.

sub runSieve {
    my $SIZE = 8190;
    my @flag; $flags[$SIZE+1] = 0;
    my ($i, $prime, $k, $count);
    my $iterations = 0;
    my $seconds = 0.0;
    my $score = 0;
    my ($startTime, $elapsedTime, $t0);

    # Due to only 1s resolution, wait until the clock ticks before test
    $startTime = time();
    $t0 = $startTime;
    while ($startTime == $t0) {
    $startTime = time();
    }
    do {
        $count=0;
        for($i=0; $i<=$SIZE; $i++) {
            $flags[$i]=1;
        }
        for ($i=0; $i<=$SIZE; $i++) {
        if($flags[$i]) {
        $prime=$i+$i+3;
        for($k=$i+$prime; $k<=$SIZE; $k+=$prime) {
            $flags[$k]=0;
        }
        $count++;
        }
        }
        $iterations++;
        $elapsedTime = time() - $startTime;
    } while ($elapsedTime < 10);
    $seconds = $elapsedTime + 0.0;
    $score = int($iterations / $seconds + 0.5);
    printf("%d iterations in %6.3f seconds\n", $iterations, $seconds);
    if ($count != 1899) {
        printf("Error: count <> 1899\n");
    }
    else {
        printf("Sieve score = %d\n", $score);
    }
}

runSieve();
 

Groovy:
// Sieve benchmark, based on the code at
// http://rsb.info.nih.gov/nih-image/java/benchmarks/Sieve.java
// but groovyized and modified to run without GUI.
// Groovy does not support do...while so that optimization for Java was
// undone.
// Much slower than either Java or JavaScript(on node/js) version.


def runSieve() {
    final int SIZE = 8190;
    boolean[] flags = new boolean[SIZE+1];
    int i, prime, k, iter, count;
    int iterations = 0;
    double seconds = 0.0;
    int score = 0;
    long startTime, elapsedTime = 0;

    startTime = System.currentTimeMillis();
    while (elapsedTime < 10000) {
        count=0;
        for(i=0; i<=SIZE; i++) flags[i]=true;
        for (i=0; i<=SIZE; i++) {
        if(flags[i]) {
        prime=i+i+3;
        for(k=i+prime; k<=SIZE; k+=prime)
            flags[k]=false;
        count++;
        }
        }
        iterations++;
        elapsedTime = System.currentTimeMillis() - startTime;
    }
    seconds = elapsedTime / 1000.0;
    score = (int) Math.round(iterations / seconds);
    println(iterations + " iterations in " + seconds + " seconds");
    if (count != 1899)
        println("Error: count <> 1899");
    else
        println("Sieve score = " + score);
}

runSieve()

Ei kommentteja:

Lähetä kommentti