VMS Benchmark - IO

Content:

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

Introduction:

Discussion came up in comp.os.vms/INFO-VAX again about relative speed of various ways to do disk IO on VMS.

I posted some numbers and some code.

This is basically the same, but with more options tested (read: more numbers and more code) and more comments.

Some may find the conclusions interesting.

Other may just be interested in the code examples. Some of the code is trivial, but other are non-trivial.

It is expected that the reader know about VMS, RMS, system services, C and preferrably also the other programming languages used. It is not really beginner friendly.

Conclusion:

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

Speed Reading records Reading blocks
Very slow Fortran READ
Pascal readln
sys$get
Slow C fgets
Medium Java BufferedReader.readLine sys$read small blocks
sys$qiow small blocks
sys$io_performw small blocks
Fast C fread small blocks
Java InputStream.read small blocks
C mmap
sys$crmpsc
Very fast C fread large blocks
sys$read large blocks
sys$qiow large blocks
sys$io_performw large blocks
Java InputStream.read large blocks
Java FileChannel.map

And:

Context:

There are 3 different ways to do IO:

Record based IO:

Record based IO stack

Block based IO:

Block based IO stack

Memory mapped file:

Memory mapped file stack

VMS "Fast IO" (sys$io_setup, sys$io_performw/sys$perform, sys$io_cleanup) was added in VMS 7.x for 64 bit platforms as a faster variant of the traditional sys$qiow/sys$qio.

The general assumptions are that:

Results:

The test will time counting number of lines in a 100 MB file.

As usual there is an infinite number of possible tests. This is just one test and it it does not in anyway look like a real world application. But it is simple - everybody can understand the code. Sometimes it is better to focus on a very narrow case with code everybody can understand than having a complex realistic case with code where nobody can understand what constructs are fast and what constructs are slow.

The numbers will be different on different systems. But I will assume the main conclusions will still hold.

Records:

Actually reading lines (tests run 10 times and rounded average used):

API VMS 8.4-2L2
Alpha simulator
Xeon @ 3.7 GHz
SSD
VMS 8.4-2L3
Itanium 1.6 Ghz
HDD
VMS 9.2-1
VMWare Player
Xeon @ 3.7 GHz
SSD
Lines of code
Fortran READ 550 s 71 s 61 s 34
Pascal readln 280 s 32 s 53 s 44
C fgets
C fgets with RAH
C fgets with ctx=stm
23 s
22 s
23 s
3.1 s
3.0 s
3.1 s
19 s
19 s
19 s
36
sys$get
sys$get with RAH
210 s
210 s
26 s
26 s
49 s
48 s
76
Java BufferedReader.readLine 40 s 1.40 s 0.50 s 27

Read ahead does not seem to make a big difference for this particular case. But it may matter more for other cases.

Java BufferedReader.readLine is much faster than anything else (except on slow Alpha simulator with old Java version) - and that is despite it converting 8 bit bytes to 16 bit UTF-16 code units. The Java IO library must do something very efficiently. The class name BufferedReader imply a buffer, but there are no unbuffered readLine and BufferedReader.readLine is the way you read text lines in Java.

Test of sys$get could have been done in another language like Fortran or Pascal, but it seems reasonable to assume C would be the language of choice today.

Blocks:

Reading disk blocks and parsing lines (tests run 10 times and rounded average used):

API VMS 8.4-2L2
Alpha simulator
Xeon @ 3.7 GHz
SSD
VMS 8.4-2L3
Itanium 1.6 Ghz
HDD
VMS 9.2-1
VMWare Player
Xeon @ 3.7 GHz
SSD
Lines of code
C fread 512 byte
C fread 512 byte with RAH
3.7 s
3.3 s
0.35 s
0.30 s
0.70 s
0.60 s
45
C fread 5120 bytes
C fread 5120 bytes with RAH
3.0 s
2.6 s
0.30 s
0.25 s
0.35 s
0.30 s
C fread 51200 bytes
C fread 51200 bytes with RAH
2.9 s
2.5 s
0.30 s
0.25 s
0.30 s
0.25 s
C fread 512000 bytes
C fread 512000 bytes with RAH
2.9 s
2.5 s
0.30 s
0.25 s
0.30 s
0.25 s
sys$read 512 bytes
sys$read 512 bytes with RAH
16.3 s
16.3 s
1.50 s
1.50 s
2.20 s
2.20 s
84
sys$read 5120 bytes
sys$read 5120 bytes with RAH
4.1 s
4.1 s
0.40 s
0.40 s
0.45 s
0.45 s
sys$read 51200 bytes
sys$read 51200 bytes with RAH
2.2 s
2.2 s
0.25 s
0.25 s
0.20 s
0.20 s
sys$read 512000 bytes
sys$read 512000 bytes with RAH
2.2 s
2.2 s
0.25 s
0.25 s
0.20 s
0.20 s
sys$qiow 512 bytes
sys$qio (max. 60 outstanding) 512 bytes
sys$io_performw 512 bytes
12.8 s
12.0 s
12.1 s
1.05 s
1.05 s
1.10 s
1.30 s
1.20 s
1.10 s
161
201
202
sys$qiow 5120 bytes
sys$qio (max. 60 outstanding) 5120 bytes
sys$io_performw 5120 bytes
3.8 s
3.6 s
3.4 s
0.35 s
0.30 s
0.35 s
0.35 s
0.30 s
0.30 s
sys$qiow 51200 bytes
sys$qio (max. 60 outstanding) 51200 bytes
sys$io_performw 51200 bytes
2.3 s
2.1 s
1.9 s
0.25 s
0.25 s
0.25 s
0.25 s
0.20 s
0.20 s
sys$qiow 512000 bytes
sys$qio (max. 60 outstanding) 512000 bytes
sys$io_performw 512000 bytes
1.9 s
1.9 s
2.0 s
0.30 s
0.70 s
0.30 s
0.35 s
0.25 s
0.30 s
Java InputStream.read 512 bytes 9.9 s 0.50 s 0.40 s 32
Java InputStream.read 5120 bytes 7.1 s 0.30 s 0.20 s
Java InputStream.read 51200 bytes 6.8 s 0.30 s 0.20 s
Java InputStream.read 512000 bytes 6.7 s 0.30 s 0.20 s
C mmap 3.1 s 0.50 s 0.80 s 57
sys$crmpsc 2.7 s 0.40 s 0.70 s 77
Java FileChannel.map 48 s 0.55 s 0.15 s 34

Read ahead does not seem to make a big difference for this particular case. But it may matter more for other cases.

At least for this example then sys$io_performw is not significant faster than sys$qiow. Maybe it is for other use cases, but somehow I doubt that the saved CPU cycles will matter much on modern fast CPU's.

At least for this example then sys$qio (not W) is not significant faster than sys$qiow. Maybe it is for other use cases where the program actually do something while waiting for IO to complete.

It was expected that reading blocks was faster than reading records, but it is somewhat surprising that the difference is so huge (except for Java).

Test of sys$read and the system services (sys$qiow, sys$qio, sys$io_performw and sys$crmpsc) could have been done in another language like Fortran or Pascal, but it seems reasonable to assume C would be the language of choice today.

Code:

For those really interested in the code here it comes.

The code is the ultimate documentation of what was tested. And everybody can check if I have made any mistakes. I hope not but one never knows.

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

The file generating program expect two parameters:

The file reading programs expect two parameters:

Generate file:

import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;

public class Gen {
    public static void main(String[] args) throws IOException {
        int size = Integer.parseInt(args[1]);
        long t1 = System.currentTimeMillis();
        try(PrintWriter pw = new PrintWriter(new FileWriter(args[0]))) {
            for(int i = 0; i < size / 10; i++) {
                for(int j = 0; j < 10 - System.getProperty("line.separator").length(); j++) {
                    pw.print(Integer.toString(i % 10));
                }
                pw.println();
            }
        }
        long t2 = System.currentTimeMillis();
        System.out.printf("Gen (%d bytes) : %d ms\n", size, t2 - t1);
    }
}

or if Java 5:

import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;

public class Gen {
    public static void main(String[] args) throws IOException {
        int size = Integer.parseInt(args[1]);
        long t1 = System.currentTimeMillis();
        PrintWriter pw = new PrintWriter(new FileWriter(args[0]));
        for(int i = 0; i < size / 10; i++) {
            for(int j = 0; j < 10 - System.getProperty("line.separator").length(); j++) {
                pw.print(Integer.toString(i % 10));
            }
            pw.println();
        }
        pw.close();
        long t2 = System.currentTimeMillis();
        System.out.printf("Gen (%d bytes) : %d ms\n", size, t2 - t1);
    }
}

Reading records:

      program plainread
      integer*4 i
      character*256 cmdlin
      integer*4 cmdlinlen, ix, expect
      call lib$get_foreign(cmdlin,,cmdlinlen)
      ix = index(cmdlin(1:cmdlinlen), ' ')
      read(cmdlin(ix+1:cmdlinlen), '(I)') expect
      do 100 i = 1,10
        call test(cmdlin(1:ix-1), expect)
100   continue
      end
c
      subroutine test(fnm, expect)
      character*(*) fnm
      integer*4 expect
      integer*4 n
      integer*8 t1, t2
      character*10 line
      call sys$gettim(t1)
      open(unit=1, file=fnm, status='old')
      n = 0
100   read(unit=1, fmt=300, end=200) line
      n = n + 1
      goto 100
200   if(n.ne.expect) then
        write(*,fmt=400) n
      end if
      close(unit=1)
      call sys$gettim(t2)
      write(*,fmt=500) (t2 - t1) / 10000
300   format(a)
400   format(1x,i8,' lines')
500   format(1x,'Fortran read : ',i6,' ms')
      end
[inherit('sys$library:pascal$lib_routines')]
program PlainReadLn(input, output);

type
    string = varying[32767] of char;

procedure test(fnm : string; expect : integer);

var
  f : text;
  line : string;
  t1, t2 : Cardinal;
  n : integer;

begin
  t1 := Clock;
  open(f, fnm, old);
  reset(f);
  n := 0;
  while not eof(f) do begin
    readln(f, line);
    n := n + 1;
  end;
  if n <> expect then begin
    writeln(n:1,' lines');
  end;
  close(f);
  t2 := Clock;
  writeln('Pascal readln : ', (t2 - t1):1, ' ms');
end;

var
  ix, expect, i : integer;
  cmdlin, fnm : string;

begin
  lib$get_foreign(resultant_string:=cmdlin.body,resultant_length:=cmdlin.length);
  ix := index(cmdlin, ' ');
  fnm := substr(cmdlin, 1, ix - 1);
  readv(substr(cmdlin, ix + 1), expect);
  for i := 1 to 10 do begin
    test(fnm, expect);
  end;
end.
#include <stdio.h>
#include <stdlib.h>

#include "high_res_timer.h"

static void test(const char *fnm, int expect)
{
    FILE *fp;
    char line[1000];
    TIMECOUNT_T t1, t2;
    int n;
    t1 = GET_TIMECOUNT;
    fp = fopen(fnm, "r");
    n = 0;
    while(fgets(line, sizeof(line), fp))
    {
        n++;
    }
    if(n != expect)
    {
        printf("%d lines\n", n);
    }
    fclose(fp);
    t2 = GET_TIMECOUNT;
    printf("C fgets : %d ms\n", (int)((t2 - t1) * 1000 / UNITS_PER_SECOND));
}

int main(int argc, char *argv[])
{
    int i;
    for(i = 0; i < 10; i++)
    {
        test(argv[1], atoi(argv[2]));
    }
    return 0;
}
#include <stdio.h>
#include <stdlib.h>

#include "high_res_timer.h"

static void test(const char *fnm, int expect)
{
    FILE *fp;
    char line[1000];
    TIMECOUNT_T t1, t2;
    int n;
    t1 = GET_TIMECOUNT;
    fp = fopen(fnm, "r", "rop=rah", "mbc=127", "mbf=127");
    n = 0;
    while(fgets(line, sizeof(line), fp))
    {
        n++;
    }
    if(n != expect)
    {
        printf("%d lines\n", n);
    }
    fclose(fp);
    t2 = GET_TIMECOUNT;
    printf("C fgets (rop=rah mbc=127 mbf=127) : %d ms\n", (int)((t2 - t1) * 1000 / UNITS_PER_SECOND));
}

int main(int argc, char *argv[])
{
    int i;
    for(i = 0; i < 10; i++)
    {
        test(argv[1], atoi(argv[2]));
    }
    return 0;
}
#include <stdio.h>
#include <stdlib.h>

#include "high_res_timer.h"

static void test(const char *fnm, int expect)
{
    FILE *fp;
    char line[1000];
    TIMECOUNT_T t1, t2;
    int n;
    t1 = GET_TIMECOUNT;
    fp = fopen(fnm, "r", "ctx=stm");
    n = 0;
    while(fgets(line, sizeof(line), fp))
    {
        n++;
    }
    if(n != expect)
    {
        printf("%d lines\n", n);
    }
    fclose(fp);
    t2 = GET_TIMECOUNT;
    printf("C fgets (ctx=stm): %d ms\n", (int)((t2 - t1) * 1000 / UNITS_PER_SECOND));
}

int main(int argc, char *argv[])
{
    int i;
    for(i = 0; i < 10; i++)
    {
        test(argv[1], atoi(argv[2]));
    }
    return 0;
}
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#include <starlet.h>
#include <rms.h>

