c ++ openmp with shared_pointer

时间:2015-04-21 11:43:13

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

如果我运行该代码几次,我可能会得到这样的结果:

0xea3308
0xea32d8
0xea3338
0x7f39f80008c8
0xea3338
0xea3338
0xea3338
0xea3338

我意识到最后4个输出的数量始终相同 人物(8)。但由于某种原因,它发生(并非总是)一个或多个 四个第一个输出包含更多(14)个字符。它看起来像是使用 openmp改变了&#34; nature&#34;指针(这是我天真的理解)。 但这种行为是正常的吗?我应该期待一些奇怪的行为吗?

修改

here是一个实时测试,在稍微复杂的代码版本中显示相同的问题

2 个答案:

答案 0 :(得分:3)

这种行为是完全合理的,让我们看看发生了什么。

串行循环

在每次迭代中,您都会在堆上创建一个A,而其中一个正在被销毁。这些操作的顺序如下:

  1. 构造
  2. 破坏
  3. 构造
  4. 破坏
  5. ...(依此类推)
  6. 由于正在堆上创建A,因此它们将通过内存分配器。当内存分配器获得新内存请求时,如步骤3,它将(在许多情况下)首先查看最近释放的内存。它看到最后一个操作是一个没有完全正确大小的内存(步骤2),因此将再次占用该块内存。此过程将在每次迭代中重复。因此,串行循环将(通常但不一定)一遍又一遍地为您提供相同的地址。

    并行循环

    现在让我们考虑并行循环。由于没有同步,因此不确定存储器分配和解除分配的顺序。因此,它们可以以您能想象的任何方式交错。因此,内存分配器通常不能使用与上次相同的技巧来始终分配同一块内存。一个示例排序可能是例如所有四个A在它们全部被销毁之前构建 - 如下所示:

    1. 构造
    2. 构造
    3. 构造
    4. 构造
    5. 破坏
    6. 破坏
    7. 破坏
    8. 破坏
    9. 因此,内存分配器必须提供 4个全新的内存,然后才能恢复并开始回收。

      基于堆栈的版本的行为稍微更具确定性,但可能依赖于编译器优化。对于串行版本,每次创建/销毁对象时,都会调整堆栈指针。由于两者之间没有任何事情发生,因此它将继续在同一位置创建。

      对于并行版本,每个线程在共享内存系统中都有自己的堆栈。因此,每个线程都会在不同的内存位置创建它的对象,并且没有&#34;回收&#34;是可能的。

      您所看到的行为绝不是奇怪的,或者保证这一点。它取决于您拥有的物理内核数量,运行的线程数,您使用的迭代次数 - 通常是运行时条件。

      底线:一切都很好,你不应该读太多。

答案 1 :(得分:1)

我认为这取决于您的环境,这不是一种吝啬,不应被视为奇怪的行为。 使用MS VS 2015预览版,您的代码为我提供了以下内容(启用了OMP):

0082C3DC
0082C41C   
0082C49C                                       
0082C45C                                       
0082C41C                                       
0082C41C                                       
0082C41C                   
0082C41C