matmul

FP matrix multiplication



This package provides floating-point matrix multiplication. It runs on Forth-94 and Forth-2012 systems with the floating-point wordset.

include matmul.4th

to get the word

matmulr ( a b c n1 n2 n3 -- )

MATMULR computes C=AxB, where A, B, and C are matrixes: where A has n1 rows and n2 columns, B has n2 rows and n3 columns, and C has n1 rows and n3 columns. Matrixes are stored in row-major order, i.e., the elements of one row are adjacent, whereas the items of a column are separated by all the items in the first row.

Testing

To test whether this works as intended with your Forth system, type

create matmul-verbose include matmul.4th include test.4th

Performance

This may not be the best-performing matrix multiplication written in Forth and certainly not outside of Forth, but it has the advantage of just working (at least that's the intention). It's also not too badly performing: Gforth performs 0.8 FLOP/cycle on a Core i5-6600K using the faxpy primitive, and ideally Forth compilers will generate as good code for FAXPY-NOSTRIDE as that for the FAXPY primitive.

Here are some results (using unrolled versions):

cycles Core i5-6600K
Forth-2012 Forth-94 Forth-94 primitive
          Rees/Ertl   pahihu     faxpy
 *1048M       1202M    1517M           vfxlin Ndp387.fth
  2484M       2945M    2764M     *316M gforth-fast
 *3584M       5067M    5017M           SwiftForth HW stack, no WAIT

Details (irrelevant for most usage)

MATMULR calls a word FAXPY-NOSTRIDE, and the library contains several implementations of this word, and automatically selects the one expected to give the best performance. If you want to know which implementation is used, you can CREATE MATMUL-VERBOSE before including matmul.4th. You can also determine which implementation is used by defining a constant FAXPY-NOSTRIDE-VARIANT. The values for this constant are as follows:

unrolling
no yes
 1     use FAXPY (a primitive in Gforth)
 4  2  separate-stack (Forth-2012) implementation
 5  3  Forth-94 implementation by Joel Rees and Anton Ertl
15 13  Forth-94 implementation by pahihu

The Forth-94 implementations work on systems with separate FP stacks as well as on systems with a combined FP and data stack with any number of cells per FP item.

Use as Benchmark

For use as benchmark, there is benchmark.4th, which performs one 500x500 matrix multiplication. There is also the script runbenchs; by default, runbenchs will run the benchmark on gforth-fast, vfxlin (with various FP packages), and sf if available (and for VFX, if %vfxpath% works), and it will use perf (with a fallback to time) for timing. You can influence these choices and more by setting environment variables before calling runbenchs. E.g., if you want to benchmark iforth only, you can do (on a bourne-compatible shell):

SF=iforth GFORTH=xx VFXLIN=xx ./runbenchs

The environment variables used are:

TIMING  command used for measuring, e.g., `time`
RUN     file used for running benchmark (RUN=test.4th for testing)
GFORTH  how to invoke Gforth (default: gforth-fast)
SF      how to invoke SwiftForth (default: sf)
VFXLIN  How to invoke VFX (default: vfxlin)
VFXHOME where the VfxLin directory is (default: %vfxpath%)
VFXLIB  where the FP packages can be found (default: $VFXHOME/Lib)
VFXX86  where the x86-specific FP packages can be found

The defaults are reasonable, but you may need to define VFXHOME yourself.

You can also use this script to test all the different variants on several systems with

TIMING=command RUN=test.4th ./runbenchs