#include "high_res_timer.h"

static void test(const char *fnm, int expect)
{
    long stat;
    struct FAB fab;
    struct RAB rab;
    char buf[1000];
    TIMECOUNT_T t1, t2;
    int n;
    t1 = GET_TIMECOUNT;
    fab = cc$rms_fab;
    fab.fab$l_fna = (char *)fnm;
    fab.fab$b_fns = strlen(fnm);
    fab.fab$b_fac = FAB$M_GET;
    stat = sys$open(&fab, 0, 0);
    if(!(stat & 1))
    {
        printf("sys$open stat = %d\n", stat);
    }
    rab = cc$rms_rab;
    rab.rab$l_fab = &fab;
    rab.rab$b_rac = RAB$C_SEQ;
    stat = sys$connect(&rab, 0 ,0);
    if(!(stat & 1))
    {
        printf("sys$connect stat = %d\n", stat);
    }
    n = 0;
    for(;;)
    {
        rab.rab$l_ubf = buf;
        rab.rab$w_usz = sizeof(buf);
        stat = sys$get(&rab, 0, 0);
        if(stat == RMS$_EOF) break;
        if(!(stat & 1))
        {
            printf("sys$get stat = %d\n", stat);
        }
        buf[rab.rab$w_rsz] = 0;
        n++;
    }
    if(n != expect)
    {
        printf("%d lines\n", n);
    }
    stat = sys$disconnect(&rab, 0, 0);
    if(!(stat & 1))
    {
       printf("sys$disconnect stat = %d\n", stat);
    }
    stat = sys$close(&fab, 0, 0);
    if(!(stat & 1))
    {
       printf("sys$close stat = %d\n", stat);
    }
    t2 = GET_TIMECOUNT;
    printf("sys$get : %d ms\n", (int)((t2 - t1) * 1000 / UNITS_PER_SECOND));
}

int main(int argc, char *argv[])
{
    int i;
    for(i = 0; i < 10; i++)
    {
        test(argv[1], atoi(argv[2]));
    }
    return 0;
}
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#include <starlet.h>
#include <rms.h>

#include "high_res_timer.h"

static void test(const char *fnm, int expect)
{
    long stat;
    struct FAB fab;
    struct RAB rab;
    char buf[1000];
    TIMECOUNT_T t1, t2;
    int n;
    t1 = GET_TIMECOUNT;
    fab = cc$rms_fab;
    fab.fab$l_fna = (char *)fnm;
    fab.fab$b_fns = strlen(fnm);
    fab.fab$b_fac = FAB$M_GET;
    stat = sys$open(&fab, 0, 0);
    if(!(stat & 1))
    {
        printf("sys$open stat = %d\n", stat);
    }
    rab = cc$rms_rab;
    rab.rab$l_fab = &fab;
    rab.rab$b_rac = RAB$C_SEQ;
    rab.rab$l_rop = RAB$M_RAH;
    rab.rab$b_mbc = 127;
    rab.rab$b_mbf = 127;
    stat = sys$connect(&rab, 0 ,0);
    if(!(stat & 1))
    {
        printf("sys$connect stat = %d\n", stat);
    }
    n = 0;
    for(;;)
    {
        rab.rab$l_ubf = buf;
        rab.rab$w_usz = sizeof(buf);
        stat = sys$get(&rab, 0, 0);
        if(stat == RMS$_EOF) break;
        if(!(stat & 1))
        {
            printf("sys$get stat = %d\n", stat);
        }
        buf[rab.rab$w_rsz] = 0;
        n++;
    }
    if(n != expect)
    {
        printf("%d lines\n", n);
    }
    stat = sys$disconnect(&rab, 0, 0);
    if(!(stat & 1))
    {
       printf("sys$disconnect stat = %d\n", stat);
    }
    stat = sys$close(&fab, 0, 0);
    if(!(stat & 1))
    {
       printf("sys$close stat = %d\n", stat);
    }
    t2 = GET_TIMECOUNT;
    printf("sys$get (rop=rah mbc=127 mbf=127) : %d ms\n", (int)((t2 - t1) * 1000 / UNITS_PER_SECOND));
}

int main(int argc, char *argv[])
{
    int i;
    for(i = 0; i < 10; i++)
    {
        test(argv[1], atoi(argv[2]));
    }
    return 0;
}
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;

public class ReadLine {
    private static void test(String fnm, int expect) throws IOException {
        long t1 = System.currentTimeMillis();
        try(BufferedReader br = new BufferedReader(new FileReader(fnm))) {
            int n = 0;
            @SuppressWarnings("unused")
            String line;
            while((line = br.readLine()) != null) {
                n++;
            }
            if(n != expect) {
                System.out.printf("%d lines\n", n);
            }
        }
        long t2 = System.currentTimeMillis();
        System.out.printf("Java BufferedReader.readLine : %d ms\n", t2 - t1);
    }
    public static void main(String[] args) throws IOException {
        for(int i = 0; i < 10; i++) {
            test(args[0], Integer.parseInt(args[1]));
        }
    }
}
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;

public class ReadLine {
    private static void test(String fnm, int expect) throws IOException {
        long t1 = System.currentTimeMillis();
        BufferedReader br = new BufferedReader(new FileReader(fnm));
        int n = 0;
        @SuppressWarnings("unused")
        String line;
        while((line = br.readLine()) != null) {
            n++;
        }
        if(n != expect) {
            System.out.printf("%d lines\n", n);
        }
        br.close();
        long t2 = System.currentTimeMillis();
        System.out.printf("Java BufferedReader.readLine : %d ms\n", t2 - t1);
    }
    public static void main(String[] args) throws IOException {
        for(int i = 0; i < 10; i++) {
            test(args[0], Integer.parseInt(args[1]));
        }
    }
}

Reading blocks:

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

#include "high_res_timer.h"

static void test(const char *fnm, int expect, int blksiz)
{
    FILE *fp;
    char *buf;
    TIMECOUNT_T t1, t2;
    int n, bufsiz, i;
    t1 = GET_TIMECOUNT;
    buf = malloc(blksiz);
    fp = fopen(fnm, "r");
    n = 0;
    while((bufsiz = fread(buf, 1, blksiz, fp)) > 0)
    {
        for(i = 0; i < bufsiz; i++)
        {
            if(buf[i] == '\n') n++;
        }
    }
    if(n != expect)
    {
        printf("%d lines\n", n);
    }
    free(buf);
    fclose(fp);
    t2 = GET_TIMECOUNT;
    printf("C fread (block size %d) : %d ms\n", blksiz, (int)((t2 - t1) * 1000 / UNITS_PER_SECOND));
}

int main(int argc, char *argv[])
{
    int blksiz[] = { 512, 5120, 51200, 512000 };
    int i, j;
    for(j = 0; j < 4; j++) 
    {
        for(i = 0; i < 10; i++)
        {
            test(argv[1], atoi(argv[2]), blksiz[j]);
        }
    }
    return 0;
}
#include <stdio.h>
#include <stdlib.h>

#include "high_res_timer.h"

static void test(const char *fnm, int expect, int blksiz)
{
    FILE *fp;
    char *buf;
    TIMECOUNT_T t1, t2;
    int n, bufsiz, i;
    t1 = GET_TIMECOUNT;
    buf = malloc(blksiz);
    fp = fopen(fnm, "r", "rop=rah", "mbc=127", "mbf=127");
    n = 0;
    while((bufsiz = fread(buf, 1, blksiz, fp)) > 0)
    {
        for(i = 0; i < bufsiz; i++)
        {
            if(buf[i] == '\n') n++;
        }
    }
    if(n != expect)
    {
        printf("%d lines\n", n);
    }
    free(buf);
    fclose(fp);
    t2 = GET_TIMECOUNT;
    printf("C fread (rop=rah mbc=127 mbf=127) (block size %d) : %d ms\n", blksiz, (int)((t2 - t1) * 1000 / UNITS_PER_SECOND));
}

