program arraytest;
{ Fills an array with random values, accesses it with an index (array[index])
and with a pointer, and times each pass. The random values are valid indices
in the array for use in the Access* procedures.
By Juan Miguel Martinez, 2017, released into the Public Domain. }
{$mode objfpc}
{$ASMMODE INTEL}
uses
sysutils, dateutils;
const
NumVal = 268435456 * 4; // 268,435,456 integers = 1GiB
NumValC = NumVal - 1;
NumAccesses = 130000000; // takes 4-5 times more than traversing 4gb
type
TProc = procedure;
TMyType=record
Data:Integer;
Padding:array[0..5] of byte;
end;
PMyType=^TMyType;
var
Vector: array of TMyType;
// Fills the array with random values, static random seed
procedure FillVector;
var
i: Integer;
begin
Randseed := 4; // Guaranteed to be random by fair dice roll
for i := 0 to NumValC do
Vector[i].Data := Random(NumVal);
end;
// Traverses whole array, index version
procedure TraverseIndex;
var
i, o: Integer;
begin
o := 0;
for i := 0 to NumValC do
if Odd(Vector[i].Data) then
Inc(o);
Write(IntToStr(o), ' odd numbers, ');
end;
// Traverses whole array, pointer version
procedure TraversePointer;
var
i, o: Integer;
p: PMyType;
begin
o := 0;
p := @Vector[0];
for i := 0 to NumValC do
begin
if Odd(p^.Data) then
Inc(o);
Inc(p);
end;
Write(IntToStr(o), ' odd numbers, ');
end;
procedure TraverseAsm;
var o:Integer;
begin
asm
MOV RAX, Vector
MOV RCX, NumVal
XOR RDX, RDX
JRCXZ @@Exit
@@Loop:
TEST DWORD PTR [RAX],1
JZ @@Even
INC RDX
@@Even:
LEA RAX, [RAX + 12]
LOOP @@Loop
@@Exit:
MOV o, EDX
end;
Write(IntToStr(o), ' odd numbers, ');
end;
// Accesses the array N times, index version
procedure AccessIndex;
var
idx, o: Integer;
i: Int64;
begin
o := 0;
RandSeed := 4; // xkcd ftw!
idx := 0;
for i := 1 to NumAccesses do
begin
if Odd(Vector[idx].Data) then
Inc(o);
idx := Vector[idx].Data;
end;
Write(IntToStr(o), ' odd numbers, ')
end;
// Accesses the array N times, pointer version
procedure AccessPointer;
var
o: Integer;
b, p: PMyType;
i: Int64;
begin
o := 0;
b := @Vector[0];
p := b;
RandSeed := 4; // xkcd ftw!
for i := 1 to NumAccesses do
begin
if Odd(p^.Data) then
Inc(o);
p := b + p^.Data;
end;
Write(IntToStr(o), ' odd numbers, ');
end;
procedure AccessAsm;
var o:Integer;
begin
asm
PUSH R9
MOV RAX, Vector
MOV R9, RAX
MOV RCX, NumAccesses
XOR RDX, RDX
JRCXZ @@Exit
@@Loop:
MOV EAX,[RAX]
TEST EAX,1
JZ @@Even
INC RDX
@@Even:
IMUL RAX, RAX, 12
LEA RAX, [R9 + RAX]
LOOP @@Loop
@@Exit:
MOV o, EDX
POP R9
end;
Write(IntToStr(o), ' odd numbers, ');
end;
// Clocks a call to one of the accessing procedures
procedure ClockProcedure(const proc: TProc; const msg: string);
var
t: TDateTime;
begin
Write(msg);
t := Now();
proc;
WriteLn(IntToStr(MilliSecondsBetween(Now(), t)), ' ms');
end;
begin
SetLength(Vector, NumVal);
ClockProcedure(@FillVector, 'Filling array... ');
ClockProcedure(@TraverseIndex, 'Traversing array with index... ');
ClockProcedure(@TraversePointer, 'Traversing array with pointer... ');
ClockProcedure(@TraverseAsm, 'Traversing array with pointer asm... ');
ClockProcedure(@AccessIndex, 'Accessing array with index... ');
ClockProcedure(@AccessPointer, 'Accessing array with pointer... ');
ClockProcedure(@AccessAsm, 'Accessing array with pointer asm... ');
WriteLn('Press Enter to continue...');
ReadLn();
end.