如何创建一个不破坏严格别名的uint8_t数组?

时间:2014-10-10 10:42:40

标签: c++ pointers c++11 strict-aliasing

我最近问过这个问题:

Using this pointer causes strange deoptimization in hot loop

问题是我正在写一个类型为uint8_t的数组,并且编译器将其视为可以使用方法的this指针(类型为struct T*)进行别名处理,因为void*char*(= uint8_t*)始终可以在C ++中对任何其他指针进行别名。此行为导致错过优化机会。当然,我想避免这种情况。所以问题是:我可以声明一个强制执行严格别名的uint8_t数组,即编译器将其视为从不与另一种类型的指针混淆吗?即,我正在寻找具有特殊别名行为的strict_uint8_t类型的uint8_t类型。有没有办法实现这个目标?

显示我的意思的示例代码,借用其他问题并简化。有关详细信息,请阅读链接的问题及其接受的答案:

struct T{
   uint8_t* target;
   void unpack3bit(char* source, int size) {
        while(size > 0){
           uint64_t t = *reinterpret_cast<uint64_t*>(source);
           /** `this->target` cannot be cached in a register here but has
               to be reloaded 16 times because the compiler
               thinks that `this->target` could alias with `this` itself.
               What I want is a special uint8_t type that does not trigger
               this behaviour. */
           this->target[0] = t & 0x7; 
           this->target[1] = (t >> 3) & 0x7;
           this->target[2] = (t >> 6) & 0x7;
           this->target[3] = (t >> 9) & 0x7;
           this->target[4] = (t >> 12) & 0x7;
           this->target[5] = (t >> 15) & 0x7;
           this->target[6] = (t >> 18) & 0x7;
           this->target[7] = (t >> 21) & 0x7;
           this->target[8] = (t >> 24) & 0x7;
           this->target[9] = (t >> 27) & 0x7;
           this->target[10] = (t >> 30) & 0x7;
           this->target[11] = (t >> 33) & 0x7;
           this->target[12] = (t >> 36) & 0x7;
           this->target[13] = (t >> 39) & 0x7;
           this->target[14] = (t >> 42) & 0x7;
           this->target[15] = (t >> 45) & 0x7;
           source+=6;
           size-=6;
           target+=16;
        }
}
};

3 个答案:

答案 0 :(得分:5)

您可以使用基本类型为uint8_t的固定大小的枚举:

enum strict_uint8_t : uint8_t {};

如果您希望能够透明地转换为uint8_t和从struct进行转换,可以使用转换构造函数和转换运算符将其包装在struct strict_uint8_t { enum : uint8_t {} i; strict_uint8_t(uint8_t i) : i{i} {} operator uint8_t() const { return i; } }; 中:

{{1}}

这似乎消除了gcc和clang中的别名悲观化:https://godbolt.org/g/9Ta98b

(注意:之前的方法,使用位域,在gcc中工作但在clang中不起作用。)

答案 1 :(得分:0)

在visual studio中,您可以使用__declspec(restict)表示函数,使用__restrict表示变量,告诉编译器指针是别名空闲的。我相信在像GCC这样的其他编译器中有一个__restrict__属性(但我不确定)。有关详细信息,请参阅here

答案 2 :(得分:0)

如果你通过一个用restrict声明指针的函数传递两个指针,我相信你会摆脱别名。这是非标准的编译器扩展,例如,在g ++的情况下:

#include <cstdint>
#include <climits>

struct T{
   uint8_t* target;
  private:
    void unpack3bit(char*__restrict__ source, int size, uint8_t*__restrict__ dst) {
        while(size > 0){
           uint64_t t = *source;
           dst[0] = t & 0x7; 
           dst[1] = (t >> 3) & 0x7;
           dst[2] = (t >> 6) & 0x7;
           dst[3] = (t >> 9) & 0x7;
           dst[4] = (t >> 12) & 0x7;
           dst[5] = (t >> 15) & 0x7;
           dst[6] = (t >> 18) & 0x7;
           dst[7] = (t >> 21) & 0x7;
           dst[8] = (t >> 24) & 0x7;
           dst[9] = (t >> 27) & 0x7;
           dst[10] = (t >> 30) & 0x7;
           dst[11] = (t >> 33) & 0x7;
           dst[12] = (t >> 36) & 0x7;
           dst[13] = (t >> 39) & 0x7;
           dst[14] = (t >> 42) & 0x7;
           dst[15] = (t >> 45) & 0x7;
           source+=6;
           size-=6;
           target+=16;
        }
    }
public:
   void unpack3bit(char* source, int size) {
       unpack3bit(source,size,this->target);
   }

};

void f(int i, T& t, char* source) {
  t.unpack3bit(source, i); 
}

在线:http://goo.gl/SCjpL6