Skip to content

Micro-OP: reduce UserItem class 72 to 64 bytes using align for 64-bit platforms - #409

Merged
JPeisach merged 1 commit into
ubuntu:mainfrom
GermanAizek:align-64bit
Aug 2, 2026
Merged

Micro-OP: reduce UserItem class 72 to 64 bytes using align for 64-bit platforms#409
JPeisach merged 1 commit into
ubuntu:mainfrom
GermanAizek:align-64bit

Conversation

@GermanAizek

@GermanAizek GermanAizek commented May 20, 2025

Copy link
Copy Markdown
Contributor

@robert-ancell,

/* saved 8 bytes and 1 cacheline! */

The reduction allowed us to fit the cache line into one cache, this is an advantage, it is desirable to have structures and classes of exactly 64 bytes or less in size, now fewer CPU cycles are spent working with this structure.

This PR 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.

Pahole example:

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

About Linux Kernel tool Pahole: https://linux.die.net/man/1/pahole

Master branch

class UserItem {
public:

        class QString             name;                  /*     0     8 */
        class QString             realName;              /*     8     8 */
        class QString             homeDirectory;         /*    16     8 */
        class QString             image;                 /*    24     8 */
        class QString             background;            /*    32     8 */
        class QString             session;               /*    40     8 */
        bool                       isLoggedIn;           /*    48     1 */
        bool                       hasMessages;          /*    49     1 */

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

        quint64                    uid;                  /*    56     8 */
        class QString displayName(const class UserItem  *);

        /* --- cacheline 1 boundary (64 bytes) --- */
        bool                       isLocked;             /*    64     1 */
        void UserItem(class UserItem *, const class UserItem  &);

        void ~UserItem(class UserItem *, int);

        void UserItem(class UserItem *);


        /* size: 72, cachelines: 2, members: 10 */
        /* sum members: 59, holes: 1, sum holes: 6 */
        /* padding: 7 */
        /* last cacheline: 8 bytes */
};

This PR

class UserItem {
public:

        class QString             name;                  /*     0     8 */
        class QString             realName;              /*     8     8 */
        class QString             homeDirectory;         /*    16     8 */
        class QString             image;                 /*    24     8 */
        class QString             background;            /*    32     8 */
        class QString             session;               /*    40     8 */
        bool                       isLoggedIn;           /*    48     1 */
        bool                       hasMessages;          /*    49     1 */
        bool                       isLocked;             /*    50     1 */

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

        quint64                    uid;                  /*    56     8 */

        /* size: 64, cachelines: 1, members: 10 */
        /* sum members: 59, holes: 1, sum holes: 5 */
};   /* saved 8 bytes and 1 cacheline! */

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/

Signed-off-by: Herman Semenov <GermanAizek@yandex.ru>

@JPeisach JPeisach left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Seems fine, I guess? It still leads to the issue of having 3 booleans followed by the 8-byte data type, is the performance that much of an increase when the hole is decreased from 6 bytes to 5?

@JPeisach
JPeisach self-requested a review July 30, 2026 18:31
@JPeisach

Copy link
Copy Markdown
Collaborator

Actually, I think this does nothing.

First, in my opinion, if you're going to use a LLM to send your PR messages, could you in the future proof check them? Because the "This PR" section removes the QString displayName from the structure.

Second, here is how I analyzed this:

class UserItem
{
public:
    QString name;
    QString realName;
    QString homeDirectory;
    QString image;
    QString background;
    QString session;
    // above are all eight bytes, should fit

    bool isLoggedIn; // 1
    bool hasMessages; // 2
    quint64 uid;            // 2+8=10
    QString displayName() const; // 8
    bool isLocked; // 9
};

So then its changed to

class UserItem
{
public:
    QString name;
    QString realName;
    QString homeDirectory;
    QString image;
    QString background;
    QString session;
    // the same
    bool isLoggedIn; // 1
    bool hasMessages; // 2
    bool isLocked; // 3
    quint64 uid; // 3 + 8 = 11
    QString displayName() const; // another 8
};

So where would the padding.. go?

@JPeisach JPeisach left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

@GermanAizek

GermanAizek commented Aug 1, 2026

Copy link
Copy Markdown
Contributor Author

is the performance that much of an increase when the hole is decreased from 6 bytes to 5?

No, the main performance gain here is that the processor cache line is 64 bytes in many processors, a 72-byte structure will take longer to load/unload/move/copy between CPU cache and its (Port 1,2,3,4,5) ALU registers than working with a single cache line.

Video:

https://www.youtube.com/watch?v=dYhvbDULJvc

  • Ports 0,1,5 are for arithmetic and logic operations (ALU).
  • Ports 2,3 are for memory reads.
  • Port 4 is for memory write.

https://easyperf.net/blog/2018/03/21/port-contention

@GermanAizek

GermanAizek commented Aug 1, 2026

Copy link
Copy Markdown
Contributor Author

So where would the padding.. go?

@JPeisach

Method
QString displayName() const; // another 8
not have size 8 bytes, in memory store only data variables structures/classes.

1 variant (2 memory holes waste sum 16 bytes, final size 72 bytes (2 cachelines))

class UserItem
{
public:
    QString name; // 8
    QString realName; // 8
    QString homeDirectory; // 8
    QString image; // 8
    QString background; // 8 
    QString session; // 8
    
    bool isLoggedIn; // 1
    bool hasMessages; // 1
    // invisible compiler padding 6 bytes
    quint64 uid;            // 8
    QString displayName() const;
    bool isLocked; // 1
    // invisible compiler padding 7 bytes
};

2 variant (1 memory hole waste sum 8 bytes, final size 64 bytes (1 cacheline))

class UserItem
{
public:
    QString name; // 8
    QString realName; // 8
    QString homeDirectory; // 8
    QString image; // 8
    QString background; // 8
    QString session; // 8
    
    bool isLoggedIn; // 1
    bool hasMessages; // 1
    bool isLocked; // 1
    // invisible compiler padding 5 bytes
    quint64 uid; // 8 
    QString displayName() const;
};

alternatively, you can make bit fields out of boolean, but bit fields are not always applicable and save memory, but they spend more CPU instructions working with them.

https://stackoverflow.com/questions/24933242/when-to-use-bit-fields-in-c
https://en.wikipedia.org/wiki/Bit_field

3 variant (1 memory hole waste sum 8 bytes, final size 64 bytes (1 cacheline))

class UserItem
{
public:
    QString name; // 8
    QString realName; // 8
    QString homeDirectory; // 8
    QString image; // 8
    QString background; // 8
    QString session; // 8
    
    bool isLoggedIn : 1; // 1 bit
    bool hasMessages : 1; // 1 bit
    bool isLocked : 1; // 1 bit
    // invisible compiler padding 7 bytes
    quint64 uid; // 8 
    QString displayName() const;
};

@JPeisach

JPeisach commented Aug 2, 2026

Copy link
Copy Markdown
Collaborator

Ok, I think I understand

The booleans should be grouped with the other booleans for the sake of organization anyway :)

On modern processors this shouldn't hurt too much.. I guess feel free to continue looking for other spots of optimization

@JPeisach
JPeisach merged commit 066ac12 into ubuntu:main Aug 2, 2026
@JPeisach JPeisach added this to the 1.33.0 milestone Aug 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants