代码之家  ›  专栏  ›  技术社区  ›  eladidan

防止堆上未对齐的数据

  •  9
  • eladidan  · 技术社区  · 16 年前

    __declspec(align(#)) ,就像这样:

    typedef __declspec(align(16)) float Vector[4];
    class MyClass{
    ...
    private:
    Vector v;
    };
    

    __declspec(对齐(#))

    MyClass *myclass = new MyClass;
    

    这也是,我知道我可以很容易地解决超载的问题 删除 要使用的运算符 _aligned_malloc _aligned_free 相应地。像这样:

    //inside MyClass:
    public:
    void* operator new (size_t size) throw (std::bad_alloc){
        void * p = _aligned_malloc(size, 16);
        if (p == 0)  throw std::bad_alloc()
        return p; 
    }
    
    void operator delete (void *p){
        MyClass* pc = static_cast<MyClass*>(p); 
        _aligned_free(p);
    }
    ...
    

    class NotMyClass{ //Not my code, which I have little or no influence over
    ...
    MyClass myclass;
    ...
    };
    int main(){
        ...
        NotMyClass *nmc = new NotMyClass;
        ...
    }
    

    自从的myclass实例 MyClass 是在NotMyClass的动态实例上静态创建的,myclass将与nmc的开头相对对齐16字节,因为Vector __declspec(align(16)) 指令。但这是毫无价值的,因为nmc是用NotMyClass在堆上动态分配的 运算符,它不一定能确保(而且肯定不能)16字节对齐。

    到目前为止,我只能想到两种方法来解决这个问题:

    1. MyClass myclass;
      

      也就是说,只能使用new操作符动态地创建MyClass的实例,从而确保MyClass的所有实例都是用MyClass的重载new动态分配的。关于如何实现这一点,我咨询了另一个问题,得到了一些很好的答案: C++, preventing class instance from being created on the stack (during compiltaion)

    2. 从我的类中有Vector成员恢复,并且只有指向Vector的指针作为成员,我将使用 _对齐\u malloc _对齐\u自由

    然而,由于这两种解决方案在我的团队中都不受欢迎,我来向您寻求不同解决方案的建议。

    3 回复  |  直到 9 年前
        1
  •  4
  •   Community Mohan Dere    9 年前

    如果您反对堆分配,另一个想法是在堆栈上过度分配并手动对齐(手动对齐在中讨论) this SO post ). 其思想是分配字节数据( unsigned char +15 ),然后从最移位的区域向下舍入以找到对齐的位置( x+15 - (x+15) % 16 x+15 & ~0x0F ). 我在上面贴了一个向量运算的工作示例 codepad (用于 g++ -O2 -msse2

    class MyClass{
       ...
       unsigned char dPtr[sizeof(float)*4+15]; //over-allocated data
       float* vPtr;                            //float ptr to be aligned
    
       public:
          MyClass(void) : 
             vPtr( reinterpret_cast<float*>( 
                (reinterpret_cast<uintptr_t>(dPtr)+15) & ~ 0x0F
             ) ) 
          {}
       ...
    };
    ...
    

    构造函数确保vPtr对齐(注意类声明中成员的顺序很重要)。

    这种方法是有效的(包含类的堆/堆栈分配与对齐无关),是可移植的(我认为大多数编译器提供了指针大小的uint) uintptr_t

    对齐操作/未对齐数据问题的风险可以通过将此逻辑封装在向量对象中来消除,从而控制对对齐指针的访问,并确保它在构造时对齐并保持有效。

        2
  •  1
  •   janm    16 年前

    void* operator new(size_t, void* p) { return p; }
    
    int main() {
        void* p = aligned_alloc(sizeof(NotMyClass));
        NotMyClass* nmc = new (p) NotMyClass;
        // ...
    
        nmc->~NotMyClass();
        aligned_free(p);
    }
    

    当然,在销毁对象时需要小心,方法是调用析构函数,然后释放空间。你不能只叫delete。您可以使用共享的\u ptr<&燃气轮机;具有不同的自动处理功能;这取决于处理共享的\u ptr(或指针的其他包装器)的开销是否是您的问题。

        3
  •  0
  •   Matthieu M.    16 年前

    即将到来的C++ 0x标准提出了处理原始内存的工具。它们已经包含在VC++2010中(在 tr1

    std::tr1::alignment_of // get the alignment
    std::tr1::aligned_storage // get aligned storage of required dimension
    

    这些是类型,您可以这样使用它们:

    static const floatalign = std::tr1::alignment_of<float>::value; // demo only
    
    typedef std::tr1::aligned_storage<sizeof(float)*4, 16>::type raw_vector;
            // first parameter is size, second is desired alignment
    

    class MyClass
    {
    public:
    
    private:
      raw_vector mVector; // alignment guaranteed
    };
    

    最后,您需要一些cast来操作它(到目前为止它是原始内存):

    float* MyClass::AccessVector()
    {
      return reinterpret_cast<float*>((void*)&mVector));
    }