Delphi/Fortran memory allocation difference: row/column major order makes a big difference. StackOverflow answer.
Posted by jpluimers 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.
You have:
- 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.
This answer tries to explain the differences in memory layout of arrays and records in FORTRAN and Delphi and amends the answer by Todd Grigsby and the answer by Remy Lebeau (I upvoted both).
FORTRAN and a few other calculation centric languages stores nested arrays in column major order. Delphi and many other languages use a row major order.
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(:) :: Walland 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):
in FORTRAN:
Name[0,0]...Name[0,39], Name[1,0]...Name[1,39], Name[2,0]...Name[2,39], Azimuth[0], Azimuth[1], Azimuth[2], Tilt[0], Tilt[1], Tilt[2], Area[0], Area[1], Area[2], Height[0], Height[1], Height[2],in Delphi:
Name[0,0]...Name[0,39], Azimuth[0], Tilt[0], Area[0], Height[0], Name[1,0]...Name[1,39], Azimuth[1], Tilt[1], Area[1], Height[1], Name[2,0]...Name[2,39], Azimuth[2], Tilt[2], Area[2], Height[2],So this FORTRAN call:
CALL HeatFlow(Wall%Area, Wall%Azimuth)
would just pass the pointers to the Area[0] and Azimuth[0] 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
Todd Grigsby and Remy Lebeau showed the first three steps in their answer using straight Delphi code, or Delphi record RTTI.
Step 4 works in a similar way.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.
–Jeroen






ObjectMethodology.com said
Cool, you’re working on heat loss calculations. Good topic, Thx.
jpluimers said
Heat loss calculations? Please explain (:
ObjectMethodology.com said
“CALL HeatFlow(Wall%Area, Wall%Azimuth)”
Isn’t that about heat loss?
jpluimers said
That was not from me, but from the guy originally asking the question (: