Strange Pascal Alignments - Programmers Heaven

#### Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

#### Categories

Welcome to the new platform of Programmer's Heaven! We apologize for the inconvenience caused, if you visited us from a broken link of the previous version. The main reason to move to a new platform is to provide more effective and collaborative experience to you all. Please feel free to experience the new platform and use its exciting features. Contact us for any issue that you need to get clarified. We are more than happy to help you.

# Strange Pascal Alignments

Posts: 41Member
[b][red]This message was edited by Csabi_B at 2006-5-2 4:46:32[/red][/b][hr]
[b][red]This message was edited by Csabi_B at 2006-5-2 4:45:41[/red][/b][hr]
[code]
{Applied in an imaginary Pascal}
program sizedemo;
type
MyInt1 = packed record
Lng: 0..\$7FFFFFFF;
Sgn: 0..1;
end;
MyInt2 = record
Lng: 0..\$7FFFFFFF;
Sgn: 0..1;
end;
begin
WriteLn(SizeOf(MyInt1),',',SizeOf(MyInt2));{4,5}
end.
{Applied in Free Pascal and Delphi}
{\$apptype console}
{\$a+}
program sizedemo;
type
MyInt3 = packed record
Lng: 0..\$7FFFFFFF;
Sgn: 0..1;
end;
MyInt4 = record
Lng: 0..\$7FFFFFFF;
Sgn: 0..1;
end;
var
begin
WriteLn(SizeOf(MyInt3),',',SizeOf(MyInt4));{5,8}
end.
[/code]
This brings up two questions.
Why do Fpc and Delphi not support the MyInt1 structure?
Why do Fpc and Delphi allocate four bytes for Sgn in MyInt4?

• Posts: 6,349Member
: [b][red]This message was edited by Csabi_B at 2006-5-2 4:46:32[/red][/b][hr]
: [b][red]This message was edited by Csabi_B at 2006-5-2 4:45:41[/red][/b][hr]
: [code]
: {Applied in an imaginary Pascal}
: program sizedemo;
: type
: MyInt1 = packed record
: Lng: 0..\$7FFFFFFF;
: Sgn: 0..1;
: end;
: MyInt2 = record
: Lng: 0..\$7FFFFFFF;
: Sgn: 0..1;
: end;
: begin
: WriteLn(SizeOf(MyInt1),',',SizeOf(MyInt2));{4,5}
: end.
: {Applied in Free Pascal and Delphi}
: {\$apptype console}
: {\$a+}
: program sizedemo;
: type
: MyInt3 = packed record
: Lng: 0..\$7FFFFFFF;
: Sgn: 0..1;
: end;
: MyInt4 = record
: Lng: 0..\$7FFFFFFF;
: Sgn: 0..1;
: end;
: var
: begin
: WriteLn(SizeOf(MyInt3),',',SizeOf(MyInt4));{5,8}
: end.
: [/code]
: This brings up two questions.
: Why do Fpc and Delphi not support the MyInt1 structure?
: Why do Fpc and Delphi allocate four bytes for Sgn in MyInt4?
:
Answer 2: In 32bit architecture it is faster to allocate all integers as 4 byte integer, because they can then fill 1 32-bit register and all faster embedded addressing algorithms can be applied.
• Posts: 41Member
: Answer 2: In 32bit architecture it is faster to allocate all integers as 4 byte integer, because they can then fill 1 32-bit register and all faster embedded addressing algorithms can be applied.
:
Indeed, but what I mean:
if I replace the byte sized data with the four-byte data (LongInt) (example 1&2), the allocation will be 6 (with the alignment set to word align as in the example). And if I add another byte sized data, the SizeOf returns the same 8 (example 3).
[code]
type
Expl1 = record {SizeOf(Expl1)=8}
FourB: LongInt;
OneB: Byte;
end;
Expl2 = record {SizeOf(Expl2)=6}
OneB: Byte;
FourB: LongInt;
end;
Expl3 = record {SizeOf(Expl3)=8}
FourB: LongInt;
OneB: Byte;
PlusB: Byte;
end;
[/code]
Isn't it strange? And the consequence could be fatal: if I create a data file (f: file of Expl1;) in word or byte alignment, and I read the data back later by another program whose alignment isn't set accordingly or does not support the previous one's (say byte or word alignment), it will result a mess. And to make things more complicated, there are 4 and 8 bytes alignment.

I could use the key word packed, but what if I wish to utilize the alignments in the running exe (e.g. in memory) but not in the data file.
And there is still the different meaning of the word "packed" in other Pascals. If one compiles the same source using different compilers and with the same data file(s) provided, the outcome will depend on God's sake. Am I right?

