Skip to content

Align structures for 64bit platforms for improve putting to CPU cacheline #18559

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

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

GermanAizek
Copy link

@GermanAizek GermanAizek commented May 14, 2025

Hi everyone, thank team so much for development PHP interpreter.

I think this PR will reduce minimum requirements for hardware, and it will decrease costs copying, moving, and creating object-structures only for common 64bit processors due to the 8-byte data alignment.

Smaller size structure or class, higher chance putting into CPU cache. Most processors are already 64 bit, so the change won't make it any worse.

Info about technique:

https://hpc.rz.rptu.de/Tutorials/AVX/alignment.shtml
https://wr.informatik.uni-hamburg.de/_media/teaching/wintersemester_2013_2014/epc-14-haase-svenhendrik-alignmentinc-presentation.pdf
https://en.wikipedia.org/wiki/Data_structure_alignment
https://stackoverflow.com/a/20882083
https://zijishi.xyz/post/optimization-technique/learning-to-use-data-alignment/

Pahole log:

  • Comment /* XXX {n} bytes hole, try to pack */ shows where optimization is possible by rearranging the order of fields structures and classes

Master branch

struct _phpdbg_breakbase_t {
        int                        id;                   /*     0     4 */
        uint8_t                    type;                 /*     4     1 */

        /* XXX 3 bytes hole, try to pack */

        zend_ulong                 hits;                 /*     8     8 */
        _Bool                      disabled;             /*    16     1 */

        /* XXX 7 bytes hole, try to pack */

        const char  *              name;                 /*    24     8 */

        /* size: 32, cachelines: 1, members: 5 */
        /* sum members: 22, holes: 2, sum holes: 10 */
        /* last cacheline: 32 bytes */




struct _phpdbg_breakop_t {
        int                        id;                   /*     0     4 */
        uint8_t                    type;                 /*     4     1 */

        /* XXX 3 bytes hole, try to pack */

        zend_ulong                 hits;                 /*     8     8 */
        _Bool                      disabled;             /*    16     1 */

        /* XXX 7 bytes hole, try to pack */

        const char  *              name;                 /*    24     8 */
        zend_ulong                 hash;                 /*    32     8 */

        /* size: 40, cachelines: 1, members: 6 */
        /* sum members: 30, holes: 2, sum holes: 10 */
        /* last cacheline: 40 bytes */




struct _phpdbg_breakcond_t {
        int                        id;                   /*     0     4 */
        uint8_t                    type;                 /*     4     1 */

        /* XXX 3 bytes hole, try to pack */

        zend_ulong                 hits;                 /*     8     8 */
        _Bool                      disabled;             /*    16     1 */

        /* XXX 7 bytes hole, try to pack */

        const char  *              code;                 /*    24     8 */
        size_t                     code_len;             /*    32     8 */
        _Bool                      paramed;              /*    40     1 */

        /* XXX 7 bytes hole, try to pack */

        phpdbg_param_t             param;                /*    48    88 */
        /* --- cacheline 2 boundary (128 bytes) was 8 bytes ago --- */
        zend_ulong                 hash;                 /*   136     8 */
        zend_op_array *            ops;                  /*   144     8 */

        /* size: 152, cachelines: 3, members: 10 */
        /* sum members: 135, holes: 3, sum holes: 17 */
        /* last cacheline: 24 bytes */




struct _phpdbg_breaksymbol_t {
        int                        id;                   /*     0     4 */
        uint8_t                    type;                 /*     4     1 */

        /* XXX 3 bytes hole, try to pack */

        zend_ulong                 hits;                 /*     8     8 */
        _Bool                      disabled;             /*    16     1 */

        /* XXX 7 bytes hole, try to pack */

        const char  *              symbol;               /*    24     8 */

        /* size: 32, cachelines: 1, members: 5 */
        /* sum members: 22, holes: 2, sum holes: 10 */
        /* last cacheline: 32 bytes */




struct _zend_smm_shared_globals {
        zend_shared_segment * *    shared_segments;      /*     0     8 */
        int                        shared_segments_count; /*     8     4 */

        /* XXX 4 bytes hole, try to pack */

        size_t                     shared_free;          /*    16     8 */
        size_t                     wasted_shared_memory; /*    24     8 */
        _Bool                      memory_exhausted;     /*    32     1 */

        /* XXX 7 bytes hole, try to pack */

