www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - core.runtime.GC memory alignment

reply "David Nadlinger" <code klickverbot.at> writes:
The documentation for the GC.malloc family of functions mentions 
that they return "an aligned block of managed memory from the 
garbage collector" without any further specification about what 
that level of alignment is supposed to be.

What do we actually want to guarantee here? "Sufficient for any 
built-in type"?

Note that my question is not about how the current implementation 
behaves, but about what the actual API guarantee should be.

David
Oct 27 2013
parent reply "Peter Alexander" <peter.alexander.au gmail.com> writes:
On Sunday, 27 October 2013 at 15:49:00 UTC, David Nadlinger wrote:
 What do we actually want to guarantee here? "Sufficient for any 
 built-in type"?
It should guarantee the same as what malloc guarantees IMO: "The malloc() and calloc() functions return a pointer to the allocated memory that is suitably aligned for any kind of variable." I see no reason to diverge from that.
Oct 27 2013
parent reply "David Nadlinger" <code klickverbot.at> writes:
On Sunday, 27 October 2013 at 16:19:28 UTC, Peter Alexander wrote:
 "The malloc() and calloc() functions return a pointer to the 
 allocated memory that is suitably aligned for any kind of 
 variable."

 I see no reason to diverge from that.
Welö, except for the fact that "any kind of variable" is not well-defined for a language that supports user-defined alignment restrictions: --- struct Foo { align(8192) byte b; } template Seq(T...) { alias Seq = T; } void main() { import core.memory, core.stdc.stdio, core.stdc.stdlib; foreach (alloc; Seq!(malloc, GC.malloc)) { auto mem = cast(Foo*)alloc(Foo.sizeof); printf("%u\n", cast(uint)(cast(size_t)mem & (Foo.alignof - 1))); } } --- David
Oct 27 2013
parent reply "Peter Alexander" <peter.alexander.au gmail.com> writes:
On Sunday, 27 October 2013 at 17:53:14 UTC, David Nadlinger wrote:
 On Sunday, 27 October 2013 at 16:19:28 UTC, Peter Alexander 
 wrote:
 "The malloc() and calloc() functions return a pointer to the 
 allocated memory that is suitably aligned for any kind of 
 variable."

 I see no reason to diverge from that.
Welö, except for the fact that "any kind of variable" is not well-defined for a language that supports user-defined alignment restrictions:
Correct me if I'm wrong, but my understanding is that align(N) on a member only specifies the alignment *within the struct*, i.e. the member offsets. There's no guarantee that the Foo object itself will be aligned to 8192. It's not really memory alignment.
Oct 27 2013
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 10/27/13 12:51 PM, Peter Alexander wrote:
 On Sunday, 27 October 2013 at 17:53:14 UTC, David Nadlinger wrote:
 On Sunday, 27 October 2013 at 16:19:28 UTC, Peter Alexander wrote:
 "The malloc() and calloc() functions return a pointer to the
 allocated memory that is suitably aligned for any kind of variable."

 I see no reason to diverge from that.
Welö, except for the fact that "any kind of variable" is not well-defined for a language that supports user-defined alignment restrictions:
Correct me if I'm wrong, but my understanding is that align(N) on a member only specifies the alignment *within the struct*, i.e. the member offsets. There's no guarantee that the Foo object itself will be aligned to 8192. It's not really memory alignment.
Yah, something like that. I found align(NNN) underspecified and underpowered for my work with allocators. As a simple matter, NNN must be a literal, not a compile-time expression. You can't even write e.g. align(size_t.alignof), which is fairly basic. Andrei
Oct 27 2013
next sibling parent reply "Peter Alexander" <peter.alexander.au gmail.com> writes:
On Sunday, 27 October 2013 at 20:30:05 UTC, Andrei Alexandrescu 
wrote:
 Yah, something like that. I found align(NNN) underspecified and 
 underpowered for my work with allocators. As a simple matter, 
 NNN must be a literal, not a compile-time expression. You can't 
 even write e.g. align(size_t.alignof), which is fairly basic.
Yeah I noticed the literal requirement as well and thought it was quite odd...
Oct 27 2013
parent Manu <turkeyman gmail.com> writes:
On 28 October 2013 06:46, Peter Alexander <peter.alexander.au gmail.com>wrote:

 On Sunday, 27 October 2013 at 20:30:05 UTC, Andrei Alexandrescu wrote:

 Yah, something like that. I found align(NNN) underspecified and
 underpowered for my work with allocators. As a simple matter, NNN must be a
 literal, not a compile-time expression. You can't even write e.g.
 align(size_t.alignof), which is fairly basic.
