-
Notifications
You must be signed in to change notification settings - Fork 17.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
proposal: spec: define/imply memory layout of tagged struct fields #10014
Comments
I would love to have the source order (read: documentation order) and layout be totally separate, and both be under programmer's control. (the programmer might know optimal better than the compiler, if optimal means more than packing, like getting on different cache lines) You could imagine a struct tag that declares the index position, or a magic zero-width field that declares the order for the rest:
|
I'd be happy to have the compiler pack the structs however it likes, unless On Thu, Feb 26, 2015 at 9:42 AM, Brad Fitzpatrick [email protected]
|
I second this. However, this can break some existing code I guess (e.g. if you pass a struct to a syscall). |
That would make it difficult to hunt cacheline pingpong issues in shared structs. Those can also happen in imported packages. Also handover to C, kernel API, wire-formats, etc. might become hairy and even lead to surprising bugs. I would switch the default or make it even a new keyword, since it technically is no struct as we know it from C family languages anymore, but something else. That will be very surprising. A struct has the following three important properties for me:
But a tool like http://linux.die.net/man/1/pahole might prove to be very useful for Go. |
I agree we probably can't make the default reorder fields for Go 1.x. But On Thu, Feb 26, 2015 at 10:55 AM, Ingo Oeser [email protected]
|
Even in Go 2 it would be very surprising to have random breakage of wire formats, binary formats, in process ABI, etc. when using the I also doubt the compiler knows enough about data access patterns to optimize it well enough for the general case. I really have a strong preference for advise instead of premature compiler optimizations here. But that's just me... |
Not offering an opinion about this proposal, but
all imply some kind of unsafe code and depending on ordering that isn't guaranteed by the Go specification today. |
The ordering of struct fields in memory only has semantic importance when doing something unsafe or when using reflect, the latter of which can be of guaranteed source-order independent of actual memory layout. Use of unsafe specifically has non-defined, non-guaranteed behavior, and proper use of cgo is increasingly moving toward the idea that memory cannot be shared across the language boundary. There may have been a time when it would have been more important to define, but, as discussed above, those use cases are evaporating. Still, if we really must define it, the major implementations' current approach of source-order and non-packing should become official; it retains control we already have in practice without adding syntax or more magic comments. Tooling (rather than the compiler) can rewrite type declarations to meet user needs, and even generate packed structs (such as for use in large slice datasets) with converters to and from a well-aligned form when accessing individual elements. |
@Jragonmiris a few things would need to happen for pointers across cgo to be safe (some of these may already be enforced):
|
@Jragonmiris Please don't hijack this issue for the cgo problem of passing objects into C land. While related, this current issue is strictly about whether the language should have a way to tell the implementation that struct fields should remain in source order (independent of whether that's the de facto case now or not). Thanks. |
There was an utility that detects when structs waste space on padding. Can we move that check into vet? |
If you do, please don't enable it by default. |
It seems fine to me for the spec not to guarantee anything about struct field order in memory. The spec doesn't operate at that level. That said, no Go compiler should probably ever reorder struct fields. That seems like it is trying to solve a 1970s problem, namely packing structs to use as little space as possible. The 2010s problem is to put related fields near each other to reduce cache misses, and (unlike the 1970s problem) there is no obvious way for the compiler to pick an optimal solution. A compiler that takes that control away from the programmer is going to be that much less useful, and people will find better compilers. |
Easy exchange of Go heap data into C programs (which I am not at all sure This would however require a second knob for all those places where the On Fri, Apr 10, 2015 at 1:38 AM, Russ Cox [email protected] wrote:
|
|
I think Golang should at least add one feature: pack(1) for struct when u deal with USB or other hardware related data structures, pack(1) is very natural for a struct's definition. for example: #pragma pack(push,1) |
@finomxf That is a complex feature that requires significant compiler support. For now and the foreseeable future I suggest defining the type using |
thanks @ianlancetaylor |
The alternative to permitting the compiler to rearrange the struct fields is to change the language spec to specify the struct field layout. We should do one or the other. My experience with GCC's struct reorg pass is that it rarely applies to real programs. There are too many constraints in practice. That may be less true with Go, but, for example, programs that use encoding/binary to save struct data require consistent field layout but do not use tags to ensure it. |
FWIW Linux kernel can now randomize struct layout to some degree to improve security: |
How would randomization interact with deterministic builds? If a package build picks a new random seed every time, it's no longer deterministic; if it doesn't, it's no longer random. |
@tv42 There might be a flag to enable/disable. But the field layout shouldn't be observable from a Go program (*), so in that sense the behavior of the program would remain deterministic. (*) The compiler may automatically disable field layout randomization for a struct if an address is taken of a field (or |
I think it would be very hard to automatically randomize struct field layout in Go, given that people can use reflect to do almost anything. It would be possible to randomize struct field layout on an opt-in basis, of course. The Linux kernel plugin is opt-in, except for structs that are only function pointers. To achieve similar security results in Go, a likely more profitable approach would be an unpredictable, but stable, ordering of interface method tables. |
I think it makes a lot more sense to formalize the struct packing algorithm that is used (either in the language spec or if that's too constraining then at least by the compiler - gc and gccgo probably pack the same for most if not all targets) as opposed to having the compiler reorder fields for some not-entirely-clear benefit. Packing can prefer size (fitting fields into otherwise unused space) or possibly performance (alignment), or if you need the layout to match some other spec exactly the concern is precision and compatibility. Sometimes these are conflicting objectives - you might want to keep things as small as possible but also need precise control. These concerns seem much too nuanced for the compiler to be guessing about. The thing is, usually packing is not a significant issue. When it is, it's probably better to just reorder the fields in your source code - it's explicit, obvious, simple and compatible with what is being done now. Seems to me it's more of an issue that what the compiler does is not standardized than the fact that the compiler doesn't magically guess what order you want the fields in. |
I just ran into this while thinking about another issue. A thing that I find surprising, but in retrospect it makes sense: 0-byte fields may or may not have the same address as following non-zero-byte fields. I don't know whether this is intentional, or whether code is already depending on it, but it might be surprising. I do note, there is exactly one thing in the existing spec that hints at an answer to the struct packing question. The word "padding" occurs exactly once in the spec, on an example of a struct with a blank-identifier field with the comment There's also the reflect documentation mentioning both My default preference would be to specify that padding is inserted only as needed for alignment, but there's a subtle problem here: We can check alignment requirements with FieldAlign() at runtime using reflect. There's no other way to check them, and we can't necessarily predict them for architectures we don't have handy to test on. So, for instance, does a structure with an |
FYI, the distinction between |
We cannot change existing structs to reorder fields this at this point. What we could do is introduce notation that arranges fields more efficiently (not necessarily "packed" but with reduced internal fragmentation). We don't have a concrete proposal at this point and this is something one can manually address for now, so closing. |
I think it might make sense to formally state that the offset of each struct field is definitely not less than the offset of any previous struct field, and that struct fields do not contain overlapping bytes. That is obviously the case currently, and I don't think it could be changed without breaking things, it's just never been directly specified. |
This is half-baked idea that needs more thought, written down so it doesn't get forgotten.
Currently, the spec does not specify how struct fields are laid out in memory (e.g. source order, or optimally packed, or any other order). That is, theoretically, a compiler is free to optimize the layout. However, currently all (?) compilers lay out struct fields in source order, possibly wasting significant amounts of space due to padding (struct internal fragmentation).
It is not unlikely that code depends on this property.
Instead of requiring all structs to follow source order layout of fields, we could say that structs with tagged fields are the ones that are laid out in source order. Here are some variants of the same idea:
The text was updated successfully, but these errors were encountered: