Ensuring Alignment with unsafe.Pointer

257 views
Skip to first unread message

Timur Celik

unread,
Jun 10, 2025, 11:03:31 AMJun 10
to golang-nuts
Consider the following generic type, which ensures to be large enough by allocating enough padding A and the to-be-aligned type T:

type Alignment interface {
Align16 | Align64
}
type Align16 = [16]byte
type Align64 = [64]byte

type Aligned[T any, A Alignment] struct {
pad A
val T
}

func (p *Aligned[T, A]) Value() *T {
size := unsafe.Sizeof(*new(A))
align := size - uintptr(unsafe.Pointer(&p.pad[0]))&(size-1)
return (*T)(unsafe.Pointer(&p.pad[align]))
}


Under the assumption that T has no heap pointers, is this safe? Or asked in another way, under which circumstances would this break?

Timur Celik

unread,
Jun 11, 2025, 7:09:42 AMJun 11
to golang-nuts
After reading a bit I came to the conclusion:
  • Aligned should include the structs.HostLayout field. And T of course too.
  • Aligned must be allocated on heap, since the stack might be moved.
  • And as said, T must not contain any heap pointers.
 Then this *should* be ok with the current version of Go.

Robert Engels

unread,
Jun 11, 2025, 8:47:57 AMJun 11
to Timur Celik, golang-nuts
I don’t think the code works at all - at least not from an alignment perspective. For instance, assume T is one byte, and you use align 64, it is aligned on 65 byte boundaries. (This may not be fully correct need to read spec on structure member alignment - but the gist should be true) 

On Jun 10, 2025, at 10:04 AM, Timur Celik <clk...@gmail.com> wrote:


--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
To view this discussion visit https://groups.google.com/d/msgid/golang-nuts/d00058ec-cb9b-495f-807d-54397be94df2n%40googlegroups.com.

Mikk Margus

unread,
Jun 11, 2025, 12:07:18 PMJun 11
to golan...@googlegroups.com
Oh, yeah, you're right, that does seem to be the intent.

From what I know, and based on the documentation I linked before[1],
you can, at most, get 32-bit alignment on 32-bit platforms and 64-bit
alignment on 64-bit platforms with Go. At least with this approach.
For in-memory size/alignment purposes, an array type [N]T is equivalent
to just having N discrete attributes of type T on the struct.

Which is to say, an array of 64 bytes does not increase your alignment
above 1 byte, and the best you'll get is an alignment of 8 bytes via
uint64, assuming you're on a 64-bit platform, and only 4 bytes on a
32-bit platform. And [2]uint64 would not fare any better.

Also, I appear to have accidentally veered off-list for this discussion
with Robert Engels, my apologies.


[1] https://go.dev/s/regabi

On 6/11/25 17:00, Robert Engels wrote:
> I don’t think so - it a “64 BYTE aligned” to prevent false sharing -
> according to the original code.
>
>> On Jun 11, 2025, at 8:39 AM, Def Ceb <mikk....@gmail.com> wrote:
>>
>> 
>> To get a n-bit-aligned pointer (where n is 16 or 64) to an arbitrary
>> value using generics, is it not?
>>
>> On Wed, Jun 11, 2025, 16:36 Robert Engels <ren...@ix.netcom.com
>> <mailto:ren...@ix.netcom.com>> wrote:
>>
>> It depends on what the OP is trying to align… eg is it to prevent
>> false sharing?
>>
>>> On Jun 11, 2025, at 8:14 AM, Def Ceb <mikk....@gmail.com
>>> <mailto:mikk....@gmail.com>> wrote:
>>>
>>> 
>>> https://go.dev/s/regabi <https://go.dev/s/regabi>
>>> I believe this is true. The arrays of bytes would not enforce
>>> alignment any tighter than that of one byte. Replacing the
>>> aliases of Align16 and Align64 from byte arrays to uint16/uint64
>>> would fix this.
>>>
>>> On Wed, Jun 11, 2025, 15:47 Robert Engels <ren...@ix.netcom.com
>>> <mailto:ren...@ix.netcom.com>> wrote:
>>>
>>> I don’t think the code works at all - at least not from an
>>> alignment perspective. For instance, assume T is one byte,
>>> and you use align 64, it is aligned on 65 byte boundaries.
>>> (This may not be fully correct need to read spec on structure
>>> member alignment - but the gist should be true)
>>>
>>>> On Jun 10, 2025, at 10:04 AM, Timur Celik <clk...@gmail.com
>>>> <mailto:clk...@gmail.com>> wrote:
>>>>
>>>> 
>>>> Consider the following generic type, which ensures to be
>>>> large enough by allocating enough padding A and the to-be-
>>>> aligned type T:
>>>>
>>>> type Alignment interface {
>>>> Align16 | Align64
>>>> }
>>>> type Align16 = [16]byte
>>>> type Align64 = [64]byte
>>>>
>>>> type Aligned[T any, A Alignment] struct {
>>>> pad A
>>>> val T
>>>> }
>>>>
>>>> func (p *Aligned[T, A]) Value() *T {
>>>> size := unsafe.Sizeof(*new(A))
>>>> align := size - uintptr(unsafe.Pointer(&p.pad[0]))&(size-1)
>>>> return (*T)(unsafe.Pointer(&p.pad[align]))
>>>> }
>>>>
>>>> Under the assumption that T has no heap pointers, is this
>>>> safe? Or asked in another way, under which circumstances
>>>> would this break?
>>>>
>>>> --
>>>> You received this message because you are subscribed to the
>>>> Google Groups "golang-nuts" group.
>>>> To unsubscribe from this group and stop receiving emails
>>>> from it, send an email to golang-
>>>> nuts+uns...@googlegroups.com <mailto:golang-
>>>> nuts+uns...@googlegroups.com>.
>>>> To view this discussion visit https://groups.google.com/d/
>>>> msgid/golang-nuts/d00058ec-
>>>> cb9b-495f-807d-54397be94df2n%40googlegroups.com <https://
>>>> groups.google.com/d/msgid/golang-nuts/d00058ec-
>>>> cb9b-495f-807d-54397be94df2n%40googlegroups.com?
>>>> utm_medium=email&utm_source=footer>.
>>>
>>> --
>>> You received this message because you are subscribed to the
>>> Google Groups "golang-nuts" group.
>>> To unsubscribe from this group and stop receiving emails from
>>> it, send an email to golang-nuts...@googlegroups.com
>>> <mailto:golang-nuts...@googlegroups.com>.
>>> To view this discussion visit https://groups.google.com/d/
>>> msgid/golang-nuts/D368E0E8-
>>> DE85-43D3-813E-60D359578DF8%40ix.netcom.com <https://
>>> groups.google.com/d/msgid/golang-nuts/D368E0E8-
>>> DE85-43D3-813E-60D359578DF8%40ix.netcom.com?
>>> utm_medium=email&utm_source=footer>.
>>>

Timur Celik

unread,
Jun 11, 2025, 1:14:00 PMJun 11
to golang-nuts
Sorry, I think the code needs more explanation: A value of type Aligned[T,A] will allocate enough memory to contain one instance of T with alignment A. The fields pad and val will contain only garbage, they only exist for allocating enough memory and could as well be a byte array. To get the aligned value ot T, the Value() method must be called. It returns an aligned pointer inside the pad field and casts it to *T. See the playground example:


One more thing worth noting, by allowing only alignments larger than Go max alignment of 8 byte, it's ensured that T has still the alignment gurantees that Go itself expects.

Axel Wagner

unread,
Jun 11, 2025, 3:08:28 PMJun 11
to Timur Celik, golang-nuts
I'm far from an expert on the runtime.
But I believe one condition under which this breaks is if `T` contains pointers (including pointer-like types, e.g. `string`, `map`, `[]T`…). The GC needs to know which memory contains pointers. If `T` is e.g. `*int` You are spreading a pointer over [N]byte (which is assumed to have no pointers) and an *int, meaning you both have a pointer where none is expected and store nonsense in a pointer.
My understanding is, that you violate the "equivalent memory layout"part of the unsafe.Pointer rules.

To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
To view this discussion visit https://groups.google.com/d/msgid/golang-nuts/7b9b2598-f39b-4980-889c-36353c5aa34cn%40googlegroups.com.
Reply all
Reply to author
Forward
0 new messages