Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

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

Closed
GermanAizek wants to merge 1 commit into php:master from GermanAizek:align-64bit

Conversation

Copy link

@GermanAizek GermanAizek commented May 14, 2025
edited
Loading

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

Copy link
Author

GermanAizek commented May 14, 2025
edited
Loading

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.

otobio reacted with thumbs up emoji

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.

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.

otobio reacted with thumbs up emoji

...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
Copy link
Member

krakjoe commented Aug 30, 2025

I'm going to close this: As mentioned, a lot of these changes should target upstream, and those that might be acceptable in php-src need to be separate and passing tests. Take my closing it as encouragement to continue the work - if you manage to get upstream changes merged, please open new individual pulls for each third party.

Thanks for your contribution.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Reviewers

@dstogov dstogov dstogov requested changes

@bukka bukka Awaiting requested review from bukka bukka is a code owner

@nielsdos nielsdos Awaiting requested review from nielsdos nielsdos is a code owner

Assignees
No one assigned
Projects
None yet
Milestone
No milestone
Development

Successfully merging this pull request may close these issues.

AltStyle によって変換されたページ (->オリジナル) /