Yeah I noticed the literal requirement as well and thought it was quite odd...
I reported that as a deficiency over a year ago, I recall discussion, and thought it was to be fixed. Guess it didn't eventuate.
Oct 27 2013
prev sibling parent reply "monarch_dodra" <monarchdodra gmail.com> writes:
On Sunday, 27 October 2013 at 20:30:05 UTC, Andrei Alexandrescu 
wrote:
 Yah, something like that. I found align(NNN) underspecified and 
 underpowered for my work with allocators. As a simple matter, 
 NNN must be a literal, not a compile-time expression. You can't 
 even write e.g. align(size_t.alignof), which is fairly basic.


 Andrei
In that case, we also need to specify how alignOf works. For example: struct S { align(128) int i; } static assert(S.alignOf == 128); If "align(N)" is supposed to only mean "alignement relative to the start of the struct", why the heck is S's "alignOf" 128? Also, (but I can't double check it right now), I seem to remember that there are odd things, like "ulong.alignOf == 8", yet if you declare one on the stack, you notice it's only 4 aligned (at least, on my win32 (I think) it is...)
Oct 27 2013
next sibling parent reply "John Colvin" <john.loughran.colvin gmail.com> writes:
On Sunday, 27 October 2013 at 20:50:30 UTC, monarch_dodra wrote:
 On Sunday, 27 October 2013 at 20:30:05 UTC, Andrei Alexandrescu 
 wrote:
 Yah, something like that. I found align(NNN) underspecified 
 and underpowered for my work with allocators. As a simple 
 matter, NNN must be a literal, not a compile-time expression. 
 You can't even write e.g. align(size_t.alignof), which is 
 fairly basic.


 Andrei
 "alignment relative to the start of the struct"
what does that even mean? Alignment means (address % alignment) == 0 what would be the use case for guaranteeing ((fieldAddress - StructAddress) % alignment) == 0 that can't be just as easily done using normal alignment
Oct 27 2013
next sibling parent "monarch_dodra" <monarchdodra gmail.com> writes:
On Sunday, 27 October 2013 at 21:20:12 UTC, John Colvin wrote:
 On Sunday, 27 October 2013 at 20:50:30 UTC, monarch_dodra wrote:
 On Sunday, 27 October 2013 at 20:30:05 UTC, Andrei 
 Alexandrescu wrote:
 Yah, something like that. I found align(NNN) underspecified 
 and underpowered for my work with allocators. As a simple 
 matter, NNN must be a literal, not a compile-time expression. 
 You can't even write e.g. align(size_t.alignof), which is 
 fairly basic.


 Andrei
 "alignment relative to the start of the struct"
what does that even mean? Alignment means (address % alignment) == 0 what would be the use case for guaranteeing ((fieldAddress - StructAddress) % alignment) == 0 that can't be just as easily done using normal alignment
Observation shows that that's *how* align currently behaves anyways: http://forum.dlang.org/thread/difcpyoejpbngzpoynce forum.dlang.org#post-nhrvixioystchhdpmakg:40forum.dlang.org Its like its incorrectly named, and actually specifies "paking". However, the two concepts seem packed into one, resulting in some confusing semantics and bahaviors.
Oct 27 2013
prev sibling parent "Peter Alexander" <peter.alexander.au gmail.com> writes:
On Sunday, 27 October 2013 at 21:20:12 UTC, John Colvin wrote:
 On Sunday, 27 October 2013 at 20:50:30 UTC, monarch_dodra wrote:
 On Sunday, 27 October 2013 at 20:30:05 UTC, Andrei 
 Alexandrescu wrote:
 Yah, something like that. I found align(NNN) underspecified 
 and underpowered for my work with allocators. As a simple 
 matter, NNN must be a literal, not a compile-time expression. 
 You can't even write e.g. align(size_t.alignof), which is 
 fairly basic.


 Andrei
 "alignment relative to the start of the struct"
what does that even mean? Alignment means (address % alignment) == 0
Alignment also means alignment within an object. It's more commonly referred to as "padding" but it's a form of alignment.
 what would be the use case for guaranteeing ((fieldAddress - 
 StructAddress) % alignment) == 0 that can't be just as easily 
 done using normal alignment
