簡體   English   中英

c ++ openmp with shared_pointer

[英]c++ openmp with shared_pointer

這是困擾我的最小例子

#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改變了指針的“本質”(這是我天真的理解)。 但這種行為是正常的嗎? 我應該期待一些奇怪的行為嗎?

編輯

是一個實時測試,在稍微復雜的代碼版本中顯示相同的問題

這種行為是完全合理的,讓我們看看發生了什么。

串行循環

在每次迭代中,你都會得到一個在堆上創建的A ,一個正在被破壞。 這些操作的順序如下:

  1. 施工
  2. 毀壞
  3. 施工
  4. 毀壞
  5. ... (等等)

由於A是在堆上創建的,因此它們通過內存分配器。 當內存分配器獲得新內存請求時,如步驟3,它將(在許多情況下)首先查看最近釋放的內存。 它看到最后一個操作是一個沒有完全正確大小的內存(步驟2),因此將再次占用該塊內存。 此過程將在每次迭代中重復。 因此,串行循環將(通常但不一定)一遍又一遍地為您提供相同的地址。

並行循環

現在讓我們考慮並行循環。 由於沒有同步,因此不確定存儲器分配和解除分配的順序。 因此,它們可以以您能想象的任何方式交錯。 因此,內存分配器通常不能使用與上次相同的技巧來始終分配同一塊內存。 一個示例排序可能是例如所有四個A都在它們全部被破壞之前構建 - 如下所示:

  1. 施工
  2. 施工
  3. 施工
  4. 施工
  5. 毀壞
  6. 毀壞
  7. 毀壞
  8. 毀壞

因此,內存分配器必須提供 4個全新的內存,然后才能回收並開始回收。

基於堆棧的版本的行為稍微更具確定性,但可能依賴於編譯器優化。 對於串行版本,每次創建/銷毀對象時,都會調整堆棧指針。 由於兩者之間沒有任何事情發生,因此它將繼續在同一位置創建。

對於並行版本,每個線程在共享內存系統中都有自己的堆棧。 因此,每個線程將在不同的內存位置創建它的對象,並且不可能“回收”。

你所看到的行為絕不是奇怪的,或者保證這一點。 它取決於您擁有的物理內核數量,運行的線程數,您使用的迭代次數 - 通常是運行時條件。

一句話 :一切都很好,你不應該讀太多。

我認為這取決於你的環境,它不是驕傲,不應該被視為奇怪的行為。 使用MS VS 2015預覽版,您的代碼為我提供了以下內容(啟用了OMP):

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM