【问题标题】:Is there a better object memory alignment for my class?我的班级是否有更好的对象内存对齐方式?
【发布时间】:2021-12-18 11:41:16
【问题描述】:

我有以下类,它的对齐方式是 8,大小是 40 字节(使用 gcc v11.2 64 位编译)。

class Foo
{
public:
    inline Foo( ) = default;
    /*
       other member functions such as
       move ctor and move assignment operator etc.
    */
    

private:
    mutable std::vector< std::vector<char> > _characterMatrix; // occupies 24 bytes on the stack
    int _Y_AxisLen; // 4 bytes
    int _X_AxisLen; // 4 bytes
    char _fillCharacter; // 1 byte

    inline static const std::unordered_set<char> CHAR_SET { '/', '\\', '|', '-' }; // static member so nothing
};

还有这句话:

std::cout << "Size: " << sizeof( Foo) << " --- alignment: " << alignof( Foo) << '\n';

生成: Size: 40 --- alignment: 8

类的大小实际上是8(向量obj的大小成员)+ 8(向量obj的容量成员)+ 8(指向向量obj堆上内存块的qword指针)+ 4(int)+ 4 (int) + 1(char)。所以 8+8+8+4+4+1 == 33 字节。每次初始化 Foo 的实例时都会浪费 7 个字节。

所以我的问题是,有没有办法将尺寸减小到 36

我也试过这个:

class alignas( 4 ) Foo
{
public:
    inline Foo( ) = default;
    /*
       other member functions such as
       move ctor and move assignment operator etc.
    */
    

private:
    mutable std::vector< std::vector<char> > _characterMatrix;
    int _Y_AxisLen;
    int _X_AxisLen;
    char _fillCharacter;

    inline static const std::unordered_set<char> CHAR_SET { '/', '\\', '|', '-' };
};

但是编译器忽略了 alignas(4)。大小还是 40。

编辑:在阅读了一些 cmets 之后,我注意到在许多 STL 实现中,std::vector 对象没有指针、大小和容量成员,而是有 3 个指针(每个指针大小为 8 个字节所以总共有 24 个)。

【问题讨论】:

  • 在一个完全不相关的注释上:另见What are the rules about using an underscore in a C++ identifier? 以下划线开头的符号后跟一个大写字母(例如_Y_AxisLen)在所有范围和情况下都是保留的。
  • AFAIK - 向量是 3 个指针(begin、end、end_of_capacity) - 但仍然 - 它的大小与您编写的相同 (3*p == p + s + c)
  • @PiotrNycz 标准库可以将其实现为 3 个指针或 1 个指针和 2 个大小(或完全其他的东西),标准并未仅规定实现所需的行为
  • 您担心节省 4 个字节并且您表示一个带有向量向量的矩阵。这两个不属于同一个房间。

标签: c++ class gcc compiler-optimization memory-alignment


【解决方案1】:

您可以使用位域来精确控制您的成员有多大。

class Foo {
public:
    Foo() = default;
    ~Foo() = default;

    Foo(const Foo & other)
    : matrix(std::make_unique_for_overwrite<char[]>(other.size())), 
      Y_AxisLen(other.Y_AxisLen),
      X_AxisLen(other.X_AxisLen),
      FillCharacter(other.FillCharacter) {
        std::copy(other.matrix.get(), other.matrix.get() + size(), matrix.get());
    }
    Foo(Foo && other) = default;

    Foo& operator=(Foo other) {
        swap(matrix, other.matrix);
        Y_AxisLen = other.Y_AxisLen;
        X_AxisLen = other.X_AxisLen;
        FillCharacter = other.FillCharacter;
        return *this;
    }

private:
    static_assert(CHAR_BIT == 8, "Bit fields assume 8 bit char");

    std::size_t size() const { return Y_AxisLen * X_AxisLen; }

    // support [x][y] access
    char* operator[](size_t col) const { return matrix.get() + (col * Y_AxisLen); }

    std::unique_ptr<char[]> matrix;
    std::uint64_t Y_AxisLen : 28;
    std::uint64_t X_AxisLen : 28;
    std::uint64_t FillCharacter : 8;

    inline static const std::unordered_set<char> CHAR_SET { '/', '\\', '|', '-' };
};

作为奖励,这有一个size of 16(但仍然是 8 对齐)

【讨论】:

  • 如果矩阵不需要初始化为零,可以使用std::make_unique for overwrite
  • @Den-Jason 确实
  • @digito_evo 不要忘记接受这个作为答案...
【解决方案2】:
class Foo
    {
    public:
        inline Foo( ) = default;
        
    
    private:
        char **  _characterMatrix;  // 8 bytes
        int _Y_AxisLen; // 4 bytes
        int _X_AxisLen; // 4 bytes
        char _fillCharacter; // 1 byte
        intline static const std::unordered_set<char> CHAR_SET { '/', '\\', '|', '-' };  
    };

【讨论】:

  • 您忘记定义复制构造函数、移动构造函数、复制赋值运算符、移动赋值运算符和析构函数。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-10-27
  • 2012-06-23
  • 2015-05-01
  • 1970-01-01
  • 2012-06-25
  • 1970-01-01
  • 2018-02-26
相关资源
最近更新 更多