int main(int argc, char *argv[])
{
    int blksiz[] = { 512, 5120, 51200, 512000 };
    int i, j;
    for(j = 0; j < 4; j++) 
    {
        for(i = 0; i < 10; i++)
        {
            test(argv[1], atoi(argv[2]), blksiz[j]);
        }
    }
    return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <starlet.h>
#include <rms.h>

#include "high_res_timer.h"

static void test(const char *fnm, int expect, int blksiz)
{
    long stat;
    struct FAB fab;
    struct RAB rab;
    char *buf;
    TIMECOUNT_T t1, t2;
    int n, i;
    t1 = GET_TIMECOUNT;
    buf = malloc(blksiz);
    fab = cc$rms_fab;
    fab.fab$l_fna = (char *)fnm;
    fab.fab$b_fns = strlen(fnm);
    fab.fab$b_fac = FAB$M_BIO | FAB$M_GET;
    stat = sys$open(&fab, 0, 0);
    if(!(stat & 1))
    {
        printf("sys$open stat = %d\n", stat);
    }
    rab = cc$rms_rab;
    rab.rab$l_fab = &fab;
    rab.rab$b_rac = RAB$C_SEQ;
    stat = sys$connect(&rab, 0 ,0);
    if(!(stat & 1))
    {
        printf("sys$connect stat = %d\n", stat);
    }
    n = 0;
    for(;;)
    {
        rab.rab$l_ubf = buf;
        rab.rab$w_usz = blksiz;
        stat = sys$read(&rab, 0, 0);
        if(stat == RMS$_EOF) break;
        if(!(stat & 1))
        {
            printf("sys$read stat = %d\n", stat);
        }
        for(i = 0; i < rab.rab$w_rsz; i++)
        {
            if(buf[i] == '\n') n++;
        }
    }
    if(n != expect)
    {
        printf("%d lines\n", n);
    }
    stat = sys$disconnect(&rab, 0, 0);
    if(!(stat & 1))
    {
       printf("sys$disconnect stat = %d\n", stat);
    }
    stat = sys$close(&fab, 0, 0);
    if(!(stat & 1))
    {
       printf("sys$close stat = %d\n", stat);
    }
    free(buf);
    t2 = GET_TIMECOUNT;
    printf("sys$read (block size %d) : %d ms\n", blksiz, (int)((t2 - t1) * 1000 / UNITS_PER_SECOND));
}

int main(int argc, char *argv[])
{
    int blksiz[] = { 512, 5120, 51200, 512000 };
    int i, j;
    for(j = 0; j < 4; j++)
    {
        for(i = 0; i < 10; i++)
        {
            test(argv[1], atoi(argv[2]), blksiz[j]);
        }
    }
    return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <starlet.h>
#include <rms.h>

#include "high_res_timer.h"

static void test(const char *fnm, int expect, int blksiz)
{
    long stat;
    struct FAB fab;
    struct RAB rab;
    char *buf;
    TIMECOUNT_T t1, t2;
    int n, i;
    t1 = GET_TIMECOUNT;
    buf = malloc(blksiz);
    fab = cc$rms_fab;
    fab.fab$l_fna = (char *)fnm;
    fab.fab$b_fns = strlen(fnm);
    fab.fab$b_fac = FAB$M_BIO | FAB$M_GET;
    stat = sys$open(&fab, 0, 0);
    if(!(stat & 1))
    {
        printf("sys$open stat = %d\n", stat);
    }
    rab = cc$rms_rab;
    rab.rab$l_fab = &fab;
    rab.rab$b_rac = RAB$C_SEQ;
    rab.rab$l_rop = RAB$M_RAH;
    rab.rab$b_mbc = 127;
    rab.rab$b_mbf = 127;
    stat = sys$connect(&rab, 0 ,0);
    if(!(stat & 1))
    {
        printf("sys$connect stat = %d\n", stat);
    }
    n = 0;
    for(;;)
    {
        rab.rab$l_ubf = buf;
        rab.rab$w_usz = blksiz;
        stat = sys$read(&rab, 0, 0);
        if(stat == RMS$_EOF) break;
        if(!(stat & 1))
        {
            printf("sys$read stat = %d\n", stat);
        }
        for(i = 0; i < rab.rab$w_rsz; i++)
        {
            if(buf[i] == '\n') n++;
        }
    }
    if(n != expect)
    {
        printf("%d lines\n", n);
    }
    stat = sys$disconnect(&rab, 0, 0);
    if(!(stat & 1))
    {
       printf("sys$disconnect stat = %d\n", stat);
    }
    stat = sys$close(&fab, 0, 0);
    if(!(stat & 1))
    {
       printf("sys$close stat = %d\n", stat);
    }
    free(buf);
    t2 = GET_TIMECOUNT;
    printf("sys$read (rop=rah mbc=127 mbf=127) (block size %d) : %d ms\n", blksiz, (int)((t2 - t1) * 1000 / UNITS_PER_SECOND));
}

int main(int argc, char *argv[])
{
    int blksiz[] = { 512, 5120, 51200, 512000 };
    int i, j;
    for(j = 0; j < 4; j++)
    {
        for(i = 0; i < 10; i++)
        {
            test(argv[1], atoi(argv[2]), blksiz[j]);
        }
    }
    return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <descrip.h>
#include <starlet.h>
#include <rms.h>
#include <ssdef.h>
#include <iodef.h>
#include <fibdef.h>
#include <atrdef.h>

#include "high_res_timer.h"

struct FAT
{
    unsigned long fat$v_rtype: 4;
    unsigned long fat$v_fileorg: 4;
    unsigned char fat$b_rattrib;
    unsigned short fat$w_rsize;
    unsigned short fat$w_hiblkh,fat$w_hiblkl;
    unsigned short fat$w_efblkh,fat$w_efblkl;
    unsigned short fat$w_ffbyte;
    unsigned char fat$b_bktsize;
    unsigned char fat$b_vfcsize;
    unsigned short fat$w_maxrec;
    unsigned short fat$w_defext;
    unsigned short fat$w_gbc;
    unsigned char fill_1[8];
    unsigned short fat$w_versions;
};

static long long offset(int blkno, int bytno)
{
    return (blkno - 1) * 512L + bytno;
}

static void test(const char *fnm, int expect, int blksiz)
{
    long stat;
    unsigned short iosb[4];
    struct FAB fab;
    struct NAM nam;
    struct dsc$descriptor_s devdesc;
    short chan;
    struct fibdef fib;
    long fibdesc[] = { FIB$K_LENGTH, (long)&fib };
    struct FAT fat;
    struct atrdef atr[] = { {ATR$S_RECATTR, ATR$C_RECATTR, &fat},{ 0, 0, NULL } };
    long eof;
    short ffb;
    int adjblksiz;
    char *buf;
    char expfnm[NAM$C_MAXRSS];
    int ix, len, n, i;
    TIMECOUNT_T t1, t2;
    t1 = GET_TIMECOUNT;
    buf = malloc(blksiz);
    fab = cc$rms_fab;
    nam = cc$rms_nam;
    fab.fab$l_fna = (char *)fnm;
    fab.fab$b_fns = strlen(fnm);
    fab.fab$b_fac = FAB$M_GET;
    fab.fab$l_nam = &nam;
    nam.nam$b_rss = sizeof(expfnm) - 1;
    nam.nam$l_rsa = expfnm;
    stat = sys$open(&fab, 0, 0);
    if(!(stat & 1))
    {
        printf("sys$open stat = %d\n", stat);
    }
    stat = sys$close(&fab, 0, 0);
    if(!(stat & 1))
    {
       printf("sys$close stat = %d\n", stat);
    }
    devdesc.dsc$b_dtype = DSC$K_DTYPE_T;
    devdesc.dsc$b_class = DSC$K_CLASS_S;
    devdesc.dsc$w_length = strlen(expfnm);
    devdesc.dsc$a_pointer = expfnm;
    stat = sys$assign(&devdesc, &chan, 0, 0, 0);
    if(!(stat & 1))
    {
       printf("sys$assign stat = %d\n", stat);
    }
    memset(&fib, 0, sizeof(fib));
    fib.fib$w_fid[0] = nam.nam$w_fid[0];
    fib.fib$w_fid[1] = nam.nam$w_fid[1];
    fib.fib$w_fid[2] = nam.nam$w_fid[2];
    stat = sys$qiow(0, chan, IO$_ACCESS | IO$M_ACCESS, iosb, 0, 0, &fibdesc, 0, 0, 0, &atr, 0);
    if(!(stat & 1))
    {
       printf("sys$qiow io$_access stat = %d\n", stat);
    }
    eof = (fat.fat$w_efblkh << 16) | fat.fat$w_efblkl;
    ffb = fat.fat$w_ffbyte;
    ix = 1;
    n = 0;
    for(;;)
    {
        if(ix > eof) break;
        if(offset(ix, blksiz) <= offset(eof, ffb))
        {
            adjblksiz = blksiz;
        }
        else
        {
            adjblksiz = offset(eof, ffb) - offset(ix, 0);
        }
        stat = sys$qiow(0, chan, IO$_READVBLK, iosb, 0, 0, buf, adjblksiz, ix, 0, 0, 0);
        if(iosb[0] == SS$_ENDOFFILE)
        {
            break;
        }
        else if(!(stat & 1))
        {
           printf("sys$qiow io$_readvblk stat = %d\n", stat);
        }
        else if(!(iosb[0] & 1))
        {
           printf("sys$qiow io$_readvblk iosb[0] = %d\n", iosb[0]);
        }
        len = (iosb[2] << 16) | iosb[1];
        for(i = 0; i < len; i++)
        {
            if(buf[i] == '\n') n++;
        }
        ix = ix + blksiz / 512;
    }
    if(n != expect)
    {
        printf("%d lines\n", n);
    }
    stat = sys$qiow(0, chan, IO$_DEACCESS, iosb, 0, 0, &fibdesc, 0, 0, 0, &atr, 0);
    if(!(stat & 1))
    {
       printf("sys$qiow io$_deaccess stat = %d\n", stat);
    }
    stat = sys$dassgn(chan);
    if(!(stat & 1))
    {
       printf("sys$dassgn stat = %d\n", stat);
    }
    free(buf);
    t2 = GET_TIMECOUNT;
    printf("sys$qiow (block size %d) : %d ms\n", blksiz, (int)((t2 - t1) * 1000 / UNITS_PER_SECOND));
}

int main(int argc, char *argv[])
{
    int blksiz[] = { 512, 5120, 51200, 512000 };
    int i, j;
    for(j = 0; j < 4; j++)
    {
        for(i = 0; i < 10; i++)
        {
            test(argv[1], atoi(argv[2]), blksiz[j]);
        }
    }
    return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <descrip.h>
#include <starlet.h>
#include <rms.h>
#include <ssdef.h>
#include <iodef.h>
#include <fibdef.h>
#include <atrdef.h>

#include "high_res_timer.h"

struct FAT
{
    unsigned long fat$v_rtype: 4;
    unsigned long fat$v_fileorg: 4;
    unsigned char fat$b_rattrib;
    unsigned short fat$w_rsize;
    unsigned short fat$w_hiblkh,fat$w_hiblkl;
    unsigned short fat$w_efblkh,fat$w_efblkl;
    unsigned short fat$w_ffbyte;
    unsigned char fat$b_bktsize;
    unsigned char fat$b_vfcsize;
    unsigned short fat$w_maxrec;
    unsigned short fat$w_defext;
    unsigned short fat$w_gbc;
    unsigned char fill_1[8];
    unsigned short fat$w_versions;
};

static long long offset(int blkno, int bytno)
{
    return (blkno - 1) * 512L + bytno;
}

#define PAR_FACTOR 60

static void test(const char *fnm, int expect, int blksiz)
{
    long stat;
    unsigned short iosb[PAR_FACTOR][4];
    struct FAB fab;
    struct NAM nam;
    struct dsc$descriptor_s devdesc;
    short chan;
    struct fibdef fib;
    long fibdesc[] = { FIB$K_LENGTH, (long)&fib };
    struct FAT fat;
    struct atrdef atr[] = { {ATR$S_RECATTR, ATR$C_RECATTR, &fat},{ 0, 0, NULL } };
    long eof;
    short ffb;
    int adjblksiz;
    char *buf;
    char expfnm[NAM$C_MAXRSS];
    int ix, len, n, i;
    int nread, slot, efn;
    int outstand[PAR_FACTOR] = { 0 };
    TIMECOUNT_T t1, t2;
    t1 = GET_TIMECOUNT;
    buf = malloc(PAR_FACTOR * blksiz);
    fab = cc$rms_fab;
    nam = cc$rms_nam;
    fab.fab$l_fna = (char *)fnm;
    fab.fab$b_fns = strlen(fnm);
    fab.fab$b_fac = FAB$M_GET;
    fab.fab$l_nam = &nam;
    nam.nam$b_rss = sizeof(expfnm) - 1;
    nam.nam$l_rsa = expfnm;
    stat = sys$open(&fab, 0, 0);
    if(!(stat & 1))
    {
        printf("sys$open stat = %d\n", stat);
    }
    stat = sys$close(&fab, 0, 0);
    if(!(stat & 1))
    {
       printf("sys$close stat = %d\n", stat);
    }
    devdesc.dsc$b_dtype = DSC$K_DTYPE_T;
    devdesc.dsc$b_class = DSC$K_CLASS_S;
    devdesc.dsc$w_length = strlen(expfnm);
    devdesc.dsc$a_pointer = expfnm;
    stat = sys$assign(&devdesc, &chan, 0, 0, 0);
    if(!(stat & 1))
    {
       printf("sys$assign stat = %d\n", stat);
    }
    memset(&fib, 0, sizeof(fib));
    fib.fib$w_fid[0] = nam.nam$w_fid[0];
    fib.fib$w_fid[1] = nam.nam$w_fid[1];
    fib.fib$w_fid[2] = nam.nam$w_fid[2];
    stat = sys$qiow(0, chan, IO$_ACCESS | IO$M_ACCESS, iosb, 0, 0, &fibdesc, 0, 0, 0, &atr, 0);
    if(!(stat & 1))
    {
       printf("sys$qiow io$_access stat = %d\n", stat);
    }
    eof = (fat.fat$w_efblkh << 16) | fat.fat$w_efblkl;
    ffb = fat.fat$w_ffbyte;
    ix = 1;
    n = 0;
    nread = 0;
    for(;;)
    {
        if(ix > eof) break;
        if(offset(ix, blksiz) <= offset(eof, ffb))
        {
            adjblksiz = blksiz;
        }
        else
        {
            adjblksiz = offset(eof, ffb) - offset(ix, 0);
        }
        slot = nread % PAR_FACTOR;
        efn = slot + 1; 
        if(outstand[slot])
        {
            stat = sys$synch(efn, iosb[slot]);
            if(!(stat & 1))
            {
               printf("sys$synch stat = %d\n", stat);
            }
            if(iosb[slot][0] == SS$_ENDOFFILE)
            {
                break;
            }
            else if(!(iosb[slot][0] & 1))
            {
               printf("sys$qio io$_readvblk iosb[0] = %d\n", iosb[slot][0]);
            }
            len = (iosb[slot][2] << 16) | iosb[slot][1];
            for(i = 0; i < len; i++)
            {
                if(buf[slot * blksiz + i] == '\n') n++;
            }
            outstand[slot] = 0;
        }
        stat = sys$qio(efn, chan, IO$_READVBLK, iosb[slot], 0, 0, buf + slot * blksiz, adjblksiz, ix, 0, 0, 0);
        if(!(stat & 1))
        {
           printf("sys$qio io$_readvblk stat = %d\n", stat);
        }
        outstand[slot] = 1;
        ix = ix + blksiz / 512;
        nread++;
    }
    for(slot = 0; slot < PAR_FACTOR; slot++)
    {
        if(outstand[slot])
        {
            efn = slot + 1; 
            stat = sys$synch(efn, iosb[slot]);
            if(iosb[slot][0] == SS$_ENDOFFILE)
            {
                continue;
            }
            else if(!(iosb[slot][0] & 1))
            {
               printf("sys$qio io$_readvblk iosb[0] = %d\n", iosb[slot][0]);
            }
            len = (iosb[slot][2] << 16) | iosb[slot][1];
            for(i = 0; i < len; i++)
            {
                if(buf[slot * blksiz + i] == '\n') n++;
            }
            outstand[slot] = 0;
        }
    }
    if(n != expect)
    {
        printf("%d lines\n", n);
    }
    stat = sys$qiow(0, chan, IO$_DEACCESS, iosb, 0, 0, &fibdesc, 0, 0, 0, &atr, 0);
    if(!(stat & 1))
    {
       printf("sys$qiow io$_deaccess stat = %d\n", stat);
    }
    stat = sys$dassgn(chan);
    if(!(stat & 1))
    {
       printf("sys$dassgn stat = %d\n", stat);
    }
    free(buf);
    t2 = GET_TIMECOUNT;
    printf("sys$qio (block size %d) : %d ms\n", blksiz, (int)((t2 - t1) * 1000 / UNITS_PER_SECOND));
}

int main(int argc, char *argv[])
{
    int blksiz[] = { 512, 5120, 51200, 512000 };
    int i, j;
    for(j = 0; j < 4; j++)
    {
        for(i = 0; i < 10; i++)
        {
            test(argv[1], atoi(argv[2]), blksiz[j]);
        }
    }
    return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <descrip.h>
#include <starlet.h>
#include <rms.h>
#include <ssdef.h>
#include <iodef.h>
#include <iosadef.h>
#include <fibdef.h>
#include <atrdef.h>
#include <gen64def.h>

#pragma pointer_size save
#pragma pointer_size 64
typedef void *void_ptr64;
#pragma pointer_size restore

#include "high_res_timer.h"

struct FAT
{
    unsigned long fat$v_rtype: 4;
    unsigned long fat$v_fileorg: 4;
    unsigned char fat$b_rattrib;
    unsigned short fat$w_rsize;
    unsigned short fat$w_hiblkh,fat$w_hiblkl;
    unsigned short fat$w_efblkh,fat$w_efblkl;
    unsigned short fat$w_ffbyte;
    unsigned char fat$b_bktsize;
    unsigned char fat$b_vfcsize;
    unsigned short fat$w_maxrec;
    unsigned short fat$w_defext;
    unsigned short fat$w_gbc;
    unsigned char fill_1[8];
    unsigned short fat$w_versions;
};

static long long offset(int blkno, int bytno)
{
    return (blkno - 1) * 512L + bytno;
}

static __align(13) char realbuf[819200];
static __align(13) union { iosa fastiosb; char dummy[8192]; } fast;

static void test(const char *fnm, int expect, int blksiz)
{
    long stat;
    unsigned short iosb[4];
    struct FAB fab;
    struct NAM nam;
    struct dsc$descriptor_s devdesc;
    short chan;
    struct fibdef fib;
    long fibdesc[] = { FIB$K_LENGTH, (long)&fib };
    struct FAT fat;
    struct atrdef atr[] = { {ATR$S_RECATTR, ATR$C_RECATTR, &fat},{ 0, 0, NULL } };
    struct _generic_64 realbufh, fasth;
    void_ptr64 realbufptr, *fastptr;
    long long realbuflen, fastlen;
    long long fh;
    long eof;
    short ffb;
    int adjblksiz;
    char expfnm[NAM$C_MAXRSS];
    int ix, len, n, i;
    TIMECOUNT_T t1, t2;
    t1 = GET_TIMECOUNT;
    fab = cc$rms_fab;
    nam = cc$rms_nam;
    fab.fab$l_fna = (char *)fnm;
    fab.fab$b_fns = strlen(fnm);
    fab.fab$b_fac = FAB$M_GET;
    fab.fab$l_nam = &nam;
    nam.nam$b_rss = sizeof(expfnm) - 1;
    nam.nam$l_rsa = expfnm;
    stat = sys$open(&fab, 0, 0);
    if(!(stat & 1))
    {
        printf("sys$open stat = %d\n", stat);
    }
    stat = sys$close(&fab, 0, 0);
    if(!(stat & 1))
    {
       printf("sys$close stat = %d\n", stat);
    }
    devdesc.dsc$b_dtype = DSC$K_DTYPE_T;
    devdesc.dsc$b_class = DSC$K_CLASS_S;
    devdesc.dsc$w_length = strlen(expfnm);
    devdesc.dsc$a_pointer = expfnm;
    stat = sys$assign(&devdesc, &chan, 0, 0, 0);
    if(!(stat & 1))
    {
       printf("sys$assign stat = %d\n", stat);
    }
    memset(&fib, 0, sizeof(fib));
    fib.fib$w_fid[0] = nam.nam$w_fid[0];
    fib.fib$w_fid[1] = nam.nam$w_fid[1];
    fib.fib$w_fid[2] = nam.nam$w_fid[2];
    stat = sys$qiow(0, chan, IO$_ACCESS | IO$M_ACCESS, iosb, 0, 0, &fibdesc, 0, 0, 0, &atr, 0);
    if(!(stat & 1))
    {
       printf("sys$qiow io$_access stat = %d\n", stat);
    }
    eof = (fat.fat$w_efblkh << 16) | fat.fat$w_efblkl;
    ffb = fat.fat$w_ffbyte;
    stat = sys$create_bufobj_64(realbuf, sizeof(realbuf), 0, 0, &realbufptr, &realbuflen, &realbufh);
    if(!(stat & 1))
    {
       printf("sys$create_bufobj_64 realbuf stat = %d\n", stat);
    }
    stat = sys$create_bufobj_64(&fast, sizeof(fast), 0, 0, &fastptr, &fastlen, &fasth);
    if(!(stat & 1))
    {
       printf("sys$create_bufobj_64 fast stat = %d\n", stat);
    }
    stat = sys$io_setup (IO$_READVBLK, &realbufh, &fasth, 0, 0, &fh);
    if(!(stat & 1))
    {
       printf("sys$io_setup stat = %d\n", stat);
    }
    ix = 1;
    n = 0;
    for(;;)
    {
        if(ix > eof) break;
        if(offset(ix, blksiz) <= offset(eof, ffb))
        {
            adjblksiz = blksiz;
        }
        else
        {
            adjblksiz = offset(eof, ffb) - offset(ix, 0);
        }
        stat = sys$io_performw(fh, chan, &fast.fastiosb, realbuf, adjblksiz, ix);
        if(fast.fastiosb.iosa$l_status == SS$_ENDOFFILE)
        {
            break;
        }
        else if(!(stat & 1))
        {
           printf("sys$io_performw stat = %d\n", stat);
        }
        else if(!(fast.fastiosb.iosa$l_status & 1))
        {
           printf("sys$io_performw iosb_status = %d\n", fast.fastiosb.iosa$l_status);
        }
        len = fast.fastiosb.iosa$ih_count;
        for(i = 0; i < len; i++)
        {
            if(realbuf[i] == '\n') n++;
        }
        ix = ix + blksiz / 512;
    }
    if(n != expect)
    {
        printf("%d lines\n", n);
    }
    stat = sys$io_cleanup(fh);
    if(!(stat & 1))
    {
       printf("sys$io_cleanup stat = %d\n", stat);
    }
    stat = sys$delete_bufobj(&realbufh);
    if(!(stat & 1))
    {
       printf("sys$delete_bufobj realbuf stat = %d\n", stat);
    }
    stat = sys$delete_bufobj(&fasth);
    if(!(stat & 1))
    {
       printf("sys$delete_bufobj fast stat = %d\n", stat);
    }
    stat = sys$qiow(0, chan, IO$_DEACCESS, iosb, 0, 0, &fibdesc, 0, 0, 0, &atr, 0);
    if(!(stat & 1))
    {
       printf("sys$qiow io$_deaccess stat = %d\n", stat);
    }
    stat = sys$dassgn(chan);
    if(!(stat & 1))
    {
       printf("sys$dassgn stat = %d\n", stat);
    }
    t2 = GET_TIMECOUNT;
    printf("sys$io_performw (block size %d) : %d ms\n", blksiz, (int)((t2 - t1) * 1000 / UNITS_PER_SECOND));
}

int main(int argc, char *argv[])
{
    int blksiz[] = { 512, 5120, 51200, 512000 };
    int i, j;
    for(j = 0; j < 4; j++)
    {
        for(i = 0; i < 10; i++)
        {
            test(argv[1], atoi(argv[2]), blksiz[j]);
        }
    }
    return 0;
}
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.IOException;

public class Read {
    private static void test(String fnm, int expect, int blksiz) throws IOException {
        long t1 = System.currentTimeMillis();
        try(InputStream is = new FileInputStream(fnm)) {
            int n = 0;
            byte[] buf = new byte[blksiz];
            int nb;
            while((nb = is.read(buf)) > 0) {
                for(int i = 0; i < nb; i++) {
                    if(buf[i] == '\n') n++;
                }
            }
            if(n != expect) {
                System.out.printf("%d lines\n", n);
            }
        }
        long t2 = System.currentTimeMillis();
        System.out.printf("Java InputStream.read (block size %d) : %d ms\n", blksiz, t2 - t1);
    }
    public static void main(String[] args) throws IOException {
        int[] blksiz = { 512, 5120, 51200, 512000 };
        for(int j = 0; j < 4; j++) {
            for(int i = 0; i < 10; i++) {
                test(args[0], Integer.parseInt(args[1]), blksiz[j]);
            }
        }
    }
}
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.IOException;

public class Read {
    private static void test(String fnm, int expect, int blksiz) throws IOException {
        long t1 = System.currentTimeMillis();
        InputStream is = new FileInputStream(fnm);
        int n = 0;
        byte[] buf = new byte[blksiz];
        int nb;
        while((nb = is.read(buf)) > 0) {
            for(int i = 0; i < nb; i++) {
                if(buf[i] == '\n') n++;
            }
        }
        if(n != expect) {
            System.out.printf("%d lines\n", n);
        }
        is.close();
        long t2 = System.currentTimeMillis();
        System.out.printf("Java InputStream.read (block size %d) : %d ms\n", blksiz, t2 - t1);
    }
    public static void main(String[] args) throws IOException {
        int[] blksiz = { 512, 5120, 51200, 512000 };
        for(int j = 0; j < 4; j++) {
            for(int i = 0; i < 10; i++) {
                test(args[0], Integer.parseInt(args[1]), blksiz[j]);
            }
        }
    }
}

Memory mapping:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>

#include "high_res_timer.h"

#define OFFSET 0
#define SIZE 102400000

static void test(const char *fnm, int expect)
{
    int fd;
    char *b;
    TIMECOUNT_T t1, t2;
    int n, ix;
    t1 = GET_TIMECOUNT;
    fd = open(fnm, O_RDONLY);
    if(fd == -1)
    {
        printf("Error opening file\n");
        exit(1);
    }
    b = mmap(NULL, SIZE, PROT_READ, MAP_PRIVATE, fd, OFFSET);
    if(b == MAP_FAILED)
    {
        printf("Error mapping file\n");
        exit(1);
    }
    n = 0;
    for(ix = 0; ix < SIZE; ix++)
    {
        if(b[ix] == '\n')
        {
            n++;
        }
    }
    if(n != expect)
    {
        printf("%d lines\n", n);
    }
    munmap(b, SIZE);
    close(fd);
    t2 = GET_TIMECOUNT;
    printf("C mmap : %d ms\n", (int)((t2 - t1) * 1000 / UNITS_PER_SECOND));
}

int main(int argc, char *argv[])
{
    int i;
    for(i = 0; i < 10; i++)
    {
        test(argv[1], atoi(argv[2]));
    }
    return 0;
}
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#include <fabdef.h>
#include <secdef.h>
#include <starlet.h>

#define PAGE_SIZE 8192
#define PAGELET_SIZE 512
#define DISKBLOCK_SIZE 512

#define BY2U(n, unit_size) (n - 1) / unit_size + 1

#include "high_res_timer.h"

#define OFFSET 0
#define SIZE 102400000

static void test(char *fnm, int expect)
{
    struct FAB myfab;
    void *range[2];
    unsigned short chan;
    long int stat;
    char *b;
    TIMECOUNT_T t1, t2;
    int n, ix;
    t1 = GET_TIMECOUNT;
    myfab = cc$rms_fab;
    myfab.fab$l_fna = fnm;
    myfab.fab$b_fns = strlen(fnm);
    myfab.fab$l_fop = FAB$M_UFO;
    myfab.fab$b_fac = FAB$M_GET;
    stat = sys$open(&myfab, 0, 0);
    if((stat & 1) == 0)
    {
        printf("Error opening file\n");
        exit(1);
    }
    range[0] = 0;
    range[1] = 0;
    chan = myfab.fab$l_stv;
    stat = sys$crmpsc(range, range, 0, SEC$M_EXPREG, 0, 0, 0, chan, BY2U(SIZE, PAGELET_SIZE), BY2U(OFFSET, DISKBLOCK_SIZE), 0, 0);
    if((stat & 1) == 0) 
    {
        printf("Error mapping file\n");
        exit(1);
    }
    b = range[0];
    n = 0;
    for(ix = 0; ix < SIZE; ix++)
    {
        if(b[ix] == '\n')
        {
            n++;
        }
    }
    if(n != expect)
    {
        printf("%d lines\n", n);
    }
    sys$deltva(range, 0, 0);
    sys$close(&myfab, 0, 0);
    t2 = GET_TIMECOUNT;
    printf("sys$crmpsc : %d ms\n", (int)((t2 - t1) * 1000 / UNITS_PER_SECOND));
}

int main(int argc, char *argv[])
{
    int i;
    for(i = 0; i < 10; i++)
    {
        test(argv[1], atoi(argv[2]));
    }
    return 0;
}
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileChannel.MapMode;
import java.nio.file.Paths;

public class MapFile {
    private static final int OFFSET = 0;
    private static final int SIZE = 102400000;
    private static void test(String fnm, int expect) throws IOException {
        long t1 = System.currentTimeMillis();
        try(FileChannel fc = FileChannel.open(Paths.get(fnm))) {
            ByteBuffer bb = fc.map(MapMode.READ_ONLY, OFFSET, SIZE);
            bb.rewind();
            int n = 0;
            for(int ix = 0; ix < SIZE; ix++) {
                byte b = bb.get(ix);
                if(b == '\n') {
                    n++;
                }
            }
            if(n != expect) {
                System.out.printf("%d lines\n", n);
            }
        }
        long t2 = System.currentTimeMillis();
        System.out.printf("Java FileChannel.map file : %d ms\n", t2 - t1);
    }
    public static void main(String[] args) throws IOException {
        for(int i = 0; i < 10; i++) {
            test(args[0], Integer.parseInt(args[1]));
        }
    }
}
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileChannel.MapMode;

public class MapFile {
    private static final int OFFSET = 0;
    private static final int SIZE = 102400000;
    private static void test(String fnm, int expect) throws IOException {
        long t1 = System.currentTimeMillis();
        FileInputStream fis = new FileInputStream(fnm);
        FileChannel fc = fis.getChannel();
        ByteBuffer bb = fc.map(MapMode.READ_ONLY, OFFSET, SIZE);
        bb.rewind();
        int n = 0;
        for(int ix = 0; ix < SIZE; ix++) {
            byte b = bb.get(ix);
            if(b == '\n') {
                n++;
            }
        }
        if(n != expect) {
            System.out.printf("%d lines\n", n);
        }
        fc.close();
        long t2 = System.currentTimeMillis();
        System.out.printf("Java FileChannel.map file : %d ms\n", t2 - t1);
    }
    public static void main(String[] args) throws IOException {
        for(int i = 0; i < 10; i++) {
            test(args[0], Integer.parseInt(args[1]));
        }
    }
}

Article history:

Version Date Description
1.0 December 6th 2023 Initial version
1.1 January 14th 2024 Add SYS$QIO (not W) test

Other articles:

See list of all articles here

Comments:

Please send comments to Arne Vajhøj