Pin*_*oyd 5 c++ pointers openmp
这是困扰我的最小例子
#include <iostream>
#include <memory>
#include"omp.h"
class A{
public:
A(){std::cout<<this<<std::endl;}
};
int main(){
#pragma omp parallel for
for(unsigned int i=0;i<4;i++){
std::shared_ptr<A> sim(std::make_shared<A>());
}
for(unsigned int i=0;i<4;i++){
std::shared_ptr<A> sim(std::make_shared<A>());
}
}
Run Code Online (Sandbox Code Playgroud)
如果我运行该代码几次,我可能得到这样的结果:
0xea3308
0xea32d8
0xea3338
0x7f39f80008c8
0xea3338
0xea3338
0xea3338
0xea3338
Run Code Online (Sandbox Code Playgroud)
我意识到最后4个输出总是具有相同的字符数(8).但由于某种原因,它发生(并非总是)四个第一个输出中的一个或多个包含更多(14)个字符.看起来使用openmp改变了指针的"本质"(这是我天真的理解).但这种行为是正常的吗?我应该期待一些奇怪的行为吗?
这是一个实时测试,在稍微复杂的代码版本中显示相同的问题
这种行为是完全合理的,让我们看看发生了什么。
在每次迭代中,您都会得到一个A在堆上创建的对象,以及一个正在被销毁的对象。这些操作的顺序如下:
由于As 是在堆上创建的,因此它们会通过内存分配器。当内存分配器收到步骤 3 中的新内存请求时,它将(在许多情况下)首先查看最近释放的内存。它发现最后一个操作是一块没有完全正确大小的内存(步骤 2),因此将再次占用该内存块。此过程将在每次迭代中重复。因此,串行循环将(通常但不一定)一遍又一遍地为您提供相同的地址。
现在让我们考虑一下并行循环。由于没有同步,因此内存分配和释放的顺序不确定。因此,它们可以以您能想象到的任何方式交错。因此,内存分配器通常无法使用与上次相同的技巧来始终分配同一块内存。一个示例排序可能是,所有四个As 在全部被销毁之前被构造 - 如下所示:
因此,内存分配器必须先提供4 块全新的内存,然后才能取回一些内存并开始回收。
基于堆栈的版本的行为更具确定性,但可能取决于编译器优化。对于串行版本,每次创建/销毁对象时都会调整堆栈指针。由于中间没有发生任何事情,因此它将继续在同一位置创建。
对于并行版本,每个线程在共享内存系统中都有自己的堆栈。因此,每个线程将在不同的内存位置创建其对象,并且不可能“回收”。
您所看到的行为一点也不奇怪,也不能保证。这取决于您拥有的物理核心数量、运行的线程数量、使用的迭代次数 - 通常是运行时条件。
底线:一切都很好,你不应该读太多。