Usually it's done when you are reading binary files that are in some particular format that may or may not pack data in the same way that you'd get from normal alignment.
 If "align(N)" is supposed to only mean "alignement relative to 
 the start of the struct", why the heck is S's "alignOf" 128?

 Also, (but I can't double check it right now), I seem to 
 remember that there are odd things, like "ulong.alignOf == 8", 
 yet if you declare one on the stack, you notice it's only 4 
 aligned (at least, on my win32 (I think) it is...)
I believe .alignof also refers to structure alignment, it doesn't guarantee alignment in memory or on the stack (as you've discovered).
Oct 27 2013
prev sibling parent reply Manu <turkeyman gmail.com> writes:
On 28 October 2013 06:50, monarch_dodra <monarchdodra gmail.com> wrote:

 On Sunday, 27 October 2013 at 20:30:05 UTC, Andrei Alexandrescu wrote:

 Yah, something like that. I found align(NNN) underspecified and
 underpowered for my work with allocators. As a simple matter, NNN must be a
 literal, not a compile-time expression. You can't even write e.g.
 align(size_t.alignof), which is fairly basic.


 Andrei
In that case, we also need to specify how alignOf works. For example: struct S { align(128) int i; } static assert(S.alignOf == 128); If "align(N)" is supposed to only mean "alignement relative to the start of the struct", why the heck is S's "alignOf" 128? Also, (but I can't double check it right now), I seem to remember that there are odd things, like "ulong.alignOf == 8", yet if you declare one on the stack, you notice it's only 4 aligned (at least, on my win32 (I think) it is...)
I had a lot of informal conversations with Walter trying to get my head around the details here. To my recollection, the intent was that it should behave like C, and that S.alignof must certainly == 128 in that case. It can't work otherwise. Alignment must be inherited by parent structures. I also recall commenting on that case with ulong. On most architectures it only needs to be 4 byte aligned, but that one is arch specific.
Oct 27 2013
parent reply "monarch_dodra" <monarchdodra gmail.com> writes:
On Monday, 28 October 2013 at 02:44:54 UTC, Manu wrote:
 I had a lot of informal conversations with Walter trying to get 
 my head
 around the details here.
 To my recollection, the intent was that it should behave like 
 C, and that
 S.alignof must certainly == 128 in that case. It can't work 
 otherwise.
 Alignment must be inherited by parent structures.
But is that really what it means though? From the above conversation, it would instead appear to mean that: struct S { int i; align(128) int j; } in this case, the *padding* needed until we reach j is 128 bytes. It doesn't mean that S itself need to be 128 aligned.
 I also recall commenting on that case with ulong. On most 
 architectures it
 only needs to be 4 byte aligned, but that one is arch specific.
Does it make sense (and should it be illegal) in that case to have anything with alignment > 8 ? I just don't see how something like "malloc" would be able to deal with them otherwise... ?
Oct 28 2013
next sibling parent "David Nadlinger" <code klickverbot.at> writes:
On Monday, 28 October 2013 at 18:33:49 UTC, monarch_dodra wrote:
 But is that really what it means though? From the above 
 conversation, it would instead appear to mean that:
 struct S
 {
     int i;
     align(128) int j;
 }
 in this case, the *padding* needed until we reach j is 128 
 bytes.
Like Manu, I think the only sane way for align() to behave is like GCC's/Clang's "aligned" attribute, which would both add padding such that j is at offset 128, and ensure that any variables of type S are aligned to 128 bytes as well.
 I just don't see how something like "malloc" would be able to 
 deal with them otherwise... ?
Raw system malloc(), without knowing anything about the type, obviously cannot honor any special alignment requests. Thus, usually special primitives exist which accept an additional parameter to specify the target alignment (cf. the std.allocator discussion). If you really want to use C malloc() for your 256-byte-aligned objects, you have to handle things yourself, e.g. by over-allocating and then adjusting the base pointer or similar strategies. David
Oct 28 2013
prev sibling parent reply Manu <turkeyman gmail.com> writes:
On 29 October 2013 04:33, monarch_dodra <monarchdodra gmail.com> wrote:

 On Monday, 28 October 2013 at 02:44:54 UTC, Manu wrote:

 I had a lot of informal conversations with Walter trying to get my head
 around the details here.
 To my recollection, the intent was that it should behave like C, and that
 S.alignof must certainly == 128 in that case. It can't work otherwise.
 Alignment must be inherited by parent structures.
But is that really what it means though? From the above conversation, it would instead appear to mean that: struct S { int i; align(128) int j; } in this case, the *padding* needed until we reach j is 128 bytes. It doesn't mean that S itself need to be 128 aligned.
Both should be true, if it's not, it's a bug. I also recall commenting on that case with ulong. On most architectures it
 only needs to be 4 byte aligned, but that one is arch specific.
Does it make sense (and should it be illegal) in that case to have anything with alignment > 8 ? I just don't see how something like "malloc" would be able to deal with them otherwise... ?
Well SIMD vector's require at least 16 byte alignment, so say goodbye to a major subsection of your CPU ;) I've never seen a malloc implementation that returns memory that is less than 16 byte aligned. If you expect higher alignment, typically you use OS provided AliognedAlloc primitives, or you use tricks like overallocating and offseting.
Oct 28 2013
parent reply "growler" <growlercab gmail.com> writes:
On Tuesday, 29 October 2013 at 00:45:59 UTC, Manu wrote:
 On 29 October 2013 04:33, monarch_dodra 
 <monarchdodra gmail.com> wrote:

 On Monday, 28 October 2013 at 02:44:54 UTC, Manu wrote:

 I had a lot of informal conversations with Walter trying to 
 get my head
 around the details here.
 To my recollection, the intent was that it should behave like 
 C, and that
 S.alignof must certainly == 128 in that case. It can't work 
 otherwise.
 Alignment must be inherited by parent structures.
But is that really what it means though? From the above conversation, it would instead appear to mean that: struct S { int i; align(128) int j; } in this case, the *padding* needed until we reach j is 128 bytes. It doesn't mean that S itself need to be 128 aligned.
Both should be true, if it's not, it's a bug.
http://dlang.org/attribute.html#align --- The alignment for the fields of an aggregate does not affect the alignment of the aggregate itself - that is affected by the alignment setting outside of the aggregate. align (2) struct S { align (1): byte a; // placed at offset 0 int b; // placed at offset 1 long c; // placed at offset 5 } auto sz = S.sizeof; // 14 --- My understanding of that is S is not affected by the alignment of its fields.
Oct 28 2013
next sibling parent reply "growler" <growlercab gmail.com> writes:
 My understanding of that is S is not affected by the alignment 
 of its fields.
To clarify, I mean the *alignment* of S is not affected by the alignment of its fields.
Oct 28 2013
parent reply Artur Skawina <art.08.09 gmail.com> writes:
On 10/29/13 06:36, growler wrote:
 My understanding of that is S is not affected by the alignment of its fields.
To clarify, I mean the *alignment* of S is not affected by the alignment of its fields.
The alignment of an object must be at least as large as the max of the alignment of all fields that it contains, obviously. The object layout never changes; using a smaller alignment for the parent object would make it impossible to guarantee the specified field alignment. artur
Oct 29 2013
parent "growler" <growlercab gmail.com> writes:
On Tuesday, 29 October 2013 at 12:31:00 UTC, Artur Skawina wrote:
 On 10/29/13 06:36, growler wrote:
 My understanding of that is S is not affected by the 
 alignment of its fields.
To clarify, I mean the *alignment* of S is not affected by the alignment of its fields.
The alignment of an object must be at least as large as the max of the alignment of all fields that it contains, obviously. The object layout never changes; using a smaller alignment for the parent object would make it impossible to guarantee the specified field alignment. artur
You're right, it would be nonsensical otherwise.
Oct 29 2013
prev sibling parent Manu <turkeyman gmail.com> writes:
On 29 October 2013 13:47, growler <growlercab gmail.com> wrote:

 On Tuesday, 29 October 2013 at 00:45:59 UTC, Manu wrote:

 On 29 October 2013 04:33, monarch_dodra <monarchdodra gmail.com> wrote:

  On Monday, 28 October 2013 at 02:44:54 UTC, Manu wrote:
  I had a lot of informal conversations with Walter trying to get my head
 around the details here.
 To my recollection, the intent was that it should behave like C, and
 that
 S.alignof must certainly == 128 in that case. It can't work otherwise.
 Alignment must be inherited by parent structures.
But is that really what it means though? From the above conversation, it would instead appear to mean that: struct S { int i; align(128) int j; } in this case, the *padding* needed until we reach j is 128 bytes. It doesn't mean that S itself need to be 128 aligned.
Both should be true, if it's not, it's a bug.
http://dlang.org/attribute.**html#align<http://dlang.org/attribute.html#align> --- The alignment for the fields of an aggregate does not affect the alignment of the aggregate itself - that is affected by the alignment setting outside of the aggregate. align (2) struct S { align (1): byte a; // placed at offset 0 int b; // placed at offset 1 long c; // placed at offset 5 } auto sz = S.sizeof; // 14 --- My understanding of that is S is not affected by the alignment of its fields.
This is a strange test. It looks like your align(1) is intended to mean pack(1), and if that is the case, then the observed sizeof is correct. with pack(1), the size would be 8+4+1 = 13, but then the struct marked align(2) must be padded to a multiple of it's alignment, so 14. What you observe is probably correct, except I don't think the use of align(1) to mean pack(1) is right. That should raise discussion...
Oct 28 2013