2015-05-15 10 views
0

如果有一個pthread_rwlock_t未持有寫鎖,有一個很大的開銷調用pthread_rwlock_rdlock/pthread_rwlock_unlock?開銷pthread_rwlock_rdlock的時候沒有寫鎖舉行

這是我想到的情況。如果還有其他啓發式可能有用,聽到它們會很高興。

你有一個簡單的程序,通過調用輸入的每一項功能轉換數據輸入到輸出結果的列表。一些輸入是相同的,或者至少是相似的,以至於你的函數可以記憶計算。假設你選擇使用散列來記憶這個函數。

隨着程序的進展,散列值增加,命中率接近100%。

下面是使用pthread_rwlock_t的可能解決方案。這樣做的一個缺點是,即使程序接近100%命中率,該函數仍在調用rdlock/unlock。

在某些時候,人們不禁要問,它是否是最好設置一個名爲「hash_frozen」在這一點把它當作常量共享數據標誌,而不是在這一點上添加任何按鍵。這似乎是一個笨重的解決方案。

struct hash h; 
pthread_rwlock_t rwl = ...; 

struct val fcn(struct inp i) 
{ 
    struct hashkey k; 
    struct hashval v; 
    pthread_rwlock_rdlock(rwl); 
    if ((k = hash_find(h, i)) != hash_end(h)) 
    { 
     struct retval v = hash_val(k); 
     pthread_rwlock_unlock(rwl); 
     return v; 
    } 
    else 
    { 
     pthread_rwlock_unlock(rwl); 
     /* i'm aware that another thread could insert the value at 
      this moment, duplicating work, but let's ignore that minor 
      inefficiency. */ 
     pthread_rwlock_wrlock(rwl); 
     struct retval v = compute_value(i); 
     k = hash_put(h, i); 
     hash_val(k) = v; /* let's say this is a macro, as in khash.h */ 
     pthread_rwlock_unlock(rwl); 
     return v; 
    } 
} 

回答

0

這顯然取決於具體的實施。

對於current glibc (NPTL) implementation,即使在rdlock快速路徑中,每個讀取器也必須獲取並釋放保護rwlock數據結構本身的低級鎖。如果在這個低級別的鎖上沒有太多的爭用 - 也就是說。你的線程在讀取鎖定部分內部或外部做了大量的工作 - 那麼這個低級鎖定也將完全在快速路徑中執行,並且開銷只會是由硬件保持包含低級鎖定和__nr_readers計數器在CPU之間同步。

如果有在這種低級別的鎖足夠的競爭 - 因爲你有這麼多讀更衣室執行得非常快,他們的顯著部分結束了在同一時間執行pthead_rwlock_rdlock()pthread_rwlock_unlock() - 他們中的一些會最終睡在那個鎖上,不得不被一個解鎖器喚醒,這會增加很大的開銷。

因此,其實可以歸結爲:你說的4個核,或4000個內核?最終找出最好的方法是實施和分析它。

如果rwlock爭用確實非常重要,那麼對於非調整大小的哈希表,您可以通過擁有多個rwlock來鎖定更細粒度的方式,每個rwlock都覆蓋散列表項的不相交子集。你計算散列時沒有鎖定(因此是'non-resizing'條件),並且使用散列查找在檢查散列條目本身之前鎖定哪個rwlock。

+0

非常感謝@caf。目前我正在使用32個內核,並且實際上已經使用讀鎖實現了這一點。不幸的是,我的應用程序的確大部分時間都處於鎖定爭用狀態。我喜歡你的想法提前計算散列值,只鎖定散列的必要部分。不幸的是,我不認爲這種技術可以用於khash.h的散列,因爲它使用「二次探測」,這意味着一個單一的扁平數組(不是每個散列值下面的鏈表)。沒辦法隔絕桌子的一部分。 –