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% 的效率。 所以代碼還是要寫的漂亮纔好 哈哈。