Delphi/Fortran memory allocation difference: row/column major order makes a big difference. StackOverflow answer.
Posted by Jeroen Pluimers on 2012/08/14
Last week I posted an in depth answer on StackOverflow.com about the memory allocation difference in Delphi and Fortran, or more accurately in two different kinds of languages.
- column major order (not only used in Fortran and other science centric languages, but also in the shading languages GLSL and HLSL that can be used in FireMonkey)
- row major order (used in most other languages, for instance Delphi, C, etc)
A very important aspect is the order of for optimized loops. For column major order, the optimum is inside out (as the inner arrays/records are in consecutive memory locations), but for row major order, it is the exact inverse.
One of the consequences is that in Fortran it is a very fast operation to pass an array of inner fields as parameter to a method.
This is what I wrote:
I’m posting this as an answer because the comments are a bit too limited to phrase this.
From a memory perspective, a record is nothing else than an array of fields that:
- have a name and not an index
- might have different types
For calculation intensive operations, it can make sense to store nested arrays column major order when your algorithms favour the columns. Same for row major order. So in loops, you need to match the order of your indexes with the order of your storage.
Given this record and array definition in FORTRAN:
TYPE WallInfo CHARACTER(len=40) :: Name REAL :: Azimuth REAL :: Tilt REAL :: Area REAL :: Height END TYPE WallInfo TYPE(WallInfo), ALLOCATABLE, DIMENSION(:) :: Wall
and the functional equivalent definition in Delphi:
type WallInfo = record Name: array[0..39] of Char; Azimuth: Real; Tilt: Real; Area: Real; Height: Real; end; var Wall: array of WallInfo;
and an array of 3 WallInfo elements, this is how the memory layout would look (they would all be continuous memory areas, I split them in lines to keep it readable):
Name[0,0]...Name[0,39], Name[1,0]...Name[1,39], Name[2,0]...Name[2,39], Azimuth, Azimuth, Azimuth, Tilt, Tilt, Tilt, Area, Area, Area, Height, Height, Height,
Name[0,0]...Name[0,39], Azimuth, Tilt, Area, Height, Name[1,0]...Name[1,39], Azimuth, Tilt, Area, Height, Name[2,0]...Name[2,39], Azimuth, Tilt, Area, Height,
So this FORTRAN call:
CALL HeatFlow(Wall%Area, Wall%Azimuth)
would just pass the pointers to the Area and Azimuth memory locations and length of those memory areas to the function.
In Delphi, that is not possible, so you have to
- construct new Area and Azimuth arays
- copy them from the info in the array of WallInfo record instance called Wall
- send them to the function
- if these are var parameters: copy the changes from the two arrays back to Wall
Both solutions use generics that were introduced in Delphi 2009.
Until Delphi 2010, RTTI on records was very minimal), so you got the right Delphi version for both answers.
Note (again): when translating your algorithms from FORTRAN to Delphi, make sure you watch for loops and other indexing in the arrays because of the column/row major change.