我已經在C中實現了一個工作隊列模式(在一個python擴展中),我對性能感到失望。python c擴展:多線程和隨機數
我有一個粒子列表(「元素」)的模擬,我基準執行時間步所需的所有計算所需的時間,並將其與所涉及的粒子數一起記錄下來。我正在四核心超線程i7上運行代碼,所以我期望性能會上升(花費的時間),線程數可達8個,但相反,最快的實現沒有工作線程(功能簡單執行,而不是添加到隊列中),並且每個工作線程的代碼變得越來越慢(對於每個新線程來說,執行時間超過了無線執行的時間!)我已經快速瀏覽了我的處理器使用情況應用程序,並且看起來python永遠不會超過130%的CPU使用率,無論有多少線程正在運行。該機器具有足夠的空間,整體系統使用率約爲200%。
現在我的隊列執行(如下圖所示)的一部分在從隊列中隨機是選擇一個項目,因爲每個工作項目的執行需要兩個元素鎖和相似的元素將接近對方在隊列中。因此,我希望線程選擇隨機索引並攻擊隊列的不同位以最小化互斥衝突。
現在,我讀過,我與rand()
初步嘗試將一直緩慢,因爲我的隨機數字是不是線程安全的(沒有這句話有道理嗎?不知道......)
我試着在random()
和drand48_r
(儘管,不幸的是,後者似乎在OS X上不可用)的實現都與統計無關。
也許別人可以告訴我什麼可能是問題的原因?代碼(工作者函數)在下面,如果你認爲任何queue_add函數或構造函數也可能對你有用,那麼請大聲呼喊。
void* worker_thread_function(void* untyped_queue) {
queue_t* queue = (queue_t*)untyped_queue;
int success = 0;
int rand_id;
long int temp;
work_item_t* work_to_do = NULL;
int work_items_completed = 0;
while (1) {
if (pthread_mutex_lock(queue->mutex)) {
// error case, try again:
continue;
}
while (!success) {
if (queue->queue->count == 0) {
pthread_mutex_unlock(queue->mutex);
break;
}
// choose a random item from the work queue, in order to avoid clashing element mutexes.
rand_id = random() % queue->queue->count;
if (!pthread_mutex_trylock(((work_item_t*)queue->queue->items[rand_id])->mutex)) {
// obtain mutex locks on both elements for the work item.
work_to_do = (work_item_t*)queue->queue->items[rand_id];
if (!pthread_mutex_trylock(((element_t*)work_to_do->element_1)->mutex)){
if (!pthread_mutex_trylock(((element_t*)work_to_do->element_2)->mutex)) {
success = 1;
} else {
// only locked element_1 and work item:
pthread_mutex_unlock(((element_t*)work_to_do->element_1)->mutex);
pthread_mutex_unlock(work_to_do->mutex);
work_to_do = NULL;
}
} else {
// couldn't lock element_1, didn't even try 2:
pthread_mutex_unlock(work_to_do->mutex);
work_to_do = NULL;
}
}
}
if (work_to_do == NULL) {
if (queue->queue->count == 0 && queue->exit_flag) {
break;
} else {
continue;
}
}
queue_remove_work_item(queue, rand_id, NULL, 1);
pthread_mutex_unlock(work_to_do->mutex);
pthread_mutex_unlock(queue->mutex);
// At this point, we have mutex locks for the two elements in question, and a
// work item no longer visible to any other threads. we have also unlocked the main
// shared queue, and are free to perform the work on the elements.
execute_function(
work_to_do->interaction_function,
(element_t*)work_to_do->element_1,
(element_t*)work_to_do->element_2,
(simulation_parameters_t*)work_to_do->params
);
// now finished, we should unlock both the elements:
pthread_mutex_unlock(((element_t*)work_to_do->element_1)->mutex);
pthread_mutex_unlock(((element_t*)work_to_do->element_2)->mutex);
// and release the work_item RAM:
work_item_destroy((void*)work_to_do);
work_to_do = NULL;
work_items_completed++;
success = 0;
}
return NULL;
}
粒子的數量是從N = 100到100,000的任何值,並且您需要至少執行該數目計算,可能更像10N。對於大數量,時間步長可能需要一秒鐘,並且當你需要運行其中的50,000時間才能達到平衡... :)但是,謝謝,循環賽可能只是爲了最大限度地減少鎖定而不需要大量的隨機計算。 – tehwalrus
你可以做一些分析,但是一般來說像rand()這樣的僞隨機數生成器(對於它來說足夠好)實際上並不是非常計算密集型的。它們可以通過幾次乘法實現,並增加結果溢出或使用反饋移位寄存器。密碼隨機生成器可以是重量級的,但你絕對不需要這樣做。 – TJD
的確如此,但循環法也減少了對其他鎖定行爲的需求,並且在任何時候都可以使除一個線程外的所有線程都不受妨礙地進入工作隊列。通過實現它的一半方法,但是當多於一個線程時會讓段錯誤變得令人沮喪:/所以很快就會知道! :) – tehwalrus