Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Mailing lists / fpc-devel / 2006-05

data alignment and int64 (or qword, maybe any single piece of size 8 if any)

12 messages · 5 people · –

Reply by email Subscribe to this list Your subscription

Read-only archive. Use j and k to move between messages.

Пётр Косаревский

It seems, that docs are outdated.

Default data alignment in records is expected to be wordly (i386, win32).

But if a record has int64 (or qword) element, I get unexpected overheads.

It's easily avoidable (with {$PACKRECORDS 1} or {$A1}; even with {$A-} [which is expected to work only on MAC, according to docs]), but unexpected.

type
  a= record
     a: longword;
     b: longword;
     c: longword;
     end; // 12 expected
  b= record
     ab: int64;
     c: longword;
     end; // 12 expected
  c= record
     ab: array[0..7] of byte;
     c: byte; // 1b overhead expected
     d: word;
     e: byte; // 1b overhead expected
     end; // 14 expected
  d= record
     ab: int64;
     c: byte; // 1b overhead expected
     d: word;
     e: byte; // 1b overhead expected
     end; // 14 expected
  e= record
     a: byte;
     b: byte;
     c: word;
     d: longword;
     e: word;
     f: byte;
     g: byte;
     end; // well, 12 expected
begin
writeln(sizeof(a)); // 12 ok!
writeln(sizeof(b)); // 16    ???
writeln(sizeof(c)); // 14 ok!
writeln(sizeof(d)); // 16    ???
writeln(sizeof(e)); // 12 ok!
end. // I don't know the numbers on other platforms/systems

Reply by email

Alexander Todorov
to Пётр Косаревский
Пётр Косаревский wrote 8 lines
begin writeln(sizeof(a)); // 12 ok! writeln(sizeof(b)); // 16 ??? writeln(sizeof(c)); // 14 ok! writeln(sizeof(d)); // 16 ??? writeln(sizeof(e)); // 12 ok! end. // I don't know the numbers on other platforms/systems

Linux, i386, FPC 2.0.2, results are the same.

Reply by email

Daniël Mantione
to Пётр Косаревский: It seems, that docs are outdated. Default data alignment in records is expected to be…

Op Mon, 15 May 2006, schreef ???? ???????????:

Show quoted text 6 lines
b= record ab: int64; c: longword; end; // 12 expected writeln(sizeof(b)); // 16 ???

Suppose you have an array of b. Then, with a size of 12, there would be no guarantee that an access to b.ab is aligned. So, the compiler pads the record to a multiple of the largest field.

Daniël

Reply by email

Пётр Косаревский
to Daniël Mantione
Show quoted text 5 lines
Suppose you have an array of b. Then, with a size of 12, there would be no guarantee that an access to b.ab is aligned. So, the compiler pads the record to a multiple of the largest field. DaniКl

I didn't state that it's useless. It's just unexpected as it is.

