[英]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
,一個正在被破壞。 這些操作的順序如下:
由於A
是在堆上創建的,因此它們通過內存分配器。 當內存分配器獲得新內存請求時,如步驟3,它將(在許多情況下)首先查看最近釋放的內存。 它看到最后一個操作是一個沒有完全正確大小的內存(步驟2),因此將再次占用該塊內存。 此過程將在每次迭代中重復。 因此,串行循環將(通常但不一定)一遍又一遍地為您提供相同的地址。
現在讓我們考慮並行循環。 由於沒有同步,因此不確定存儲器分配和解除分配的順序。 因此,它們可以以您能想象的任何方式交錯。 因此,內存分配器通常不能使用與上次相同的技巧來始終分配同一塊內存。 一個示例排序可能是例如所有四個A
都在它們全部被破壞之前構建 - 如下所示:
因此,內存分配器必須提供 4個全新的內存,然后才能回收並開始回收。
基於堆棧的版本的行為稍微更具確定性,但可能依賴於編譯器優化。 對於串行版本,每次創建/銷毀對象時,都會調整堆棧指針。 由於兩者之間沒有任何事情發生,因此它將繼續在同一位置創建。
對於並行版本,每個線程在共享內存系統中都有自己的堆棧。 因此,每個線程將在不同的內存位置創建它的對象,並且不可能“回收”。
你所看到的行為絕不是奇怪的,或者保證這一點。 它取決於您擁有的物理內核數量,運行的線程數,您使用的迭代次數 - 通常是運行時條件。
一句話 :一切都很好,你不應該讀太多。
我認為這取決於你的環境,它不是驕傲,不應該被視為奇怪的行為。 使用MS VS 2015預覽版,您的代碼為我提供了以下內容(啟用了OMP):
0082C3DC
0082C41C
0082C49C
0082C45C
0082C41C
0082C41C
0082C41C
0082C41C
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.