Linux 下 openMP 效率並未提升的解決方案

OpenMP 正確觀察計算時間

在使用 openmp的過程中, 使用了簡單的 #pragma omp parallel 但是觀察計算時間並未得到優化

// 使用 openmp 優化的代碼
#include <stdio.h>
#include <omp.h> 
#include <time.h>
#include <iostream>

int main(int argc, char **argv)
{
    int m = 1100;

    int a[m][m],i, j;

    int sum = 0;

    const clock_t begin_time = clock();

    omp_set_num_threads(4);

    double start = omp_get_wtime( );

    std::cout <<" NumThreads : "<< omp_get_max_threads()<<std::endl;

    #pragma omp parallel for private(j) reduction( +:sum)  // 將其註釋即可觀察不使用openmp的情況
    for(int k=0; k<100; k++)
    {
        for(int i=0; i<m; i++){
            for (int j = 0; j < m; j++) 
            {
            sum = sum + 1;
            }
        }
        //std::cout << "Thread ID : " << omp_get_thread_num()<<"   sum : " << sum << std::endl;
    }

    double end = omp_get_wtime( );
    std::cout << "   sum  Total : " << sum << std::endl;

    std::cout << "Time clock(): "<< float( clock () - begin_time ) /  CLOCKS_PER_SEC << std::endl; 

    std::cout << "Time wtime(): "<< float( end - start )  << std::endl; 

    return 0;
}

使用 openmp優化的結果

 NumThreads : 4
   sum  Total : 121000000
   Time clock(): 0.278465

不使用openmp優化的結果

 NumThreads : 4
sum  Total : 121000000
Time clock(): 0.21804

可以看出, 不是用的效果甚至更好一些,但是這不符合常識啊。 經過測試原來發現正確的測量方法應該是使用 omp_get_wtime( ), 使用的代碼如下。

#include <omp.h> 
#include <time.h>
#include <iostream>

int main(int argc, char **argv)
{
    int m = 1100;

    int a[m][m],i, j;

    int sum = 0;

    const clock_t begin_time = clock();

    omp_set_num_threads(4);

    double start = omp_get_wtime( );

    std::cout <<" NumThreads : "<< omp_get_max_threads()<<std::endl;

    #pragma omp parallel for private(j) reduction( +:sum) 
    for(int k=0; k<100; k++)
    {
        for(int i=0; i<m; i++){
            for (int j = 0; j < m; j++) 
            {
            sum = sum + 1;
            }
        }
        //std::cout << "Thread ID : " << omp_get_thread_num()<<"   sum : " << sum << std::endl;
    }

    double end = omp_get_wtime( );
    std::cout << "sum  Total : " << sum << std::endl;

    std::cout << "Time clock(): "<< float( clock () - begin_time ) /  CLOCKS_PER_SEC << std::endl; 

    std::cout << "Time wtime(): "<< float( end - start )  << std::endl; 

    return 0;
}

最後的結果如下

 NumThreads : 4
sum  Total : 121000000
Time clock(): 0.280769
Time wtime(): 0.108388

解釋一下, clock() 記錄的是cpu的滴答數的,當並行多個進程同時計算, cpu滴答數成倍增加, 所以我們得到的差值並不是真實的時間數, openmp 提供的omp_get_wtime() 才記錄的運行時的真實時間, 所以一直錯誤是沒有正確的觀察時間。 廢了我兩個小時解決。

OpenMP 經驗總結

(1) openmp 線程使用範圍

openmp是基於 “fork-join” 思想搭建的,在構造過程中。 不是很方便構造一個需要伴隨着主線程運行的線程(不知道能不能叫做守護線程), 因爲在工作中需要開出一個線程用於監聽一個 信號, 我使用了 boost::thread 庫中生成線程的方法

#include <boost/thread.hpp> 

new_thread_ = new boost::thread(boost::bind(&B::A, this));

void B::A() 
{
  while(ros::ok()) // 主要是監測這個
  {
    sleep(1); // 每隔 1s 檢查一次

    if(Signal)  // updateMap_Signal 是全局更新地圖的控制信號, 有兩個地方可以發出信號;手都發出與定時更新
       {
        if(remap()) 
        {                   
            ROS_DEBUG("Updated");
        }
         Signal = 0;               
         }
  }
return;
}

這樣產生的線程, 會伴隨着 ros 運行時的狀態, 直到主線程死亡。

(2)openmp 多層嵌套的問題

openmp 會根據cpu核心數目,fork處固定數目的線程, 如果產生了嵌套在線程切換過程中也消耗了時間, 此時反而可能拖累了執行的效率。 下面的例子有三個 for 循環, 分別檢測一下他們的結果和執行效率。

#include <omp.h> 
#include <time.h>
#include <iostream>


int main(int argc, char **argv)
{
    int m = 1100;

    int a[m][m],i, j;

    int sum = 0;

    const clock_t begin_time = clock();

    omp_set_num_threads(4);

    double start = omp_get_wtime( );

    std::cout <<" NumThreads : "<< omp_get_max_threads()<<std::endl;

    #pragma omp parallel for private(j) reduction( +:sum) 
    for(int k=0; k<100; k++)
    {
        #pragma omp parallel for
        for(int i=0; i<m; i++){
            #pragma omp parallel for
            for (int j = 0; j < m; j++) 
            {
            sum = sum + 1;
            }
        }
        //std::cout << "Thread ID : " << omp_get_thread_num()<<"   sum : " << sum << std::endl;
    }

    double end = omp_get_wtime( );
    std::cout << "sum  Total : " << sum << std::endl;

    std::cout << "Time clock(): "<< float( clock () - begin_time ) /  CLOCKS_PER_SEC << std::endl; 

    std::cout << "Time wtime(): "<< float( end - start )  << std::endl; 

    return 0;
}
嵌套數 解釋 時間
2 註釋掉第三個 omp 0.189526
1 註釋掉第二個和第三個 omp 0.098441

還有很多其它的測試, 但是這裏也可以看出,只進行一個 omp 優化反而效率最高。

所以總結的策略是, 將 omp 放置在循環迭代次數最多的地方, 經過測試還發現,多線程的優化大概也就能提升 50% 的效率。 所以代碼還是要寫的漂亮纔好 哈哈。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章