        zend_shared_memory_state   shared_memory_state;  /*    40    16 */
        void *                     app_shared_globals;   /*    56     8 */
        /* --- cacheline 1 boundary (64 bytes) --- */
        void *                     reserved;             /*    64     8 */
        size_t                     reserved_size;        /*    72     8 */

        /* size: 80, cachelines: 2, members: 9 */
        /* sum members: 69, holes: 2, sum holes: 11 */
        /* last cacheline: 16 bytes */




struct _phpdbg_command_t {
        const char  *              name;                 /*     0     8 */
        size_t                     name_len;             /*     8     8 */
        const char  *              tip;                  /*    16     8 */
        size_t                     tip_len;              /*    24     8 */
        char                       alias;                /*    32     1 */

        /* XXX 7 bytes hole, try to pack */

        phpdbg_command_handler_t   handler;              /*    40     8 */
        const phpdbg_command_t  *  subs;                 /*    48     8 */
        char *                     args;                 /*    56     8 */
        /* --- cacheline 1 boundary (64 bytes) --- */
        const phpdbg_command_t  *  parent;               /*    64     8 */
        _Bool                      flags;                /*    72     1 */

        /* size: 80, cachelines: 2, members: 10 */
        /* sum members: 66, holes: 1, sum holes: 7 */
        /* padding: 7 */
        /* last cacheline: 16 bytes */




struct lxb_css_syntax_token {
        union lxb_css_syntax_token_u types;              /*     0    80 */
        /* --- cacheline 1 boundary (64 bytes) was 16 bytes ago --- */
        lxb_css_syntax_token_type_t type;                /*    80     4 */

        /* XXX 4 bytes hole, try to pack */

        uintptr_t                  offset;               /*    88     8 */
        _Bool                      cloned;               /*    96     1 */

        /* size: 104, cachelines: 2, members: 4 */
        /* sum members: 93, holes: 1, sum holes: 4 */
        /* padding: 7 */
        /* last cacheline: 40 bytes */




struct _phpdbg_breakopline_t {
        int                        id;                   /*     0     4 */
        uint8_t                    type;                 /*     4     1 */

        /* XXX 3 bytes hole, try to pack */

        zend_ulong                 hits;                 /*     8     8 */
        _Bool                      disabled;             /*    16     1 */

        /* XXX 7 bytes hole, try to pack */

        const char  *              func_name;            /*    24     8 */
        size_t                     func_len;             /*    32     8 */
        const char  *              class_name;           /*    40     8 */
        size_t                     class_len;            /*    48     8 */
        zend_ulong                 opline_num;           /*    56     8 */
        /* --- cacheline 1 boundary (64 bytes) --- */
        zend_ulong                 opline;               /*    64     8 */

        /* size: 72, cachelines: 2, members: 10 */
        /* sum members: 62, holes: 2, sum holes: 10 */
        /* last cacheline: 8 bytes */




struct _phpdbg_breakfile_t {
        int                        id;                   /*     0     4 */
        uint8_t                    type;                 /*     4     1 */

        /* XXX 3 bytes hole, try to pack */

        zend_ulong                 hits;                 /*     8     8 */
        _Bool                      disabled;             /*    16     1 */

        /* XXX 7 bytes hole, try to pack */

        const char  *              filename;             /*    24     8 */
        zend_ulong                 line;                 /*    32     8 */

        /* size: 40, cachelines: 1, members: 6 */
        /* sum members: 30, holes: 2, sum holes: 10 */
        /* last cacheline: 40 bytes */




struct _phpdbg_breakline_t {
        int                        id;                   /*     0     4 */
        uint8_t                    type;                 /*     4     1 */

        /* XXX 3 bytes hole, try to pack */

        zend_ulong                 hits;                 /*     8     8 */
        _Bool                      disabled;             /*    16     1 */

        /* XXX 7 bytes hole, try to pack */

        const char  *              name;                 /*    24     8 */
        zend_ulong                 opline;               /*    32     8 */
        phpdbg_breakopline_t *     base;                 /*    40     8 */

        /* size: 48, cachelines: 1, members: 7 */
        /* sum members: 38, holes: 2, sum holes: 10 */
        /* last cacheline: 48 bytes */




struct _phpdbg_breakmethod_t {
        int                        id;                   /*     0     4 */
        uint8_t                    type;                 /*     4     1 */

