VMS Benchmark - compiler optimization

Content:

  1. Introduction
  2. Conclusion
  3. Context
  4. Results
  5. Code

Introduction:

Discussion has come up a few times in comp.os.vms/INFO-VAX about relative speed of various languages on VMS.

I posted some numbers and some code.

This is just a summary of those posts with a few more details and comments.

Some may find the conclusions interesting.

Disclaimer: this is a micro-benchmark. Some may even call it a nano-benchmark. It measures optimization of a handful of lines with a rather silly calculation that does not reflect any real world calculation. It is chosen because it is easy to port between different languages. But be prepared for significant differences between this benchmark and your real application.

Conclusion:

It may be unusual to start with the conclusion, but here it is:

Optimization Compilers/interpreters
Very fast clang with high optimization
Java 8
Fast traditional (Fortran, Pascal, Basic, C, C++)
clang without high optimization
Ada without checks and with high optimization
Medium Ada with checks and without high optimization
Java 5
Groovy with @CompileStatic
Slow Jython
Groovy without @CompileStatic
Very slow Python

Nothing particular surprising.

For the traditional VMS languages (C non-clang, Fortran, Pascal, Basic) the release compilers are signficantly faster than the Field Test (FT) compilers.

The difference in Java 5 on VMS Alpha and Java 8 on VMS Itanium/VMS x86-64 is likely due to 3 factors:

Results:

Software versions:

VMS compilers/interpreters
VMS 8.4-2 Alpha C 7.4
C++ 7.4
Fortran 8.3
Pascal 6.2
Basic 1.8
GNAT 3.12
Java 5
Python 2.7
Jython 2.5 on Java 5
VMS 8.4-2 Itanium C 7.4
C++ 7.4
Fortran 8.3
Pascal 6.2
Basic 1.8
Java 8
Python 3.10
Jython 2.7 on Java 8
Groovy 4.0 on Java 8
VMS 9.2-1 x86-64 C 7.4 (*)
C++/clang 10.0 (*)
Fortran 8.5 (*)
Pascal 6.3 (*)
Java 8 (*)
Jython 2.7 on Java 8 (*)
Groovy 4.0 on Java 8 (*)
VMS 9.2-2 x86-64 C 7.5
C++/clang 10.0
Fortran 8.5
Pascal 6.3
Basic 1.8 (*)
Java 8
Python 3.10 (*)
Jython 2.7 on Java 8
Groovy 4.0 on Java 8

*) Field test version.

Integer operations:

Million operations per second (rounded):

Language VMS 8.4-2L2
Alpha simulator
Xeon @ 3.7 GHz
VMS 8.4-2L3
Itanium @ 1.6 Ghz
VMS 9.2-1
FT compilers
VMWare Player
Xeon @ 3.7 GHz
VMS 9.2-2
Release compilers
VMWare Player
Xeon @ 3.7 GHz
C
with /OPT=LEVEL:5
clang
clang with -O3
56
56
N/A
N/A
246
246
N/A
N/A
136
136
133
784
543
543
135
783
C++
with /OPT=LEVEL:5
clang
clang with -O3
59
59
N/A
N/A
246
246
N/A
N/A
132
786
135
769
135
784
135
787
Fortran
with /OPT=LEVEL:5
60
60
246
246
136
138
546
546
Pascal 63 246 136 537
Basic
with /NOCHECK
44
60
45
246
N/A
N/A
719
546
Ada
with -gnatp -O3
21
70
N/A
N/A
N/A
N/A
N/A
N/A
Java 28 286 707 711
Python 0.14 0.64 N/A 3.49
Jython 0.08 3.05 16 17
Groovy
with @CompileStatic
N/A
N/A
3
19
13
64
13
131

That the Basic code is faster without /NOCHECK must be an anommly - I will retest when release version of Basic becomes available.

Floating point operations:

Million operations per second (rounded):

Language VMS 8.4-2L2
Alpha simulator
Xeon @ 3.7 GHz
VMS 8.4-2L3
Itanium @ 1.6 Ghz
VMS 9.2-1
FT compilers
VMWare Player
Xeon @ 3.7 GHz
VMS 9.2-2
Release compilers
VMWare Player
Xeon @ 3.7 GHz
C
with /OPT=LEVEL:5
clang
clang with -O3
12
11
N/A
N/A
108
108
N/A
N/A
204
204
142
268
270
270
141
271
C++
with /OPT=LEVEL:5
clang
clang with -O3
11
11
N/A
N/A
108
108
N/A
N/A
142
(optimized away)
144
265
145
(optimized away)
143
274
Fortran
with /OPT=LEVEL:5
10
11
108
108
204
204
270
270
Pascal 12 108 204 270
Basic
with /NOCHECK
12
12
77
79
N/A
N/A
46
45
Ada
with -gnatp -O3
10
15
N/A
N/A
N/A
N/A
N/A
N/A
Java 6 107 270 268
Python 0.07 0.70 N/A 2.84
Jython 0.09 2.65 20 26
Groovy
with @CompileStatic
N/A
N/A
3
107
16
218
14
205

String operations:

Note that string operation speed is less about compiler optimization and more about string representation and string processing paradigms. The code tries to use strings as strings are typical used in the language - not what could be done for optimal performance. But I still consider the results a bit random.

Million operations per second (rounded):

Language VMS 8.4-2L2
Alpha simulator
Xeon @ 3.7 GHz
VMS 8.4-2L3
Itanium @ 1.6 Ghz
VMS 9.2-1
FT compilers
VMWare Player
Xeon @ 3.7 GHz
VMS 9.2-2
Release compilers
VMWare Player
Xeon @ 3.7 GHz
C
with /OPT=LEVEL:5
clang
clang with -O3
0.26
0.26
N/A
N/A
1.94
1.94
N/A
N/A
2.60
2.60
2.20
2.80
2.70
2.70
2.27
2.93
C++
with /OPT=LEVEL:5
clang
clang with -O3
0.01
0.01
N/A
N/A
0.40
0.40
N/A
N/A
1.80
2.30
1.70
2.40
2.00
2.06
1.74
2.00
Fortran
with /OPT=LEVEL:5
0.46
0.46
6.95
6.95
0.00
0.00
0.00
0.00
Pascal 0.01 0.32 0.15 0.16
Basic
with /NOCHECK
0.01
0.01
0.13
0.13
N/A
N/A
0.14
0.14
Ada
with -gnatp -O3
0.01
0.01
N/A
N/A
N/A
N/A
N/A
N/A
Java 0.08 1.77 16.00 17.00
Python 0.01 0.06 N/A
Jython 0.01 0.34 1.60 1.50
Groovy
with @CompileStatic
N/A
N/A
0.19
0.56
1.20
2.90
1.00
3.00

Code:

If you want everything to test yourself, then get this kit - it has all source, scripts etc..

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>

#include "high_res_timer.h"

void printres(TIMECOUNT_T t1, TIMECOUNT_T t2, int n1, int n2, char *ops)
{
    double xperf;
    xperf = (double)n1 * (double)n2 / ((t2 - t1) * 1.0 / UNITS_PER_SECOND);
    printf("%.4f million %s per second\n", xperf / 1000000, ops);
}

#define NINT 10000
#define NFP 1000
#define NSTR 100
#define N 1000000

void testint(int scale)
{
    int i, j;
    int nintscale, sum;
    TIMECOUNT_T t1, t2;
    nintscale = NINT / scale;
    t1 = GET_TIMECOUNT;
    for(i = 0; i < nintscale; i++)
    {
        sum = i;
        for(j = 0; j < N; j++)
        {
            sum = ((sum + 1) * 2 + 1) / 2;
        }
        if(sum != (i + N))
        {
            printf("Integer test error\n");
            exit(0);
        }
    }
    t2 = GET_TIMECOUNT;
    printres(t1, t2, nintscale, N, "integer operations");
}

void testfp(int scale)
{
    int i, j;
    int nfpscale;
    double sum;
    TIMECOUNT_T t1, t2;
    nfpscale = NFP / scale;
    t1 = GET_TIMECOUNT;
    for(i = 0; i < nfpscale; i++)
    {
        sum = i;
        for(j = 0; j < N; j++)
        {
            sum = ((sum + 1) * 2 + 1) / 2;
        }
        if(fabs(sum - (i + 1.5 * N)) > 1)
        {
            printf("Floating point test error\n");
            exit(0);
        }
    }
    t2 = GET_TIMECOUNT;
    printres(t1, t2, nfpscale, N, "floating point operations");
}

#define ALFA "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
#define VMS_FACTOR 100

void teststr(int scale)
{
    int i, j;
    int ix, ix1, ix2;
    int nstrscale;
    char s[1000], buf[N+1];
    TIMECOUNT_T t1, t2;
    nstrscale = NSTR / scale;
    t1 = GET_TIMECOUNT;
    for(i = 0; i < nstrscale * VMS_FACTOR; i++)
    {
        strcpy(buf, "");
        for(j = 0; j < N / VMS_FACTOR; j = j + 10)
        {
            strcpy(s, ALFA);
            strcat(s, ALFA);
            ix = (i + j) % strlen(ALFA);
            strncat(buf + j, s + ix, 1);
            strncat(buf + j, s + ix + 1, 2);
            strncat(buf + j, s + ix + 3, 3);
            strncat(buf + j, s + ix + 6 , 4);
        }
        ix1 = (N / VMS_FACTOR) / 3;
        ix2 = 2 * (N / VMS_FACTOR) / 3; 
        if(strlen(buf) != (N / VMS_FACTOR) || buf[ix1] != ALFA[(i + ix1) % strlen(ALFA)] || buf[ix2] != ALFA[(i + ix2) % strlen(ALFA)])
        {
            printf("String test error\n");
            exit(0);
        }
    }
    t2 = GET_TIMECOUNT;
    printres(t1, t2, nstrscale, N / 10, "string operations");
}

#define REP 10

int main(int argc, char *argv[])
{
    int i;
    int scale;
    printf("C:\n");
    if(argc > 1)
    {
        scale = atoi(argv[1]);
    }
    else
    {
        scale = 1;
    }
    for(i = 0; i < REP; i++)
    {
        testint(scale);
    }
    for(i = 0; i < REP; i++)
    {
        testfp(scale);
    }
    for(i = 0; i < REP; i++)
    {
        teststr(scale);
    }
    return 0;
}
#include "high_res_timer.h"

#include <iostream>
#include <iomanip>
#include <ios>
#include <string>
#include <cmath>
#include <cstdlib>

using namespace std;

void printres(TIMECOUNT_T t1, TIMECOUNT_T t2, int n1, int n2, const char *ops)
{
    double xperf = (double)n1 * (double)n2 / ((t2 - t1) * 1.0 / UNITS_PER_SECOND);
    cout << fixed << setprecision(4) << (xperf / 1000000) << " million " << ops << " per second" << endl;
}

const int NINT = 10000;
const int NFP = 1000;
const int NSTR = 100;
const int N = 1000000;

void testint(int scale)
{
    int nintscale = NINT / scale;
    TIMECOUNT_T t1 = GET_TIMECOUNT;
    for(int i = 0; i < nintscale; i++)
    {
        int sum = i;
        for(int j = 0; j < N; j++)
        {
            sum = ((sum + 1) * 2 + 1) / 2;
        }
        if(sum != (i + N))
        {
            cout << "Integer test error" << endl;
            exit(0);
        }
    }
    TIMECOUNT_T t2 = GET_TIMECOUNT;
    printres(t1, t2, nintscale, N, "integer operations");
}

void testfp(int scale)
{
    int nfpscale = NFP / scale;
    TIMECOUNT_T t1 = GET_TIMECOUNT;
    for(int i = 0; i < nfpscale; i++)
    {
        double sum = i;
        for(int j = 0; j < N; j++)
        {
            sum = ((sum + 1) * 2 + 1) / 2;
        }
        if(fabs(sum - (i + 1.5 * N)) > 1)
        {
            cout << "Floating point test error" << endl;
            exit(0);
        }
    }
    TIMECOUNT_T t2 = GET_TIMECOUNT;
    printres(t1, t2, nfpscale, N, "floating point operations");
}

const string ALFA = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
const int VMS_FACTOR = 100;

void teststr(int scale)
{
    int nstrscale = NSTR / scale;
    TIMECOUNT_T t1 = GET_TIMECOUNT;
    for(int i = 0; i < nstrscale * VMS_FACTOR; i++)
    {
        string s, buf;
        buf = "";
        for(int j = 0; j < N / VMS_FACTOR; j = j + 10)
        {
            s = ALFA + ALFA;
            int ix = (i + j) % ALFA.length();
            //buf = buf + s.substr(ix, 1) + s.substr(ix + 1, 2) + s.substr(ix + 3, 3) + s.substr(ix + 6, 4);
            buf.append(s, ix, 1);
            buf.append(s, ix + 1, 2);
            buf.append(s, ix + 3, 3);
            buf.append(s, ix + 6, 4);
        }
        int ix1 = (N / VMS_FACTOR) / 3;
        int ix2 = 2 * (N / VMS_FACTOR) / 3; 
        if(buf.length() != (N / VMS_FACTOR) || buf[ix1] != ALFA[(i + ix1) % ALFA.length()] || buf[ix2] != ALFA[(i + ix2) % ALFA.length()])
        {
            cout << "String test error" << endl;
            exit(0);
        }
    }
    TIMECOUNT_T t2 = GET_TIMECOUNT;
    printres(t1, t2, nstrscale, N / 10, "string operations");
}

#define REP 10

int main(int argc, char *argv[])
{
    int i;
    int scale;
    cout << "C++:" << endl;
    if(argc > 1)
    {
        scale = atoi(argv[1]);
    }
    else
    {
        scale = 1;
    }
    for(i = 0; i < REP; i++)
    {
        testint(scale);
    }
    for(i = 0; i < REP; i++)
    {
        testfp(scale);
    }
    for(i = 0; i < REP; i++)
    {
        teststr(scale);
    }
    return 0;
}
      program native_test
      integer*4 REP
      parameter (REP = 10)
      integer*4 i, scale
      character*256 cmdlin
      integer*4 cmdlinlen
      write(*,*) 'Fortran:'
      call lib$get_foreign(cmdlin,,cmdlinlen)
      if(cmdlinlen.gt.0) then
        read(cmdlin(1:cmdlinlen),'(I)') scale
      else
        scale = 1
      end if
      do 100 i =1,REP
        call testint(scale)
100   continue
      do 200 i =1,REP
        call testfp(scale)
200   continue
      do 300 i =1,REP
        call teststr(scale)
300   continue
      end
c
      subroutine testint(scale)
      integer*4 scale
      integer*4 NINT,N
      parameter (NINT = 10000, N = 1000000)
      integer*4 i, j, nintscale, sum
      integer*8 t1, t2
      nintscale = NINT / scale
      call sys$gettim(t1)
      do 200 i = 1, nintscale
        sum = i - 1
        do 100 j = 1, N
          sum = ((sum + 1) * 2 + 1) / 2
100     continue
        if(sum.ne.(i - 1 + N)) then
          write(*,*) 'Integer test error'
          stop
        end if
200   continue
      call sys$gettim(t2)
      call printres(t1, t2, nintscale, N, 'integer operations')
      end
c
      subroutine testfp(scale)
      integer*4 scale
      integer*4 NFP, N
      parameter (NFP = 1000, N = 1000000)
      integer*4 i, j, nfpscale
      real*8 sum
      integer*8 t1, t2
      nfpscale = NFP / scale
      call sys$gettim(t1)
      do 200 i = 1, nfpscale
        sum = i - 1
        do 100 j = 1, N
          sum = ((sum + 1) * 2 + 1) / 2
