Page 1 of 1

Fast ARRAY and RECORD moves

Posted: Mon Sep 16, 2013 1:29 pm
by cfbsoftware
The auto-increment / auto-decrement versions of SYSTEM.GET and SYSTEM.PUT in v4.5 Astrobe and later allow you to implement very efficient array processing functions. For example, the following are useful general-purpose functions for moving /copying blocks of data:

Code: Select all

 PROCEDURE* MoveWords*(fromAdr, toAdr, nBytes: INTEGER);
 VAR 
   word, lastAdr: INTEGER;
 BEGIN
   lastAdr := fromAdr + nBytes;
   REPEAT
     SYSTEM.GET(fromAdr, word, 4);
     SYSTEM.PUT(toAdr, word, 4); 
   UNTIL fromAdr = lastAdr
 END MoveWords;

 PROCEDURE* MoveBytes*(fromAdr, toAdr, nBytes: INTEGER);
 VAR 
   byte: BYTE;
   lastAdr: INTEGER;
 BEGIN
   lastAdr := fromAdr + nBytes;
   REPEAT
     SYSTEM.GET(fromAdr, byte, 1);
     SYSTEM.PUT(toAdr, byte, 1); 
   UNTIL fromAdr = lastAdr
 END MoveBytes;
 
 PROCEDURE Move*(fromAdr, toAdr, nBytes: INTEGER);
 VAR 
   nBytes4, nBytes1: INTEGER;
 BEGIN
   nBytes1 := nBytes MOD 4;
   nBytes4 := nBytes - nBytes1;
   IF nBytes4 > 0 THEN 
     MoveWords(fromAdr, toAdr, nBytes4); 
   END;
   IF nBytes1 > 0 THEN 
     MoveBytes(fromAdr + nBytes4, toAdr + nBytes4, nBytes1); 
   END
 END Move;

The main loop of MoveBytes and MoveWords consist of just four instructions. MoveWords is typically four times faster than MoveBytes because it loads/stores four bytes in each instruction and can be used for INTEGER arrays or records that are multiples of four bytes. Move can be used if it is not known if the data is a multiple of four bytes. It uses MoveWords for all except the last (less than four) bytes of data.

An example of their use is a function to shift all the bytes in an array by one position. The normal way to do this would be something like:

Code: Select all

PROCEDURE* ShiftArray(VAR a: ARRAY OF BYTE);
 VAR
   i: INTEGER;
 BEGIN
   FOR i := 1 TO LEN(a) - 1 DO a[i-1] := a[i] END
 END ShiftArray;
However, if maximum performance is a priority, an alternative implementation is:

Code: Select all

 PROCEDURE ShiftArray(VAR a: ARRAY OF BYTE);
 BEGIN
   Move(SYSTEM.ADR(a[1]), SYSTEM.ADR(a[0]), LEN(a) - 1)
 END ShiftArray;
A test with 1000-byte arrays showed that this second version that uses GET and PUT is about five times faster.

Re: Fast ARRAY and RECORD moves

Posted: Tue Sep 03, 2019 12:59 pm
by cfbsoftware
Since the Introduction of SYSTEM.COPY (equivalent to an inline version of MoveWords) an alternative implementation of Move is:

Code: Select all

 PROCEDURE* Move*(fromAdr, toAdr, nBytes: INTEGER);
 VAR 
   nWords, endAdr: INTEGER;
   byte: BYTE;
 BEGIN
   endAdr := fromAdr + nBytes;
   nWords := nBytes DIV 4;
   IF nWords > 0 THEN 
     SYSTEM.COPY(fromAdr, toAdr, nWords)
   END;
   WHILE fromAdr < endAdr DO  
     SYSTEM.GET(fromAdr, byte, 1);
     SYSTEM.PUT(toAdr, byte, 1) 
   END
 END Move;