The solution, as you also hinted (and thanks for it), is the use of LongInts, DoubleWords for fast alignment and safer data structures, but then the data allocation can be four times larger than needed, and as space is speed, I get an 1:1 in the end.
• Posts: 6,349Member
: : Answer 2: In 32bit architecture it is faster to allocate all integers as 4 byte integer, because they can then fill 1 32-bit register and all faster embedded addressing algorithms can be applied.
: :
: Indeed, but what I mean:
: if I replace the byte sized data with the four-byte data (LongInt) (example 1&2), the allocation will be 6 (with the alignment set to word align as in the example). And if I add another byte sized data, the SizeOf returns the same 8 (example 3).
: [code]
: type
: Expl1 = record {SizeOf(Expl1)=8}
: FourB: LongInt;
: OneB: Byte;
: end;
: Expl2 = record {SizeOf(Expl2)=6}
: OneB: Byte;
: FourB: LongInt;
: end;
: Expl3 = record {SizeOf(Expl3)=8}
: FourB: LongInt;
: OneB: Byte;
: PlusB: Byte;
: end;
: [/code]
: Isn't it strange? And the consequence could be fatal: if I create a data file (f: file of Expl1;) in word or byte alignment, and I read the data back later by another program whose alignment isn't set accordingly or does not support the previous one's (say byte or word alignment), it will result a mess. And to make things more complicated, there are 4 and 8 bytes alignment.
:
: I could use the key word packed, but what if I wish to utilize the alignments in the running exe (e.g. in memory) but not in the data file.
: And there is still the different meaning of the word "packed" in other Pascals. If one compiles the same source using different compilers and with the same data file(s) provided, the outcome will depend on God's sake. Am I right?
:
: The solution, as you also hinted (and thanks for it), is the use of LongInts, DoubleWords for fast alignment and safer data structures, but then the data allocation can be four times larger than needed, and as space is speed, I get an 1:1 in the end.
:
That's indeed strange. I know from Delphi that integers in unpacked records will always be 32bit aligned, while packed records will be "normally" aligned. In memory there is no difference between packed and unpacked, because good compilers will add realign code inside the assignment. Example:
[code]
type
TPackRec = packed record
b: byte;
end;

TUnPackRec = record
b: byte;
end;

PackRec.b := UnPackRec.b; // <= will be automatically realigned
[/code]
Differences between packed and unpacked within the memory between compilers is also not important, unless you use pointers to access the fields. The above code will also work perfectly for a 64-bit or a 16-bit compiler.
The main reason for packed records is mostly in file-I/O, and compatibility with older programs and libraries. In cross-compiler file-I/O it is often better to write each field with the size explicitly specified, than to write the record as a whole.
As for the speed: unpacked records are faster than packed records. The memory allocation of a record is optimalized for 32-bit alignment. Also so for the access of the fields.
• Posts: 41Member
I have to do some corrections. I was a bit confused because I read in a guide that Fpc (and Delphi 7) uses word alignment by default, but the newer releases use 4 byte alignment by default, so in some of my tests I set it \$a2 and in others I did not set it at all expecting the same result, and that confusion led me to state that Expl2 allocated 6 bytes in the previous post. So I was wrong and I am sorry.
Here is the correct example program, and I added new examples:
[code]{\$apptype console}
{\$a4}
program sizetest;
type
Expl1 = record {8}{I'd expect 5}
FourB: LongInt;
OneB: Byte;
end;
Expl2 = record {8}{I'd also expect 8}
OneB: Byte;
FourB: LongInt;
end;
Expl3 = record {8}{I'd expect 6 or, after Expl1, 12}
FourB: LongInt;
OneB: Byte;
PlusB: Byte;
end;
Expl4 = record {4}
FourB: LongInt;
end;
Expl5 = packed record {4}
FourB: LongInt;
end;
Expl6 = record {8}{I'd expect 5}
FourB: Expl4;
OneB: Byte;
end;
Expl7 = record {5}{I'd expect 8 after Expl6}
FourB: Expl5;
OneB: Byte;
end;
Expl8 = record {5}
Frst: Byte;
Scnd: Byte;
Thrd: Byte;
Frth: Byte;
Ffth: Byte;
end;
Expl9 = record {12}
FourB: LongInt; {from 1st to 4th}
Frst: Byte; {5th}
Scnd: Byte; {6th}
Thrd: Byte; {7th}
Frth: Byte; {8th}
Ffth: Byte; {from 9th to 12th}
end;
{From Expl8 and 9 it seems a record won't be aligned
(i.e. expanded) until a large item is added}
begin
WriteLn('Expl1= ',SizeOf(Expl1));
WriteLn('Expl2= ',SizeOf(Expl2));
WriteLn('Expl3= ',SizeOf(Expl3));
WriteLn('Expl4= ',SizeOf(Expl4));
WriteLn('Expl5= ',SizeOf(Expl5));
WriteLn('Expl6= ',SizeOf(Expl6));
WriteLn('Expl7= ',SizeOf(Expl7));
WriteLn('Expl8= ',SizeOf(Expl8));
WriteLn('Expl9= ',SizeOf(Expl9));
end.[/code]
• Posts: 6,349Member
: I have to do some corrections. I was a bit confused because I read in a guide that Fpc (and Delphi 7) uses word alignment by default, but the newer releases use 4 byte alignment by default, so in some of my tests I set it \$a2 and in others I did not set it at all expecting the same result, and that confusion led me to state that Expl2 allocated 6 bytes in the previous post. So I was wrong and I am sorry.
: Here is the correct example program, and I added new examples:
: [code]{\$apptype console}
: {\$a4}
: program sizetest;
: type
: Expl1 = record {8}{I'd expect 5}
: FourB: LongInt;
: OneB: Byte;
: end;
: Expl2 = record {8}{I'd also expect 8}
: OneB: Byte;
: FourB: LongInt;
: end;
: Expl3 = record {8}{I'd expect 6 or, after Expl1, 12}
: FourB: LongInt;
: OneB: Byte;
: PlusB: Byte;
: end;
: Expl4 = record {4}
: FourB: LongInt;
: end;
: Expl5 = packed record {4}
: FourB: LongInt;
: end;
: Expl6 = record {8}{I'd expect 5}
: FourB: Expl4;
: OneB: Byte;
: end;
: Expl7 = record {5}{I'd expect 8 after Expl6}
: FourB: Expl5;
: OneB: Byte;
: end;
: Expl8 = record {5}
: Frst: Byte;
: Scnd: Byte;
: Thrd: Byte;
: Frth: Byte;
: Ffth: Byte;
: end;
: Expl9 = record {12}
: FourB: LongInt; {from 1st to 4th}
: Frst: Byte; {5th}
: Scnd: Byte; {6th}
: Thrd: Byte; {7th}
: Frth: Byte; {8th}
: Ffth: Byte; {from 9th to 12th}
: end;
: {From Expl8 and 9 it seems a record won't be aligned
: (i.e. expanded) until a large item is added}
: begin
: WriteLn('Expl1= ',SizeOf(Expl1));
: WriteLn('Expl2= ',SizeOf(Expl2));
: WriteLn('Expl3= ',SizeOf(Expl3));
: WriteLn('Expl4= ',SizeOf(Expl4));
: WriteLn('Expl5= ',SizeOf(Expl5));
: WriteLn('Expl6= ',SizeOf(Expl6));
: WriteLn('Expl7= ',SizeOf(Expl7));
: WriteLn('Expl8= ',SizeOf(Expl8));
: WriteLn('Expl9= ',SizeOf(Expl9));
: end.[/code]
:
In D5 the record sizes behaved as I expected: 32-bit aligned. Which means that every record is multiples of 4 bytes in size, except for packed records. Because SHR and AND operations are very fast, the delphi compiler will place 4 consecutive byte-sized (or 2 word-sized) fields into 1 single longint-sized "field".
• Posts: 41Member
: In D5 the record sizes behaved as I expected: 32-bit aligned. Which means that every record is multiples of 4 bytes in size, except for packed records. Because SHR and AND operations are very fast, the delphi compiler will place 4 consecutive byte-sized (or 2 word-sized) fields into 1 single longint-sized "field".
:
So that's the reason...Nevertheless one should be aware what rules are applied on records in aligned modes. They can be very different from how one's mind works and what expects. This alignment thing was one of the thousand notable cases I must learn I'm afraid;)
• Posts: 6,349Member
: : In D5 the record sizes behaved as I expected: 32-bit aligned. Which means that every record is multiples of 4 bytes in size, except for packed records. Because SHR and AND operations are very fast, the delphi compiler will place 4 consecutive byte-sized (or 2 word-sized) fields into 1 single longint-sized "field".
: :
: So that's the reason...Nevertheless one should be aware what rules are applied on records in aligned modes. They can be very different from how one's mind works and what expects. This alignment thing was one of the thousand notable cases I must learn I'm afraid;)
:
The rules can be found in the help files under "Memory management" and "Structured Types".
• Posts: 41Member
: The rules can be found in the help files under "Memory management" and "Structured Types".
:
Thanks, but I erased Delphi 7 just like Delphi 2006 for the same reason: it was too big system and generated too large code for my aims. I only left the command line compiler to test things and for a reference for Delphi syntax, and I don't plan to use it for anything. An earlier version you probably use would definitely satisfy me, but I cannot get one in the way I got the newest releases (from magazine CD). I cannot download large files from the net. I got 1.8 Free Pascal from a magazine CD which is not the newest release.
As a final decision I returned to TP 5.5 as I added to the last post of my compiler topic. Maybe I should have opened a new post instead of the edit.