繫線組合
.
POSIX 執行緒
我今年的任務是將最大可能性函數從單一執行緒版本移植到多重執行緒,根據該程式碼最慢部分的架構前提,實際最佳化邏輯在模型化群組 ID 的索引上迴圈,而模型化群組 ID 的每個重複都應該獨立於其餘部分。
迴圈時遇到問題
- 無法重新初始化相依的自動變數,
- 變更數個全域指標,其索引方式為建立群組 ID (
人才招聘), - 需要 thread_local 變數來區隔用於計算的全域記憶體區段 (以每個 ID 為基礎)。
1 和 3 是直覺的。有了 2,我發現了一個與一個概念有關的聰明演算法THREAD_BUNDLE,這是序列模型化群組 ID 的每個繫線區塊。
透過確保THREAD_BUNDLE 至少與記憶體分頁大小除以個別指標的大小為 2,我們可以確保不同的執行緒在獨立運作。頁面 在虛擬記憶體中,因此我們可以將 mutex 鎖定在這些記憶體寫入。(假設執行緒會處理它們的當然) THREAD_BUNDLE 速度大致相同。
此外,我最終將迴圈分割成偶數 / 奇數配對,以移除「相同的速度」相依性,這保證至少有一個記憶體頁面 分離使用中THREAD_BUNDLE 迴圈。
核心迴圈
/* EVEN */
for (jG = minG; jG <= numG; jG += 2*THREAD_BUNDLE)
{
pthread_t ptid; 3 refs
arg_t* arg = mal (sizeof(*arg), "arg_t"); 12 refs
#define SET_ARG(foo) arg->foo = (foo) 30 refs
SET_ARG(jG);
...
#undef SET_ARG
// rsim_mlf_thread loops over the index bundle
// from jG to MIN(numG, jG + THREAD_BUNDLE - minG), doing
// stuff with global data structures also indexed by jG
pthread_create (&ptid, NULL, rsim_mlf_thread, arg);
PUSH_STACK_ELT(thread_stack, ptid, pthread_t);
}
{
pthread_t * ptid; 3 refs
while (POP_STACK_PTR(thread_stack, ptid, pthread_t) != NULL)
{
arg_t* arg; 6 refs
pthread_join(*ptid, (void**)&arg);
...
}
/* ODD */
for (jG = THREAD_BUNDLE + minG; jG <= numG; jG += 2*THREAD_BUNDLE)
{
pthread_t ptid; 3 refs
arg_t* arg = mal (sizeof(*arg), "arg_t"); 12 refs
...
