C++中的共享内存缓冲区,不违反严格的别名规则

rsp*_*984 7 c++ memory buffer strict-aliasing type-punning

我正在努力实现共享内存缓冲区而不破坏C99的严格别名规则.

假设我有一些处理一些数据的代码,需要有一些'临时'内存来运行.我可以把它写成:

void foo(... some arguments here ...) {
  int* scratchMem = new int[1000];   // Allocate.
  // Do stuff...
  delete[] scratchMem;  // Free.
}
Run Code Online (Sandbox Code Playgroud)

然后我有另一个功能,做一些其他需要一个临时缓冲区的东西:

void bar(...arguments...) {
  float* scratchMem = new float[1000];   // Allocate.
  // Do other stuff...
  delete[] scratchMem;  // Free.
}
Run Code Online (Sandbox Code Playgroud)

问题是在操作期间可能会多次调用foo()和bar(),并且在性能和内存碎片方面,整个地方的堆分配可能非常糟糕.一个明显的解决方案是分配一个适当大小的公共共享内存缓冲区,然后将其作为参数传递给foo()和bar(),BYOB样式:

void foo(void* scratchMem);
void bar(void* scratchMem);

int main() {
  const int iAmBigEnough = 5000;
  int* scratchMem = new int[iAmBigEnough];

  foo(scratchMem);
  bar(scratchMem);

  delete[] scratchMem;
  return 0;
}

void foo(void* scratchMem) {
  int* smem = (int*)scratchMem;
  // Dereferencing smem will break strict-aliasing rules!
  // ...
}

void bar(void* scratchMem) {
  float* smem = (float*)scratchMem;
  // Dereferencing smem will break strict-aliasing rules!
  // ...
}
Run Code Online (Sandbox Code Playgroud)


我想我现在有两个问题:
- 如何实现一个不违反别名规则的共享公共暂存内存缓冲区?
- 即使上面的代码确实违反了严格的别名规则,别名也没有"伤害".因此,任何理智的编译器都可以生成(优化的)代码,但仍会让我遇到麻烦吗?

谢谢

Ker*_* SB 1

将对象解释为字节序列始终是有效的(即,将任何对象指针视为指向字符数组的第一个元素的指针,这不是别名冲突),并且您可以在任何字节序列中构造对象足够大且适当对齐的内存。

因此,您可以分配一个大的 s 数组char(任何符号),并找到对齐的偏移量alignof(maxalign_t);现在,一旦您构造了适当的对象(例如,在 C++ 中使用placement-new),您就可以将该指针解释为对象指针。

当然,您必须确保不要写入现有对象的内存;事实上,对象的生命周期与代表该对象的内存发生的情况密切相关。

例子:

char buf[50000];

int main()
{
    uintptr_t n = reinterpret_cast<uintptr_t>(buf);
    uintptr_t e = reinterpret_cast<uintptr_t>(buf + sizeof buf);

    while (n % alignof(maxalign_t) != 0) { ++n; }

    assert(e > n + sizeof(T));

    T * p = :: new (reinterpret_cast<void*>(n)) T(1, false, 'x');

    // ...

    p->~T();
}
Run Code Online (Sandbox Code Playgroud)

malloc请注意,由或获得的内存始终new char[N]对齐为最大对齐(但不能更多,并且您可能希望使用过度对齐的地址)。

  • 如果您使用“float”的存储来创建“int”,则会导致严格的别名违规。事实上,我认为编译器甚至可能被允许延迟对“float”的写入,直到内存被重新用于“int”(破坏“int”的值)之后,因为严格的别名允许编译器假设不同类型的对象不会重叠。由于这个问题特别涉及严格的别名规则,因此我认为这很重要。最好是使用以“char[]”开始的内存。 (2认同)