        /* XXX 3 bytes hole, try to pack */

        zend_ulong                 hits;                 /*     8     8 */
        _Bool                      disabled;             /*    16     1 */

        /* XXX 7 bytes hole, try to pack */

        const char  *              class_name;           /*    24     8 */
        size_t                     class_len;            /*    32     8 */
        const char  *              func_name;            /*    40     8 */
        size_t                     func_len;             /*    48     8 */

        /* size: 56, cachelines: 1, members: 8 */
        /* sum members: 46, holes: 2, sum holes: 10 */
        /* last cacheline: 56 bytes */




struct _php_netstream_data_t {
        php_socket_t               socket;               /*     0     4 */
        char                       is_blocked;           /*     4     1 */

        /* XXX 3 bytes hole, try to pack */

        struct timeval             timeout;              /*     8    16 */
        char                       timeout_event;        /*    24     1 */

        /* XXX 7 bytes hole, try to pack */

        size_t                     ownsize;              /*    32     8 */

        /* size: 40, cachelines: 1, members: 5 */
        /* sum members: 30, holes: 2, sum holes: 10 */
        /* last cacheline: 40 bytes */

My PR changes:

struct _phpdbg_breakbase_t {
        uint8_t                    type;                 /*     0     1 */
        _Bool                      disabled;             /*     1     1 */

        /* XXX 2 bytes hole, try to pack */

        int                        id;                   /*     4     4 */
        zend_ulong                 hits;                 /*     8     8 */
        const char  *              name;                 /*    16     8 */

        /* size: 24, cachelines: 1, members: 5 */
        /* sum members: 22, holes: 1, sum holes: 2 */
        /* last cacheline: 24 bytes */




struct _phpdbg_breakop_t {
        uint8_t                    type;                 /*     0     1 */
        _Bool                      disabled;             /*     1     1 */

        /* XXX 2 bytes hole, try to pack */

        int                        id;                   /*     4     4 */
        zend_ulong                 hits;                 /*     8     8 */
        const char  *              name;                 /*    16     8 */
        zend_ulong                 hash;                 /*    24     8 */

        /* size: 32, cachelines: 1, members: 6 */
        /* sum members: 30, holes: 1, sum holes: 2 */
        /* last cacheline: 32 bytes */




struct _phpdbg_breakcond_t {
        _Bool                      paramed;              /*     0     1 */
        uint8_t                    type;                 /*     1     1 */
        _Bool                      disabled;             /*     2     1 */

        /* XXX 1 byte hole, try to pack */

        int                        id;                   /*     4     4 */
        zend_ulong                 hits;                 /*     8     8 */
        const char  *              code;                 /*    16     8 */
        size_t                     code_len;             /*    24     8 */
        phpdbg_param_t             param;                /*    32    88 */
        /* --- cacheline 1 boundary (64 bytes) was 56 bytes ago --- */
        zend_ulong                 hash;                 /*   120     8 */
        /* --- cacheline 2 boundary (128 bytes) --- */
        zend_op_array *            ops;                  /*   128     8 */

        /* size: 136, cachelines: 3, members: 10 */
        /* sum members: 135, holes: 1, sum holes: 1 */
        /* last cacheline: 8 bytes */




struct _phpdbg_breaksymbol_t {
        uint8_t                    type;                 /*     0     1 */
        _Bool                      disabled;             /*     1     1 */

        /* XXX 2 bytes hole, try to pack */

        int                        id;                   /*     4     4 */
        zend_ulong                 hits;                 /*     8     8 */
        const char  *              symbol;               /*    16     8 */

        /* size: 24, cachelines: 1, members: 5 */
        /* sum members: 22, holes: 1, sum holes: 2 */
        /* last cacheline: 24 bytes */




struct _zend_smm_shared_globals {
        zend_shared_segment * *    shared_segments;      /*     0     8 */
        int                        shared_segments_count; /*     8     4 */
        _Bool                      memory_exhausted;     /*    12     1 */

        /* XXX 3 bytes hole, try to pack */

        size_t                     shared_free;          /*    16     8 */
        size_t                     wasted_shared_memory; /*    24     8 */
        zend_shared_memory_state   shared_memory_state;  /*    32    16 */
        void *                     app_shared_globals;   /*    48     8 */
        void *                     reserved;             /*    56     8 */
        /* --- cacheline 1 boundary (64 bytes) --- */
        size_t                     reserved_size;        /*    64     8 */

        /* size: 72, cachelines: 2, members: 9 */
        /* sum members: 69, holes: 1, sum holes: 3 */
        /* last cacheline: 8 bytes */




struct _phpdbg_command_t {
        const char  *              name;                 /*     0     8 */
        size_t                     name_len;             /*     8     8 */
        const char  *              tip;                  /*    16     8 */
        size_t                     tip_len;              /*    24     8 */
        char                       alias;                /*    32     1 */
        _Bool                      flags;                /*    33     1 */

        /* XXX 6 bytes hole, try to pack */

        phpdbg_command_handler_t   handler;              /*    40     8 */
        const phpdbg_command_t  *  subs;                 /*    48     8 */
        char *                     args;                 /*    56     8 */
        /* --- cacheline 1 boundary (64 bytes) --- */
        const phpdbg_command_t  *  parent;               /*    64     8 */

        /* size: 72, cachelines: 2, members: 10 */
        /* sum members: 66, holes: 1, sum holes: 6 */
        /* last cacheline: 8 bytes */




struct lxb_css_syntax_token {
        union lxb_css_syntax_token_u types;              /*     0    80 */
        /* --- cacheline 1 boundary (64 bytes) was 16 bytes ago --- */
        lxb_css_syntax_token_type_t type;                /*    80     4 */
        _Bool                      cloned;               /*    84     1 */

        /* XXX 3 bytes hole, try to pack */

        uintptr_t                  offset;               /*    88     8 */

        /* size: 96, cachelines: 2, members: 4 */
        /* sum members: 93, holes: 1, sum holes: 3 */
        /* last cacheline: 32 bytes */




struct _phpdbg_breakopline_t {
        uint8_t                    type;                 /*     0     1 */
        _Bool                      disabled;             /*     1     1 */

        /* XXX 2 bytes hole, try to pack */

        int                        id;                   /*     4     4 */
        zend_ulong                 hits;                 /*     8     8 */
        const char  *              func_name;            /*    16     8 */
        size_t                     func_len;             /*    24     8 */
        const char  *              class_name;           /*    32     8 */
        size_t                     class_len;            /*    40     8 */
        zend_ulong                 opline_num;           /*    48     8 */
        zend_ulong                 opline;               /*    56     8 */

        /* size: 64, cachelines: 1, members: 10 */
        /* sum members: 62, holes: 1, sum holes: 2 */




struct _phpdbg_breakfile_t {
        uint8_t                    type;                 /*     0     1 */
        _Bool                      disabled;             /*     1     1 */

        /* XXX 2 bytes hole, try to pack */

        int                        id;                   /*     4     4 */
        zend_ulong                 hits;                 /*     8     8 */
        const char  *              filename;             /*    16     8 */
        zend_ulong                 line;                 /*    24     8 */

        /* size: 32, cachelines: 1, members: 6 */
        /* sum members: 30, holes: 1, sum holes: 2 */
        /* last cacheline: 32 bytes */




struct _phpdbg_breakline_t {
        uint8_t                    type;                 /*     0     1 */
        _Bool                      disabled;             /*     1     1 */

        /* XXX 2 bytes hole, try to pack */

        int                        id;                   /*     4     4 */
        zend_ulong                 hits;                 /*     8     8 */
        const char  *              name;                 /*    16     8 */
        zend_ulong                 opline;               /*    24     8 */
        phpdbg_breakopline_t *     base;                 /*    32     8 */

        /* size: 40, cachelines: 1, members: 7 */
        /* sum members: 38, holes: 1, sum holes: 2 */
        /* last cacheline: 40 bytes */




struct _phpdbg_breakmethod_t {
        uint8_t                    type;                 /*     0     1 */
        _Bool                      disabled;             /*     1     1 */

        /* XXX 2 bytes hole, try to pack */

        int                        id;                   /*     4     4 */
        zend_ulong                 hits;                 /*     8     8 */
        const char  *              class_name;           /*    16     8 */
        size_t                     class_len;            /*    24     8 */
        const char  *              func_name;            /*    32     8 */
        size_t                     func_len;             /*    40     8 */

        /* size: 48, cachelines: 1, members: 8 */
        /* sum members: 46, holes: 1, sum holes: 2 */
        /* last cacheline: 48 bytes */




struct _php_netstream_data_t {
        php_socket_t               socket;               /*     0     4 */
        char                       is_blocked;           /*     4     1 */
        char                       timeout_event;        /*     5     1 */

        /* XXX 2 bytes hole, try to pack */

        struct timeval             timeout;              /*     8    16 */
        size_t                     ownsize;              /*    24     8 */

        /* size: 32, cachelines: 1, members: 5 */
        /* sum members: 30, holes: 1, sum holes: 2 */
        /* last cacheline: 32 bytes */

Affected structs (decreased sizes):

  • _phpdbg_breakbase_t 32 to 24 bytes
  • lxb_css_property_float_t 40 to 32 bytes
  • _phpdbg_breakop_t 40 to 32 bytes
  • zend_hooked_object_iterator 160 to 152 bytes
  • _phpdbg_breakcond_t 152 to 136 bytes
  • lxb_css_selectors_pseudo_data_func_t 72 to 64 bytes
  • _phpdbg_breaksymbol_t 32 to 24 bytes
  • _zend_smm_shared_globals 80 to 72 bytes
  • lxb_encoding_ctx_2022_jp_t 16 to 12 bytes
  • _phpdbg_command_t 80 to 72 bytes
  • lxb_css_syntax_token 104 to 96 bytes
  • _phpdbg_breakopline_t 72 to 64 bytes
  • cdf_directory_t 136 to 128 bytes
  • _phpdbg_breakfile_t 40 to 32 bytes
  • err_buf 32 to 24 bytes
  • phpdbg_lexer_data 48 to 40 bytes
  • Table 64 to 56 bytes
  • _phpdbg_breakline_t 48 to 40 bytes
  • glob_s_t 144 to 136 bytes
  • _phpdbg_breakmethod_t 56 to 48 bytes
  • _php_netstream_data_t 40 to 32 bytes

@GermanAizek
Copy link
Author

GermanAizek commented May 14, 2025

Also, information to all developers, make it a rule to try to make structures and classes smaller than 64 bytes.
When a structure is larger than 1 cache line, much more processor cycles are spent on it, a large structure can be divided that stores pointers to another structure that is also less than 64 bytes.

Copy link
Member

@dstogov dstogov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

phpdbg seems broken. Tests are failed.

Comment on lines -231 to +232
lxb_css_float_type_t type;
lxb_css_value_length_type_t length;
lxb_css_float_type_t type;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please, don't change the bundled third-party libraries without a big reason.

Comment on lines +360 to -365
int sizearray;
struct Table*metatable;
TValue*array;
Node*node;
Node*lastfree;
GCObject*gclist;
int sizearray;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is also third-party software.

@iluuu1994
Copy link
Member

Hi @GermanAizek. Thank you for your contribution. As @dstogov mentioned, lexbor, libmagic and dynasm should not change. If these need to be adjusted, it should be done upstream first. Adjusting the other structs should be ok, though note sometimes it's more important to place things that are frequently accessed together close together to increase the chances of them landing on the same cache line. But I don't see any issues with the changes you have made.

…heline

affected structs (decreased sizes):
_phpdbg_breakbase_t 32 to 24 bytes
lxb_css_property_float_t 40 to 32 bytes
_phpdbg_breakop_t 40 to 32 bytes
zend_hooked_object_iterator 160 to 152 bytes
_phpdbg_breakcond_t 152 to 136 bytes
lxb_css_selectors_pseudo_data_func_t 72 to 64 bytes
_phpdbg_breaksymbol_t 32 to 24 bytes
_zend_smm_shared_globals 80 to 72 bytes
lxb_encoding_ctx_2022_jp_t 16 to 12 bytes
_phpdbg_command_t 80 to 72 bytes
lxb_css_syntax_token 104 to 96 bytes
_phpdbg_breakopline_t 72 to 64 bytes
cdf_directory_t 136 to 128 bytes
_phpdbg_breakfile_t 40 to 32 bytes
err_buf 32 to 24 bytes
phpdbg_lexer_data 48 to 40 bytes
Table 64 to 56 bytes
_phpdbg_breakline_t 48 to 40 bytes
glob_s_t 144 to 136 bytes
_phpdbg_breakmethod_t 56 to 48 bytes
_php_netstream_data_t 40 to 32 bytes
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants