Skip to content
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

re-order struct fields for better performance and memory usage #168

Open
1 of 7 tasks
andrewrk opened this issue Aug 3, 2016 · 13 comments
Open
1 of 7 tasks

re-order struct fields for better performance and memory usage #168

andrewrk opened this issue Aug 3, 2016 · 13 comments
Labels
enhancement Solving this issue will likely involve adding new logic or components to the codebase. frontend Tokenization, parsing, AstGen, Sema, and Liveness. optimization
Milestone

Comments

@andrewrk
Copy link
Member

andrewrk commented Aug 3, 2016

In Zig, you have no guarantee about the memory layout of a struct unless you make a struct packed or export.

Take advantage of this:

  • align fields appropriately for fast access. Allow explicitly setting the minimum alignment per field.
  • re-order smaller fields to fit in alignment gaps for smaller memory usage
  • gaps created by maybe types, error unions, and other containers can be filled by smaller types
  • put large fixed size arrays at the end for better cache performance of the rest of the struct fields
  • to enable the same function code to work for multiple specialized data types. For example, a field that uses a parameterized type might be moved to the last position, so that functions which only reference fields before that one can share the same codegen.

Provide safety against incorrect assumptions about field order:

  • provide builtin function @container_base(inline T: type, field_name_symbol, field_pointer) -> &T (better name suggestion welcome @thejoshwolfe). This function takes a pointer to a container field, the container type, and a symbol which is the name of the field, and returns a pointer to the struct. Programmers should use this builtin instead of futzing around with pointers to go from a field pointer to the container base pointer.
  • in debug mode, order fields backwards from declaration order. This ensures that an incorrect assumption will not work in debug mode. This safety trick may turn out to be too expensive. If so, we will do the same field ordering logic in debug mode and release mode.
@andrewrk andrewrk added enhancement Solving this issue will likely involve adding new logic or components to the codebase. lowpriority optimization labels Aug 3, 2016
@felipecrv
Copy link

Don't you think it would be better to let the programmer decide the packing order of the fields? If the compiler decides how to pack stuff, working programs may break from one compiler version to another. I don't think the complexity and risk this optimization adds is worth it. If you still want to add it, you can provide a special annotation to indicate that the compiler should decide how to pack the struct.

@andrewrk
Copy link
Member Author

andrewrk commented Sep 5, 2016

The programmer has a choice: leave the field order undefined and give the power to the compiler, or use a packed struct and specify the order themselves. Packed structs are not done yet. See #183

@kiljacken
Copy link

So if I need a guarantee on the order of a structure it has to be packed,
right?

If so you'd probably need a chapter of documentation describing how to
properly align field in structs to avoid (or at least mitigate) people
complaining about segfaults :)

On Mon, Sep 5, 2016, 07:29 Andrew Kelley notifications@github.com wrote:

The programmer has a choice: leave the field order undefined and give the
power to the compiler, or use a packed struct and specify the order
themselves. Packed structs are not done yet. See #183
#183


You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub
#168 (comment), or mute
the thread
https://github.com/notifications/unsubscribe-auth/AAMxqYFC5UHwisC5e5fWQ664YNSloSNiks5qm6ipgaJpZM4Jb6L_
.

@felipecrv
Copy link

The programmer has a choice: leave the field order undefined and give the power to the compiler, or use a packed struct and specify the order themselves. Packed structs are not done yet. See #183

Yeah. But don't make undefined the default behavior. You still going to have padding of course, but the order of fields will be guaranteed.

@andrewrk
Copy link
Member Author

andrewrk commented Sep 5, 2016

So if I need a guarantee on the order of a structure it has to be packed,
right?

Correct.

There is also the concept of export or extern structs which have the C rules about layout and these are guaranteed to be compatible with other C code.

If so you'd probably need a chapter of documentation describing how to
properly align field in structs to avoid (or at least mitigate) people
complaining about segfaults :)

Can you explain a situation that would result in a segfault because of code relying on field order?

@andrewrk
Copy link
Member Author

andrewrk commented Sep 5, 2016

don't make undefined the default behavior. You still going to have padding of course, but the order of fields will be guaranteed.

Can you explain the use case where you want to rely on field order?

@kiljacken
Copy link

kiljacken commented Sep 5, 2016

x86 will segfault upon doing an unaligned memory access (typically aligned to data size). Imagine a structure like this:

struct Gizmo {
    tag: u8,
    thingy: u16
}

Assuming allocated memory is aligned, dereferencing the field thingy of a pointer to a struct that's a Gizmo will result in a segfault.

There's a few details with regard to the load/store instructions used, SSE load/store segfault, whereas (iirc) the normal mov "just" suffers performace wise due to splitting into several aligned loads/stores.

@felipecrv
Copy link

don't make undefined the default behavior. You still going to have padding of course, but the order of fields will be guaranteed.

Can you explain the use case where you want to rely on field order?

Most of the use cases can be considered hacks. Considering that we're talking about a low-level language, these hacks are use cases. If the compiler defines the order of the the fields it becomes hard to analyze memory dumps (there may be many situations where raw memory is the only thing you have to debug something), appending a field to a struct can lead to a reordering of the fields that existed before. This gives up the ability to write code that deals with data that was generated based on the previous struct specification.

Maybe I'm being too pessimistic here, but my impression is that this complexity would backfire in unpredictable ways if you have it by default.

@andrewrk
Copy link
Member Author

andrewrk commented Sep 11, 2016

Maybe I'm being too pessimistic here, but my impression is that this complexity would backfire in unpredictable ways if you have it by default.

If that happens we can always change the decision. And code that does not assume field order will continue to work correctly if it can then start assuming field order.

Also there will be a builtin function for getting the address of a field based on its name. Example: @getFieldPointer(struct_instance, "field_name"). Note that "field_name" will have to be a compile time constant.

@kyle-github
Copy link

Is this something where the field enumeration in #383 would help?

Note for the last part, is this not the same as getting the address of the field in the strut via &?

@andrewrk
Copy link
Member Author

Note for the last part, is this not the same as getting the address of the field in the strut via &?

See http://ziglang.org/documentation/#builtin-fieldParentPtr

@andrewrk
Copy link
Member Author

This issue will now have a meaningful impact on async functions since it applies to all local variables.

@andrewrk andrewrk modified the milestones: 0.5.0, 0.6.0 Sep 11, 2019
@andrewrk andrewrk added the frontend Tokenization, parsing, AstGen, Sema, and Liveness. label Oct 17, 2019
@andrewrk andrewrk removed this from the 0.6.0 milestone Oct 17, 2019
@andrewrk andrewrk added this to the 0.7.0 milestone Oct 17, 2019
@andrewrk andrewrk modified the milestones: 0.7.0, 0.8.0 Apr 15, 2020
@andrewrk andrewrk modified the milestones: 0.8.0, 0.10.0 May 19, 2021
Vexu added a commit to Vexu/zig that referenced this issue Jan 16, 2023
This is a simple starting version of the optimization described in ziglang#168
where the fields are just sorted by order of descending alignment.
@Cloudef
Copy link
Contributor

Cloudef commented Jun 7, 2024

I mentioned in discord but it would be useful if you could tag certain fields to be omit from reordering but let zig do whatever it wants with rest. At top of the struct I'm storing small tags which I take the address of, the tags contain the runtime information about the type of the struct the tag is part of and offset to the struct's base. I want to keep this tag small so u8 for offsets would've been fine if not struct reordering. I ended up storing field index instead and use inline for to find the offset of the field, which also is fine but adds at least small cost of switch.

Extern struct is not applicable here because I actually do want the zig struct semantics for rest of the fields and also not have the extern struct must only contain extern struct restriction.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement Solving this issue will likely involve adding new logic or components to the codebase. frontend Tokenization, parsing, AstGen, Sema, and Liveness. optimization
Projects
None yet
Development

No branches or pull requests

5 participants