自學OpenMP指南【多層for循環】

很多場景下,爲了實現某一目標我們會使用多層for循環來解決問題,針對多層for循環如何使用openmp來進行加速,是本篇博客所關注的問題。本篇博客將就着以下3點進行討論

從一個多層循環的例子說起

先看這麼一段代碼:

for(int i = 0; i < 2; i++) {
	cout << "first loop"<< endl;
	for (int j = 0; j < 2; j++) {
		cout << "second loop" << endl;
		for (int k = 0; k < 2; k++) {
			printf("third loop i = %d j = %d k = %d \n");
		}
	}
}

以上爲0-test.cc,後面會結合這個代碼進行改動得到1-test.cc,2-test.cc.3-test.cc

這個案例中一共有3層for循環,每一層都進行了打印操作,我們的目標是對這三層循環進行加速。

首先我們應該清楚openmp能提升運行速度的原因主要時因爲並行。不使用openmp,程序是串行的,一個操作接着一個操作進行。但實際上許多操作本身是不互相影響的,因此給提升性能帶來了可能,我們可以讓程序同一時刻進行多個互相不干擾的程序,進而提升效率。

如果某個程序不同操作之間有影響,難道就不能並行了麼?也不全是,並行的程序不能相互影響,因此想要提升相互影響的程序,首先可以先改代碼使得不同操作之間沒有影響,然後再並行。這個會在後期的其他博客中寫,這裏按住不提。

就着上一個例子說明,單看最內層的打印操作,可以發現事實上對於最內層的打印來說,這個程序僅僅是重複打印了8次,這8次是互相不干擾的,因此是可以通過並行提升性能的。

重點是我們如何用openmp進行並行。

一些並行嘗試

爲了更好的解釋OpenMP在這裏的作用我會用4個例子來具體介紹

1-test.cc
opm_set_num_threads(4)
#pragma omp parallel
for(int i = 0; i < 2; i++) {
 cout << "first loop"<< endl;
 for (int j = 0; j < 2; j++) {
  cout << "second loop" << endl;
  for (int k = 0; k < 2; k++) {
   printf("third loop i = %d j = %d k = %d \n");
  }
 }
}

很遺憾,這個代碼並不會提升性能,在#pragma omp parallel的確會生成指定數目的線程個數,但是在1-test.cc的多個線程每個線程都完成了一遍整個程序,因此時間沒有減少。

2-test.cc
opm_set_num_threads(4)
#pragma omp parallel
#pragma parallel for
for(int i = 0; i < 2; i++) {
 cout << "first loop"<< endl;
 for (int j = 0; j < 2; j++) {
  cout << "second loop" << endl;
  for (int k = 0; k < 2; k++) {
   printf("third loop i = %d j = %d k = %d \n");
  }
 }
}

這個代碼才真正做到了提升效率,並且時間可以減少爲原來的一半。

這究竟是什麼原因呢?通過查詢IBM官方
給出的解釋可以知道#pragma omp parallel會進行以下操作:

  • 生成指定數目的線程組
  • 每個線程完成語句生效範圍內的所有操作
  • Working-sharing construct區域內的操作將由不同線程分別完成
    IBM官方給出的解釋
    #pragam parallel for則完成了working-sharing construct區域建立的功能,因此必須在for循環前面加一個#pragma parallel for才能真正實現並行
3-test.cc

#pragma omp parallel for可以實現2-test.cc中的2行代碼的功能

opm_set_num_threads(4)
#pragma omp parallel for
for(int i = 0; i < 2; i++) {
 cout << "first loop"<< endl;
 for (int j = 0; j < 2; j++) {
  cout << "second loop" << endl;
  for (int k = 0; k < 2; k++) {
   printf("third loop i = %d j = %d k = %d \n");
  }
 }
}

結論

對於多層for循環這種代碼結構而言,如果最內層for循環各個分支之間互相不影響,則可以通過最外層for循環添加#pragma omp parallel for的方式實現併發。

不過這個方法要特別注意是,多層for循環最內層分支不能互相有影響,而對於有影響的for循環,應該在併發之前改動代碼結構,使得內層結構不互相影響,之後纔可以進行代碼併發化。

並且,不是所有的多層for循環都是可以簡單通過一行指令進行並行的,對於更復雜的代碼結構,應該先理清代碼邏輯,然後改寫原代碼成可以並行的代碼結構,之後進行並行。

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