100     continue
        if(abs(sum - (i - 1 + 1.5 * N)).gt.1) then
          write(*,*) 'Floating point test error'
          stop
        end if
200   continue
      call sys$gettim(t2)
      call printres(t1, t2, nfpscale, N, 'floating point operations')
      end
c
      subroutine teststr(scale)
      integer*4 scale
      integer*4 NSTR, N, VMS_FACTOR
      character*26 ALFA
      parameter (NSTR = 100, N = 1000000,
     +           ALFA = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', VMS_FACTOR = 100)
      integer*4 i, j, nstrscale, slen, ix, ix1, ix2, ix1m, ix2m
      character*52 s
      character*32767 buf
      integer*8 t1, t2
      nstrscale = NSTR / scale
      call sys$gettim(t1)
      do 200 i = 1, nstrscale * VMS_FACTOR
        slen = 0
        do 100 j = 1, N / VMS_FACTOR, 10
          s = ALFA // ALFA
          ix = mod(i - 1 + j - 1, len(ALFA))
          buf(slen:slen+9) = s(ix + 1:ix + 1) // s(ix + 2:ix + 3) //
     +      s(ix + 4: ix + 6) // s(ix + 7:ix + 10)
          slen = slen + 10
100     continue
        ix1 = (N / VMS_FACTOR) / 3
        ix2 = 2 * (N / VMS_FACTOR) / 3
        ix1m = mod(i - 1 + ix1, len(ALFA)) + 1
        ix2m = mod(i - 1 + ix2, len(ALFA)) + 1
        if((slen.ne.(N / VMS_FACTOR)).or.
     +     (buf(ix1:ix1).ne.ALFA(ix1m:ix1m)).or.
     +     (buf(ix2:ix2).ne.ALFA(ix2m:ix2m))) then
          write(*,*) 'String test error'
          stop
        end if
200   continue
      call sys$gettim(t2)
      call printres(t1, t2, nstrscale, N / 10, 'string operations')
      end
c
      subroutine printres(t1, t2, n1, n2, ops)
      integer*8 t1, t2
      integer*4 n1, n2
      character*(*) ops
      real*8 xperf
      xperf = n1 * 1.0 * n2 / ((t2 - t1) / 10000000.0)
      write(*,'(1x,f9.4,9h million ,a,11h per second)')
     +  (xperf / 1000000), ops
      end
[inherit('sys$library:pascal$lib_routines')]
program native_test(input, output);

type
    string = varying[32767] of char;

procedure printres(t1, t2 : Cardinal; n1, n2 : integer; ops : string);

var
    xperf : double;

begin
    xperf := n1 * 1.0 * n2 / ((t2 - t1) / 1000.0);
    writeln((xperf / 1000000):1:4, ' million ', ops, ' per second');
end;

const
    NINT = 10000;
    NFP = 1000;
    NSTR = 100;
    N = 1000000;

procedure testint(scale : integer);

var
    i, j, nintscale, sum : integer;
    t1, t2 : Cardinal;

begin
    nintscale := NINT div scale;
    t1 := Clock;
    for i := 1 to nintscale do begin
        sum := i - 1;
        for j := 1 to N do begin
            sum := ((sum + 1) * 2 + 1) div 2;
        end;
        if sum <> (i - 1 + N) then begin
            writeln('Integer test error');
            halt;
        end;
    end;
    t2 := Clock;
    printres(t1, t2, nintscale, N, 'integer operations');
end;

procedure testfp(scale : integer);

var
    i, j, nfpscale : integer;
    sum : double;
    t1, t2 : Cardinal;

begin
    nfpscale := NFP div scale;
    t1 := Clock;
    for i := 1 to nfpscale do begin
        sum := i - 1;
        for j := 1 to N do begin
            sum := ((sum + 1) * 2 + 1) / 2;
        end;
        if abs(sum - (i - 1 + 1.5 * N)) > 1 then begin
            writeln('Floating point test error');
            halt;
        end;
    end;
    t2 := Clock;
    printres(t1, t2, nfpscale, N, 'floating point operations');
end;

const
    ALFA = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
    VMS_FACTOR = 100;

procedure teststr(scale : integer);

var
    i, j, nstrscale, ix, ix1, ix2 : integer;
    s, buf : string;
    t1, t2 : Cardinal;

begin
    nstrscale := NSTR div scale;
    t1 := Clock;
    for i := 1 to nstrscale * VMS_FACTOR do begin
        buf := '';
        for j := 1 to ((N div VMS_FACTOR) div 10) do begin
            s := ALFA + ALFA;
            ix := (i - 1 + 10 * (j - 1)) mod length(ALFA);
            buf := buf + substr(s, ix + 1, 1) + substr(s, ix + 2, 2) + substr(s, ix + 4, 3) + substr(s, ix + 7, 4);
        end;
        ix1 := (N div VMS_FACTOR) div 3;
        ix2 := 2 * (N div VMS_FACTOR) div 3;
        if (length(buf) <> (N div VMS_FACTOR)) or
           (buf[ix1] <> ALFA[((i - 1 + ix1 - 1) mod length(ALFA)) + 1]) or
           (buf[ix2] <> ALFA[((i - 1 + ix2 - 1) mod length(ALFA)) + 1]) then begin
            writeln('String test error');
            halt;
        end;
    end;
    t2 := Clock;
    printres(t1, t2, nstrscale, N div 10, 'string operations');
end;

const
    REP = 10;

var
    i, scale : integer;
    cmdlin : string;

begin
    writeln('Pascal:');
    lib$get_foreign(resultant_string:=cmdlin.body,resultant_length:=cmdlin.length);
    if cmdlin.length > 0 then begin
        readv(cmdlin, scale);
    end else begin
        scale := 1;
    end;
    for i := 1 to REP do begin
        testint(scale);
    end;
    for i := 1 to REP do begin
        testfp(scale);
    end;
    for i := 1 to REP do begin
        teststr(scale);
    end;
end.
program native_test

declare integer constant REP = 10
declare integer i, xscale
declare string cmdlin
external long function lib$get_foreign(string)
external sub testint(integer)
external sub testfp(integer)
external sub teststr(integer)

print "Basic:"
call lib$get_foreign(cmdlin)
if len(cmdlin) > 0 then
    xscale = val%(cmdlin)
else
    xscale = 1
end if
for i = 1 to REP
    call testint(xscale)
next i
for i = 1 to REP
    call testfp(xscale)
next i
for i = 1 to REP
    call teststr(xscale)
next i

end program
!
sub testint(integer xscale)

declare integer constant NINT = 10000
declare integer constant N = 1000000
declare integer i, j, nintscale, sum
declare quad t1, t2
external sub printres(quad, quad, integer, integer, string)
external long function sys$gettim(quad)

nintscale = NINT / xscale
call sys$gettim(t1)
for i = 1 to nintscale
    sum = i - 1%
    for j = 1 to N
        sum = ((sum + 1%) * 2% + 1%) / 2%
    next j
    if sum <> (i - 1% + N) then
        print "Integer test error"
        stop
    end if
next i
call sys$gettim(t2)
call printres(t1, t2, nintscale, N, "integer operations")

end sub
!
sub testfp(integer xscale)

declare integer constant NFP = 1000
declare integer constant N = 1000000
declare integer i, j, nfpscale
declare double sum
declare quad t1, t2
external sub printres(quad, quad, integer, integer, string)

nfpscale = NFP / xscale
call sys$gettim(t1)
for i = 1 to nfpscale
    sum = i - 1.0
    for j = 1 to N
        sum = ((sum + 1.0) * 2.0 + 1.0) / 2.0
    next j
    if abs(sum - (i - 1.0 + 1.5 * N)) > 1 then
        print "Floating point test error"
        stop
    end if
next i
call sys$gettim(t2)
call printres(t1, t2, nfpscale, N, "floating point operations")

end sub
!
sub teststr(integer xscale)

declare integer constant NSTR = 100
declare integer constant N = 1000000
declare string constant ALFA = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
declare integer constant VMS_FACTOR = 100
declare integer i, j, nstrscale, ix1, ix2
declare string s, buf
declare quad t1, t2
external sub printres(quad, quad, integer, integer, string)

nstrscale = NSTR / xscale
call sys$gettim(t1)
for i = 1 to nstrscale * VMS_FACTOR
    buf = ""
    for j = 1 to (N / VMS_FACTOR) / 10
        s = ALFA + ALFA
        ix = mod(i + 1 + 10 * (j - 1), len(ALFA))
        buf = buf + mid(s, ix + 1, 1) + mid(s, ix + 2, 2) + mid(s, ix + 4, 3) + mid(s, ix + 7, 4)
    next j
    ix1 = (N / VMS_FACTOR) / 3
    ix2 = 2 * (N / VMS_FACTOR) / 3
    if((len(buf) <> (N / VMS_FACTOR)) or &
       (mid(buf, ix1, 1) <> mid(ALFA, mod(i + ix1, len(ALFA)) + 1, 1)) or &
       (mid(buf, ix2, 1) <> mid(ALFA, mod(i + ix2, len(ALFA)) + 1, 1))) then
        print "String test error"
        stop
    end if
next i
call sys$gettim(t2)
call printres(t1, t2, nstrscale, N / 10, "string operations")

end sub
!
sub printres(quad t1, quad t2, integer n1, integer n2, string ops)

declare double xperf

xperf = n1 * 1.0 * n2 / ((t2 - t1) / 10000000.0)
print using "####.#### million 'E per second", (xperf / 1000000), ops

end sub
with Ada.Command_line,
     Ada.Task_Identification,
     Ada.Strings.Unbounded,
     Ada.Text_IO,
     Ada.Integer_Text_IO,
     Ada.Float_Text_IO,
     Ada.Real_Time;

use Ada.Command_line,
    Ada.Task_Identification,
    Ada.Strings.Unbounded,
    Ada.Text_IO,
    Ada.Integer_Text_IO,
    Ada.Float_Text_IO,
    Ada.Real_Time;

procedure Native_Test is

procedure Print_Res(t1, t2 : Time; n1, n2 : Integer; ops: String) is

xperf : Float;

begin
    xperf := Float(n1) * Float(n2) / Float(To_Duration(t2 - t1));
    Put(xperf / 1000000.0, Aft => 4, Exp => 0);
    Put(" million ");
    Put(ops);
    Put(" per second");
    New_Line;
end Print_Res;

NINT : constant Integer := 10000;
NFP : constant Integer := 1000;
NSTR : constant Integer := 100;
N : constant Integer := 1000000;

procedure Test_Int(scale : Integer) is

nintscale, sum : Integer;
t1, t2 : Time;
tsk : Task_Id;

begin
    nintscale := NINT / scale;
    t1 := Clock;
    for i in 1..nintscale loop
        sum := i - 1;
        for j in 1..N loop
            sum := ((sum + 1) * 2 + 1) / 2;
        end loop;
        if sum /= (i - 1 + N) then
            Put_Line("Integer test error");
            tsk := Current_Task;
            Abort_Task(tsk);
        end if;
    end loop;
    t2 := Clock;
    Print_Res(t1, t2, nintscale, N, "integer operations");
end Test_Int;

procedure Test_FP(scale : Integer) is

nfpscale : Integer;
sum : Long_Float;
t1, t2 : Time;
tsk : Task_Id;

begin
    nfpscale := NFP / scale;
    t1 := Clock;
    for i in 1..nfpscale loop
        sum := Long_Float(i - 1);
        for j in 1..N loop
            sum := ((sum + 1.0) * 2.0 + 1.0) / 2.0;
        end loop;
        if abs (sum - (Long_Float(i - 1) + 1.5 * Long_Float(N))) > 1.0 then
            Put_Line("Floating point test error");
            tsk := Current_Task;
            Abort_Task(tsk);
        end if;
    end loop;
    t2 := Clock;
    Print_Res(t1, t2, nfpscale, N, "floating point operations");
end Test_Fp;

procedure Test_Str(scale : Integer) is

ALFA : constant String := "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
VMS_FACTOR : constant Integer := 100;
s : constant String := ALFA & ALFA;
nstrscale, ix, ix1, ix2, ix1alfa, ix2alfa : Integer;
buf : Unbounded_String;
t1, t2 : Time;
tsk : Task_Id;

begin
    nstrscale := NSTR / scale;
    t1 := Clock;
    for i in 1..(nstrscale * VMS_FACTOR) loop
        buf := To_Unbounded_String("");
        for j in 1..(N / VMS_FACTOR) /10 loop
            ix := (i - 1 + 10 * (j - 1)) mod ALFA'Length;
            Append(buf, s(ix+1..ix+1));
            Append(buf, s(ix+2..ix+3));
            Append(buf, s(ix+4..ix+6));
            Append(buf, s(ix+7..ix+10));
        end loop;
        ix1 := (N / VMS_FACTOR) / 3;
        ix2 := 2 * (N / VMS_FACTOR) / 3;
        ix1alfa := (i - 1 + ix1 - 1) mod ALFA'Length + 1;
        ix2alfa := (i - 1 + ix2 - 1) mod ALFA'Length + 1;
        if To_String(buf)'Length /= (N / VMS_FACTOR) or To_String(buf)(ix1..ix1) /= ALFA(ix1alfa..ix1alfa) or To_String(buf)(ix2..ix2) /= ALFA(ix2alfa..ix2alfa) then
            Put_Line("String test error");
            tsk := Current_Task;
            Abort_Task (tsk);
        end if;
    end loop;
    t2 := Clock;
    Print_Res(t1, t2, nstrscale, N/10, "string operations");
end Test_Str;

REP : constant Integer := 10;
scale : Integer;

begin
    Put_Line("Ada:");
    if Argument_Count > 0 then
        scale := Integer'Value(Argument(1));
    else
        scale := 1;
    end if;
    for i in 1..REP loop
        Test_Int(scale);
    end loop;
    for i in 1..REP loop
        Test_FP(scale);
    end loop;
    for i in 1..REP loop
        Test_Str(scale);
    end loop;
end Native_Test;
import java.text.NumberFormat;
import java.text.DecimalFormat;

public class JvmTest {
    private static NumberFormat nf = new DecimalFormat("0.0000");
    private static void printres(long t1, long t2, int n1, int n2, String ops) {
        double xperf = (double)n1 * (double)n2 / ((t2 - t1) / 1000.0) ;
        String sperf = nf.format(xperf / 1000000);
        System.out.println(sperf + " million " + ops + " per second");
    }
    private final static int NINT = 10000;
    private final static int NFP = 1000;
    private final static int NSTR = 1000;
    private final static int N = 1000000;
    public static void testint(int scale) {
        int nintscale = NINT / scale;
        long t1 = System.currentTimeMillis();
        for(int i = 0; i < nintscale; i++) {
            int sum = i;
            for(int j = 0; j < N; j++) {
                sum = ((sum + 1) * 2 + 1) / 2;
            }
            if(sum != (i + N)) {
                System.out.println("Integer test error");
                System.exit(1);
            }
        }
        long t2 = System.currentTimeMillis();
        printres(t1, t2, nintscale, N, "integer operations");
    }
    public static void testfp(int scale) {
        int nfpscale = NFP / scale;
        long t1 = System.currentTimeMillis();
        for(int i = 0; i < nfpscale; i++) {
            double sum = i;
            for(int j = 0; j < N; j++) {
                sum = ((sum + 1) * 2 + 1) / 2;
            }
            if(Math.abs(sum - (i + 1.5 * N)) > 1) {
                System.out.println("Floating point test error");
                System.exit(1);
            }
        }
        long t2 = System.currentTimeMillis();
        printres(t1, t2, nfpscale, N, "floating point operations");
    }
    private final static String ALFA = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    private final static int VMS_FACTOR = 100;
    public static void teststr(int scale) {
        int nstrscale = NSTR / scale;
        long t1 = System.currentTimeMillis();
        for(int i = 0; i < nstrscale * VMS_FACTOR; i++) {
            StringBuffer sb = new StringBuffer("");
            for(int j = 0; j < N / VMS_FACTOR; j = j + 10) {
                String s = ALFA + ALFA;
                int ix = (i + j) % ALFA.length();
                sb.append(s.substring(ix, ix + 1) + s.substring(ix + 1, ix + 3) + s.substring(ix + 3, ix + 6) + s.substring(ix + 6, ix + 10));
            }
            int ix1 = (N / VMS_FACTOR) / 3;
            int ix2 = 2 * (N / VMS_FACTOR) / 3; 
            if(sb.length() != (N / VMS_FACTOR) || sb.charAt(ix1) != ALFA.charAt((i + ix1) % ALFA.length()) || sb.charAt(ix2) != ALFA.charAt((i + ix2) % ALFA.length())) {
                System.out.println("String test error");
                System.exit(1);
            }
        }
        long t2 = System.currentTimeMillis();
        printres(t1, t2, nstrscale, N / 10, "string operations");
    }
    private final static int REP = 10;
    public static void main(String[] args) {
        System.out.println("Java:");
        int scale = 1;
        if(args.length > 0) {
            scale = Integer.parseInt(args[0]);
        }
        for(int i = 0; i < REP; i++) {
            testint(scale);
        }
        for(int i = 0; i < REP; i++) {
            testfp(scale);
        }
        for(int i = 0; i < REP; i++) {
            teststr(scale);
        }
    }
}
import sys
import time
import math

def printres(t1, t2, n1, n2, ops):
    xperf = n1 * n2 / (t2 - t1)
    print('%.4f million %s per second' % (xperf / 1000000, ops))

NINT = 10000
NFP = 10000
NSTR = 10000
N = 10000

def testint(scale):
    nintscale = NINT // scale
    t1 = time.time()
    for i in range(nintscale):
        sum = i
        for j in range(N):
            sum = ((sum + 1) * 2 + 1) // 2
        if sum != (i + N):
            print('Integer test error')
            sys.exit(0)
    t2 = time.time()
    printres(t1, t2, nintscale, N, 'integer operations')

def testfp(scale):
    nfpscale = NFP // scale
    t1 = time.time()
    for i in range(nfpscale):
        sum = i
        for j in range(N):
            sum = ((sum + 1.0) * 2 + 1) / 2
        if math.fabs(sum - (i + 1.5 * N)) > 1:
            print('Floating point test error')
            sys.exit(0)
    t2 = time.time()
    printres(t1, t2, nfpscale, N, 'floating point operations')

ALFA = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
VMS_FACTOR = 100

def teststr(scale):
    nstrscale = NSTR // scale
    t1 = time.time()
    for i in range(nstrscale * VMS_FACTOR):
        sb = ''
        for j in range((N // VMS_FACTOR) // 10):
            realj = 10 * j
            s = ALFA + ALFA
            ix = (i + j) % len(ALFA)
            sb = sb + s[ix:ix+1] + s[ix+1:ix+3] + s[ix+3:ix+6] + s[ix+6:ix+10]
        ix1 = (N // VMS_FACTOR) // 3
        ix2 = 2 * (N // VMS_FACTOR) // 3
        mix1 = (i + ix1) % len(ALFA)
        mix2 = (i + ix2) % len(ALFA)
        if len(sb) != (N // VMS_FACTOR) or sb[ix1:ix1] != ALFA[mix1:mix1] or sb[ix2:ix2] != ALFA[mix2:mix2]:
            print('String test error')
            sys.exit(0)
    t2 = time.time()
    printres(t1, t2, nstrscale, N // 10, 'string operations')

REP = 10

print('Python:')
if len(sys.argv) > 1:
    scale = int(sys.argv[1])
else:
    scale = 1
for i in range(REP):
    testint(scale)
for i in range(REP):
    testfp(scale)
for i in range(REP):
    teststr(scale)
import java.text.DecimalFormat

nf = new DecimalFormat("0.00")

void printres(long t1, long t2, int n1, int n2, String ops) {
    xperf = n1 * n2 / ((t2 - t1) / 1000.0) 
    sperf = nf.format(xperf / 1000000)
    println("${sperf} million ${ops} per second")
}
    
NINT = 10000
NFP = 10000
NSTR = 10000
N = 10000
    
void testint(int scale) {
    nintscale = NINT.intdiv(scale)
    t1 = System.currentTimeMillis()
    for(i = 0; i < nintscale; i++) {
        int sum = i
        for(j = 0; j < N; j++) {
            sum = ((sum + 1) * 2 + 1).intdiv(2)
        }
        if(sum != (i + N)) {
            println("Integer test error")
            System.exit(1)
        }
    }
    t2 = System.currentTimeMillis()
    printres(t1, t2, nintscale, N, "integer operations")
}

void testfp(int scale) {
    nfpscale = NFP.intdiv(scale)
    t1 = System.currentTimeMillis()
    for(i = 0; i < nfpscale; i++) {
        double sum = i
        for(j = 0; j < N; j++) {
            sum = ((sum + 1) * 2 + 1) / 2
        }
        if(Math.abs(sum - (i + 1.5 * N)) > 1) {
            println("Floating point test error")
            System.exit(1)
        }
    }
    t2 = System.currentTimeMillis()
    printres(t1, t2, nfpscale, N, "floating point operations")
}

ALFA = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
VMS_FACTOR = 100

void teststr(int scale) {
    nstrscale = NSTR.intdiv(scale)
    t1 = System.currentTimeMillis()
    for(i = 0; i < nstrscale * VMS_FACTOR; i++) {
        sb = new StringBuffer("")
        for(j = 0; j < N.intdiv(VMS_FACTOR); j = j + 10) {
            s = ALFA + ALFA
            ix = (i + j) % ALFA.length()
            sb << (s[ix .. ix] + s[ix + 1 .. ix + 2] + s[ix + 3 .. ix + 5] + s[ix + 6 .. ix + 9])
        }
        ix1 = N.intdiv(VMS_FACTOR).intdiv(3)
        ix2 = (2 * N.intdiv(VMS_FACTOR)).intdiv(3) 
        if(sb.length() != N.intdiv(VMS_FACTOR) || sb[ix1] != ALFA[(i + ix1) % ALFA.length()] || sb[ix2] != ALFA[(i + ix2) % ALFA.length()]) {
            println("String test error")
            System.exit(1)
        }
    }
    t2 = System.currentTimeMillis()
    printres(t1, t2, nstrscale, N.intdiv(10), "string operations")
}

REP = 10

println("Groovy:")
int scale = 1
if(args.length > 0) {
    scale = Integer.parseInt(args[0])
}
for(i in 1..REP) {
    testint(scale)
}
for(i in 1..REP) {
    testfp(scale)
}
for(i in 1..REP) {
    teststr(scale)
}
import java.text.DecimalFormat
import java.text.NumberFormat

import groovy.transform.*

@CompileStatic
void printres(long t1, long t2, int n1, int n2, String ops) {
    NumberFormat nf = new DecimalFormat("0.00")
    double xperf = n1 * n2 / ((t2 - t1) / 1000.0) 
    String sperf = nf.format(xperf / 1000000)
    println("${sperf} million ${ops} per second")
}
    
@CompileStatic
void testint(int scale) {
    int NINT = 100000
    int N = 10000
    int nintscale = NINT.intdiv(scale)
    long t1 = System.currentTimeMillis()
    for(int i = 0; i < nintscale; i++) {
        int sum = i
        for(int j = 0; j < N; j++) {
            sum = ((sum + 1) * 2 + 1).intdiv(2)
        }
        if(sum != (i + N)) {
            println("Integer test error")
            System.exit(1)
        }
    }
    long t2 = System.currentTimeMillis()
    printres(t1, t2, nintscale, N, "integer operations")
}

@CompileStatic
void testfp(int scale) {
    int NFP = 100000
    int N = 10000
    int nfpscale = NFP.intdiv(scale)
    long t1 = System.currentTimeMillis()
    for(int i = 0; i < nfpscale; i++) {
        double sum = i
        for(int j = 0; j < N; j++) {
            sum = ((sum + 1) * 2 + 1) / 2
        }
        if(Math.abs(sum - (i + 1.5d * N)) > 1) {
            println("Floating point test error")
            System.exit(1)
        }
    }
    long t2 = System.currentTimeMillis()
    printres(t1, t2, nfpscale, N, "floating point operations")
}

@CompileStatic
void teststr(int scale) {
    int NSTR = 10000
    int N = 10000
    String ALFA = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    int VMS_FACTOR = 100
    int nstrscale = NSTR.intdiv(scale)
    long t1 = System.currentTimeMillis()
    for(int i = 0; i < nstrscale * VMS_FACTOR; i++) {
        StringBuffer sb = new StringBuffer("")
        for(int j = 0; j < N.intdiv(VMS_FACTOR); j = j + 10) {
            String s = ALFA + ALFA
            int ix = (i + j) % ALFA.length()
            sb << (s[ix .. ix] + s[ix + 1 .. ix + 2] + s[ix + 3 .. ix + 5] + s[ix + 6 .. ix + 9])
        }
        int ix1 = N.intdiv(VMS_FACTOR).intdiv(3)
        int ix2 = (2 * N.intdiv(VMS_FACTOR)).intdiv(3) 
        if(sb.length() != N.intdiv(VMS_FACTOR) || sb[ix1] != ALFA[(i + ix1) % ALFA.length()] || sb[ix2] != ALFA[(i + ix2) % ALFA.length()]) {
            println("String test error")
            System.exit(1)
        }
    }
    long t2 = System.currentTimeMillis()
    printres(t1, t2, nstrscale, N.intdiv(10), "string operations")
}

REP = 10

println("Groovy @CompileStatic:")
int scale = 1
if(args.length > 0) {
    scale = Integer.parseInt(args[0])
}
for(i in 1..REP) {
    testint(scale)
}
for(i in 1..REP) {
    testfp(scale)
}
for(i in 1..REP) {
    teststr(scale)
}

Article history:

Version Date Description
1.0 December 6th 2023 Initial version
1.1 December 15th 2023 Add Groovy
1.2 March 29th 2024 Add release compilers and FT Basic for x86-64

Other articles:

See list of all articles here

Comments:

Please send comments to Arne Vajhøj