The result is the same both with -O3r and -Og switches. Setting processor to 386 (I don't think that such an alignment helps its performance) doesn't change the result.

When I read record or something, I use
{$if sizeof(b)12}
  {$fatal }
{$endif}
so it's not much of a problem (added to {$A1} etc).

1/2/4/8/16 alignment is not required by the processor architecture, it does not always improve performance too. If needed programmer can control alignment almost precisely with and {$a##} and {$if}.

Automatic longword alignment in some cases exists too (size of "record a: longword; b: byte; end;" is 8). It appears even with longint type (as I remember, it was in early [<5] versions of Turbo Pascal on 8086 or 8088, e.g. [I know, that FPC does not support <32 width oriented models and absolutely don't expect it to do it in the future]).

So this feature surely should be documented (at least for easier solving possible compatibility problems).

Reply by email

Daniël Mantione
to Пётр Косаревский

Op Mon, 15 May 2006, schreef ???? ???????????:

Show quoted text 10 lines
> Suppose you have an array of b. Then, with a size of 12, there would be no > guarantee that an access to b.ab is aligned. So, the compiler pads the > record to a multiple of the largest field. > > Dani?l I didn't state that it's useless. It's just unexpected as it is. The result is the same both with -O3r and -Og switches. Setting processor to 386 (I don't think that such an alignment helps its performance) doesn't change the result.

This is true, an 32-bit processor has no advantage of 64-bit alignment of 64-bit value, so it is open to discussion wether the compiler should really do this for 64-bit values.

Show quoted text 5 lines
When I read record or something, I use {$if sizeof(b)12} {$fatal } {$endif} so it's not much of a problem (added to {$A1} etc).

If you read or write records, please declare them packed to disable alignment, so you don't need these ugly tricks.

1/2/4/8/16 alignment is not required by the processor architecture, it does not always improve performance too. If needed programmer can control alignment almost precisely with and {$a##} and {$if}.

Exactly. In the general case, it does improve performance, so the default setting is okay. If alignment is for one reason or another unwanted, the programmer can use preprocessor directives or declare the record packed and align manually.

Show quoted text 5 lines
Automatic longword alignment in some cases exists too (size of "record a: longword; b: byte; end;" is 8). It appears even with longint type (as I remember, it was in early [<5] versions of Turbo Pascal on 8086 or 8088, e.g. [I know, that FPC does not support <32 width oriented models and absolutely don't expect it to do it in the future]).

Yes, if the longword is the largest type in the record (size 4), the record is padded to a multiple of 4.

So this feature surely should be documented (at least for easier solving possible compatibility problems).

I agree.

Daniël

Reply by email

Peter Vreman
to Daniël Mantione
Show quoted text 20 lines
Op Mon, 15 May 2006, schreef ???? ???????????: > > Suppose you have an array of b. Then, with a size of 12, there would > be no > > guarantee that an access to b.ab is aligned. So, the compiler pads the > > record to a multiple of the largest field. > > > > Dani?l > > I didn't state that it's useless. It's just unexpected as it is. > > The result is the same both with -O3r and -Og switches. > Setting processor to 386 (I don't think that such an alignment helps its > performance) doesn't change the result. This is true, an 32-bit processor has no advantage of 64-bit alignment of 64-bit value, so it is open to discussion wether the compiler should really do this for 64-bit values.

This is not always true. Sparc requires 64bit alignment for doubles. And i386 requires 128bit alignment for XMM.

Reply by email

Пётр Косаревский
to Peter Vreman

Main warning at the end of the message.

This is not always true. Sparc requires 64bit alignment for doubles. And i386 requires 128bit alignment for XMM.

1. I think, that when you optimize for size, default options should be "avoid alignment when possible".
2. SSE2 is a special case and, by the way, will FPC support integer SSE2 operations? (Current mmx unit is not widely used, so Florian K. said it is low priority.)

Compile this with "-OG" and notice, that while record has size 9, individual variables of that type are aligned by 16-byte. (FPC 2.0.3, i386, win32) And it does not depend on processor model: -Cp386 and -CpPENTIUM4 yield the same results.

{$A-}
type
  a= record
     a: int64;
     b: byte;
     end;
var
  x: a;
  y: byte;        // these are
  yy: longword;   //   9
  zz: longword;   //     bytes
  z: a;
begin
writeln(sizeof(a));
writeln(longint(@x));
writeln(longint(@y));
writeln(longint(@z));
end.

This program makes me feel strange: it aligns records by 16 bytes for no reason (last two addresses are odd). (Don't forget {$A-})

{$A-}
type
  a= record
     b: byte;
     a: int64;
     end;
var
  x: a;
  y: byte;
  yy: longword;
  zz: longword;
  z: a;
begin
writeln(sizeof(a));
writeln;
writeln(longint(@x));
writeln(longint(@y));
writeln(longint(@z));
writeln;
writeln(longint(@(x.a)));
writeln(longint(@(z.a)));
end.

Get rid of {$A-}, use "-O3rp4", "-CpPENTIUM4", and one of int64 addresses is not 4 bytes aligned! And records are persistently 16 bytes aligned!

Reply by email

Jonas Maebe
to Пётр Косаревский
Пётр Косаревский wrote 11 lines
1. I think, that when you optimize for size, default options should be "avoid alignment when possible". 2. SSE2 is a special case and, by the way, will FPC support integer SSE2 operations? (Current mmx unit is not widely used, so Florian K. said it is low priority.) Compile this with "-OG" and notice, that while record has size 9, individual variables of that type are aligned by 16-byte. (FPC 2.0.3, i386, win32) And it does not depend on processor model: -Cp386 and -CpPENTIUM4 yield the same results.

Record packing is separate from optimization or target processors, because otherwise this would be too confusing (e.g. you'd have problems if you want to share memory or files between programs compiled with different optimization modes or for a different processor). In other compilers (both Pascal and C), record packing/ alignment is also completely separate from optimization switches.

If you want a particular alignment mode other than the default, use
{$packrecords x}

Jonas

Reply by email

Пётр Косаревский
to Jonas Maebe

I myself don't have unsolved problems with alignment in records.

I only point at some inconsistency with 16-byte alignment by default: variables are 16-bytes aligned and their members of type int64 may have addresses not divisible byt 4.

{$PACKRECORDS n} has almost nothing to do with this inconsistency.

I wrote about default behavior.

I wrote about dependency of alignment of variables on compiler switch: if I want to conserve memory and may have int64 aligned (by default) only by word, I don't want big gaps between variables because of 16 bytes alignment.

Originally I pointed to the lack of documentation (which states, that the default alignment in records is 1-2 bytes).

Reply by email

Jonas Maebe
to Пётр Косаревский
Пётр Косаревский wrote 5 lines
I myself don't have unsolved problems with alignment in records. I only point at some inconsistency with 16-byte alignment by default: variables are 16-bytes aligned and their members of type int64 may have addresses not divisible byt 4.

At least the AIX ABI requires a kind of alignment that can result in this sort of situations. Maybe other ABI's do as well.

{$PACKRECORDS n} has almost nothing to do with this inconsistency.

It does, since the member alignment is defined by {$packrecords n}

I wrote about default behavior.

There is a default for {$packrecords }, just like for all other settings.

I wrote about dependency of alignment of variables on compiler switch: if I want to conserve memory and may have int64 aligned (by default) only by word, I don't want big gaps between variables because of 16 bytes alignment.

You shouldn't get big gaps them between different variables if you specify a small alignment. You may get them between different record members if you don't change the packrecords setting as well.

Originally I pointed to the lack of documentation (which states, that the default alignment in records is 1-2 bytes).

That indeed must be fixed.

Jonas

Reply by email

Пётр Косаревский
to Jonas Maebe
Show quoted text 7 lines
> {$PACKRECORDS n} has almost nothing to do with this inconsistency. It does, since the member alignment is defined by {$packrecords n} You shouldn't get big gaps them between different variables if you specify a small alignment. You may get them between different record members if you don't change the packrecords setting as well.

I meant, that global variables are 16 bytes aligned by default. FPC 2.0.3, i386, win32. Even with -Cp386.

{$A-}
type
  a= record
     a: int64;
     b: byte;
     end;
var
  x: a;
  y: byte;
  yy: longword; // 3 bytes gap
  zz: longword; // 9 bytes gap. it's not much, but it's more than one third of user variables space (rtl not counted)
  z: a;
begin
writeln(sizeof(a));
writeln(longint(@x));
writeln(longint(@y));
writeln(longint(@z));
end.

Reply by email

Peter Vreman
to Пётр Косаревский: Main warning at the end of the message. 1. I think, that when you optimize for size,…
Show quoted text 24 lines
{$A-} type a= record a: int64; b: byte; end; var x: a; y: byte; // these are yy: longword; // 9 zz: longword; // bytes z: a; begin writeln(sizeof(a)); writeln(longint(@x)); writeln(longint(@y)); writeln(longint(@z)); end. This program makes me feel strange: it aligns records by 16 bytes for no reason (last two addresses are odd). (Don't forget {$A-})

You are mixing record alignment and memory alignment.

Reply by email