简体   繁体   English

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

[英]Shared memory buffers in C++ without violating strict aliasing rules

I am struggling with implementing a shared memory buffer without breaking C99's strict aliasing rules. 我正在努力实现共享内存缓冲区而不破坏C99的严格别名规则。

Suppose I have some code that processes some data and needs to have some 'scratch' memory to operate. 假设我有一些处理一些数据的代码,需要有一些“临时”内存来运行。 I could write it as something like: 我可以把它写成:

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

Then I have another function that does some other stuff that also needs a scratch buffer: 然后我有另一个功能,做一些其他需要一个临时缓冲区的东西:

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

The problem is that foo() and bar() may be called many times during operation and having heap allocations all over the place may be quite bad in terms of performance and memory fragmentation. 问题是在操作期间可能会多次调用foo()和bar(),并且就性能和内存碎片而言,遍布整个地方的堆分配可能非常糟糕。 An obvious solution would be to allocate a common, shared memory buffer of proper size once and then pass it into foo() and bar() as an argument, BYOB-style: 一个明显的解决方案是分配一个适当大小的公共共享内存缓冲区,然后将其作为参数传递给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!
  // ...
}


I guess I have two questions now: 我想我现在有两个问题:
- How can I implement a shared common scratch memory buffer that is not in violation of aliasing rules? - 如何实现不违反别名规则的共享公共暂存内存缓冲区?
- Even though the above code does violate strict aliasing rules, there is no 'harm' being done with the alias. - 即使上面的代码确实违反了严格的别名规则,别名也没有“伤害”。 Therefore could any sane compiler generate (optimized) code that still gets me into trouble? 因此,任何理智的编译器都可以生成(优化的)代码,但仍会让我遇到麻烦吗?

Thanks 谢谢

Actually, what you have written is not a strict aliasing violation. 实际上,你所写的并不是严格的别名违规。

C++11 spec 3.10.10 says: C ++ 11规范3.10.10说:

If a program attempts to access the stored value of an object through a glvalue of other than one of the following types the behavior is undefined 如果程序试图通过以下类型之一以外的glvalue访问对象的存储值,则行为未定义

So the thing that causes the undefined behavior is accessing the stored value, not just creating a pointer to it. 因此导致未定义行为的是访问存储的值,而不仅仅是创建指向它的指针。 Your example does not violate anything. 你的例子没有违反任何规定。 It would need to do the next step: float badValue = smem[0]. 它需要做下一步:浮动badValue = smem [0]。 smem[0] gets the stored value from the shared buffer, creating an aliasing violation. smem [0]从共享缓冲区中获取存储的值,从而产生别名冲突。

Of course, you aren't about to just grab smem[0] before setting it. 当然,您不必在设置之前抓住smem [0]。 You are going to write to it first. 你要先写信给它。 Assigning to the same memory does not access the stored value, so no ailiasing However, it IS illegal to write over the top of an object while it is still alive. 分配给相同的内存不会访问存储的值,因此没有任何关联但是,在对象仍处于活动状态时写入对象的顶部是非法的。 To prove that we are safe, we need object lifespans from 3.8.4: 为了证明我们是安全的,我们需要3.8.4的对象寿命:

A program may end the lifetime of any object by reusing the storage which the object occupies or by explicitly calling the destructor for an object of a class type with a non-trivial destructor. 程序可以通过重用对象占用的存储来结束任何对象的生命周期,或者通过使用非平凡的析构函数显式调用类类型的对象的析构函数来结束任何对象的生命周期。 For an object of a class type with a non-trivial destructor, the program is not required to call the destructor explicitly before the storage which the object occupies is reused or released; 对于具有非平凡析构函数的类类型的对象,程序不需要在重用或释放对象占用的存储之前显式调用析构函数; ... [continues on regarding consequences of not calling destructors] ...... [关于不调用析构函数的后果继续]

You have a POD type, so trivial destructor, so you can simply declare verbally "the int objects are all at the end of their lifespan, I'm using the space for floats." 你有一个POD类型,所以简单的析构函数,所以你可以简单地声明“int对象都在他们的生命周期结束时,我正在使用浮动空间。” You then reuse the space for floats, and no aliasing violation occurs. 然后,您可以将该空间重用于浮点数,并且不会发生别名冲突。

It is always valid to interpret an object as a sequence of bytes (ie it is not an aliasing violation to treat any object pointer as the pointer to the first element of an array of chars), and you can construct an object in any piece of memory that's large enough and suitably aligned. 将对象解释为字节序列总是有效的(即,将任何对象指针视为指向字符数组的第一个元素的指针不是别名冲突),并且可以在任何一个字符串中构造一个对象。记忆力足够大且适当对齐。

So, you can allocate a large array of char s (any signedness), and locate an offset that's aliged at alignof(maxalign_t) ; 因此,您可以分配大量的char (任何签名),并找到在alignof(maxalign_t)alignof(maxalign_t)的偏移量; now you can interpret that pointer as an object pointer once you've constructed the appropriate object there (eg using placement-new in C++). 现在,一旦你在那里构造了适当的对象,就可以将该指针解释为对象指针(例如,在C ++中使用placement-new)。

You do of course have to make sure not to write into an existing object's memory; 你当然必须确保不要写入现有对象的内存; in fact, object lifetime is intimately tied to what happens to the memory which represents the object. 实际上,对象生命周期与代表对象的内存发生的情况密切相关。

Example: 例:

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();
}

Note that memory obtained by malloc or new char[N] is always aligned for maximal alignment (but not more, and you may wish to use over-aligned addresses). 请注意, mallocnew char[N]获取的malloc 始终对齐以进行最大对齐(但不是更多,您可能希望使用过度对齐的地址)。

If a union is used to hold the int and float variables, then you can by pass the strict aliasing. 如果使用union来保存int和float变量,那么可以通过严格别名来传递。 More about this is given in http://cellperformance.beyond3d.com/articles/2006/06/understanding-strict-aliasing.html 有关这方面的更多信息,请参见http://cellperformance.beyond3d.com/articles/2006/06/understanding-strict-aliasing.html

Also see the following article. 另请参阅以下文章。

http://blog.regehr.org/archives/959 http://blog.regehr.org/archives/959

He gives a way to use unions to do this. 他提供了一种使用工会来做到这一点的方法。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM