2015-07-19 41 views
1

我有一個串行代碼,我想使用Cilk Plus進行並行化;主循環在不同的數據集上重複調用一個處理函數,所以迭代是相互獨立的,除了使用非線程安全的資源,該資源被封裝到一個類中(例如,nts),由外部提供庫,它需要一個文件名,並做它的I/O。如何在Cilk Plus中組織非線程安全資源池(每個工作者一個資源)?

如果我使用OpenMP,我會創建一個包含儘可能多的資源,因爲我有線程資源池,並訪問這些資源根據線程ID:

std::vector<nts> nts_pool; 
for (std::size_t i{0}; i < omp_get_num_threads(); ++i) 
    nts_pool.push_back(nts{}); 

nts_pool[omp_get_thread_num()].do_stuff(); // from inside the task 

使用的Cilk另外,我可以儘可能多地使用__cilkrts_get_nworkers()__cilkrts_get_worker_number() API,但是從英特爾論壇上的多個帖子中,我認爲這被認爲是錯誤的解決方案,正確的解決方案是使用持有者超級對象。

現在,持有人解決方案確實看起來不錯,除了我真的只想創建像我有工作線程一樣多的視圖。也就是說,對於3個工作線程,我想有3個對象,而不是更多。理由是,正如我所說,資源是由第三方庫提供的,構建起來非常昂貴,而且之後我將不得不處理結果文件,因此越少越好。

不幸的是,我發現不是每個工作人員製作一個視圖並保持它一直到同步,持有者根據我不明白的邏輯創建和銷燬視圖,而且似乎沒有一種影響這種行爲的方式。

是否有可能讓持有人按我想要的方式行事,如果沒有,我的問題將會是一種慣用的Cilk Plus解決方案?

這裏是我用來研究人,請注意,一個跑,被隨機分配和銷燬貌似在我的測試機上50次創造了該程序:

#include <iostream> 
#include <atomic> 

#include <cilk/cilk.h> 
#include <cilk/holder.h> 
#include <cilk/reducer_ostream.h> 
#include <cilk/cilk_api.h> 

cilk::reducer_ostream *hyper_cout; 

class nts { 
public: 
    nts() : tag_{std::to_string(++id_)} { 
     *hyper_cout << "NTS constructor: " << tag_ << std::endl; 
    } 
    ~nts() { 
     *hyper_cout << "NTS destructor: " << tag_ << std::endl; 
    } 
    void print_tag() { 
     *hyper_cout << "NTS tag: " << tag_ << std::endl; 
    } 
    static void is_lock_free() { 
     *hyper_cout << "Atomic is lockfree: " << id_.is_lock_free() << std::endl; 
    } 
private: 
    const std::string tag_; 
    static std::atomic_size_t id_; 
}; 

std::atomic_size_t nts::id_{0}; 

class nts_holder { 
public: 
    void print_tag() { nts_().print_tag(); } 
private: 
    cilk::holder<nts> nts_; 
}; 

int main() { 

    __cilkrts_set_param("nworkers", "4"); 

    cilk::reducer_ostream cout{std::cout}; 
    hyper_cout = &cout; 

    *hyper_cout << "Workers: " << __cilkrts_get_nworkers() << std::endl; 
    nts::is_lock_free(); 

    nts_holder ntsh; 
    ntsh.print_tag(); 

    for (std::size_t i{0}; i < 1000; ++i) { 
     cilk_spawn [&]() { 
      ntsh.print_tag(); 
     }(); 
    } 

    cilk_sync; 

    return 0; 

} 

回答

1

你是正確的對於這個特定的問題,持有者是一種誘人的但低效的解決方案。如果您的程序使用每個工作人員有一個插槽的插槽陣列是正確的,則在這種情況下使用__cilkrts_get_nworkers()__cilkrts_get_worker_number() API確實沒有任何問題。我們確實勸阻他們使用一般;寧願編寫Cilk Plus代碼,而忽略數字工作者,因爲它通常會以這種方式更好地擴展。然而,有些情況下,包括這個,爲每個員工創造一個插槽是最好的策略。

+0

非常感謝您給出明確的答案;在這種情況下,我會去爲我的插槽解決方案陣列!不過,如果您可以額外澄清爲什麼持有人按照他們目前的方式行事,我將不勝感激。這讓我覺得特別不直觀,因爲想到的最簡單的實現是一系列插槽... :-) –

+0

持有者行爲方式的主要原因是歷史性的:持有者建立在減速機構,它保留了關聯操作的要求,即使所討論的操作通常是無操作的。正如您發現的那樣,這通常不是一個有用的屬性,但是它可以用於例如檢測值沒有改變並因此可以在不重新計算的情況下使用。當允許交換時,我們正在考慮針對減速器和持有者的更有效的(即每個員工)機制。 –