摘 要:面對日益復雜的智能移動應用,多核處理器已成為高性能移動計算的一個有效解決方案。對于多核系統(tǒng)中的應用軟件性能優(yōu)化也是其中的研究重點。本文研究了并行程序設計算法和并行程序性能優(yōu)化技術。通過對程序進行優(yōu)化,可以使它充分的發(fā)揮多核的計算能力,其方法包括增加任務數(shù)量改善負載均衡,選擇最優(yōu)的線程與處理核之間關聯(lián)策略,從而能夠大幅提高系統(tǒng)的整體性能。
關鍵詞:移動智能多核系統(tǒng);多線程;并行計算;分治法;GPA
中圖分類號:TP311.52
由于移動智能系統(tǒng)是一個資源受限的系統(tǒng),它對程序的運行空間和時間要求比桌面系統(tǒng)更為苛刻,因此,應用軟件的優(yōu)化對移動智能系統(tǒng)來說尤顯必要和緊迫[1]。
本文主要研究的性能優(yōu)化是主要指運行速度的優(yōu)化。應用軟件對其運行速度進行優(yōu)化是指在充分掌握軟、硬件特征的基礎上,通過應用程序結構調整等手段來縮短完成指定任務所需的運行時間,主要應用在對實時性要求比較高的場合。
目前,移動智能系統(tǒng)的處理器在物理上也支持多線程的并發(fā)執(zhí)行,采用適當數(shù)量的并發(fā)線程可以獲得比單一線程高的運行速度[2]。對于多核系統(tǒng)中的應用軟件性能優(yōu)化,本文研究了基于Android系統(tǒng)的并行程序設計算法和并行程序性能優(yōu)化技術。
1 我們通過一個實際應用來分析、研究基于移動平臺的應用軟件優(yōu)化的技術
包括并行處理技術、多線程優(yōu)化技術以及利用GPA(Graphics Performance Analyzers)工具輔助分析技術。
本例是一個求圓周率π的應用。有如下數(shù)學公式:
將積分公式表示為極限:
實際上Δx不可能做到無窮小,只能讓Δx盡可能小,這樣求出的結果越接近π。我們用step表示一個Δx,則num_step=1/step盡量地大??紤]到f(x)=1/(x2+1)是一個凸函數(shù),這里取一個中值來求和,即使用f[(i+0.5)/(num_steps)]來代替f[i/(num_steps)]求和,這樣求出的和不會總是比實際值偏小。最后可以得出編寫程序依據(jù)的公式為:
根據(jù)上面公式我們編寫出相應的計算程序。
2 原始應用(未經優(yōu)化)研究與分析
我們首先根據(jù)上述公式直接推導出應用的計算代碼,此代碼是沒有經過優(yōu)化的,我們稱其為“原始應用”,將其命名為SerialPi。
該應用設計思路是:讓計算π的任務放在輔助線程(這里稱為任務線程)中運行,主活動上設置按鈕來控制線程的運行,并用一個TextView來顯示任務線程的結果。應用運行的界面如圖1所示。
應用啟動后的界面如圖1(a)所示。當我們點擊“開始計算”按鈕后,界面所有的按鈕都變成灰色,直到計算π的線程運算完成。這時,界面顯示π的計算結果以及線程運行的總時間,如圖1(b)所示。
從上述運行界面可知,此應用計算π的時間大約為22秒多。多次運行此應用,其顯示的計算時間也大約為此時長。
圖1 SerialPi應用運行的界面
2.1 在原始應用 中 新 建 線 程 類MyTaskThread,讓其計算π的值。編輯源文件
MyTaskThread.java,主要代碼如下:
1 package com.example.serialpi;
2 public class MyTaskThread extends Thread {
3 private Handler mainHandler;
4 public static final int MSG_FINISHED = 1; //定義表示\"計算結束\"的消息類型
5 private static final long num_steps = 200000000;//公式中的 num_steps 變量,總步數(shù)
6 private static final double step = 1.0 / num_steps;//公式中的step變量,步長
7 public static double pi = 0.0; //π的計算結果
……
20 public void r u n ()
21 {
22 double x ,sum=0.0;
23 long i;
24 for ( i=0; i< num_steps; i++){
25 x = ( i+ 0. 5) * s t e p ;
26 sum=sum+ 4. 0 /(1.0 + x*x );
27 }
28 pI=step*sum;
29 Messagemsg=new Message();
30 msg.what=MSG _FINISHED;//定義消息類型
31 main Hand ler.send Message(msg);//發(fā)送消息}
第22行至第28行是根據(jù)計算公式書寫的計算π的代碼。這里x變量是函數(shù)f(x)=1/(x2+1)的自變量x,sum是Σ的累積變量。累積完Σ后,最后在第28行,讓π=step×∑算出最后結果。【在線程的run函數(shù)中,一旦計算完成,在第29行開始,就向主線程發(fā)送計算完成的消息】
2.2 編輯主程序的源代碼文件MainActivity.java,讓其控制線程的運行,并顯示計算結果,其主要代碼如下:
1 package com.example.serialpi;
2 public class MainActivity extends Activity {
……
13 private MyTaskThread myThread = 1;
14 private TextView tv_TaskOutputInfo;// 顯示(計算)任務線程輸出
15 private Handler mHandler;;
16 private long end_time;
17 private long time;
18 private long start_time;
……
38mHandler = new Handler() {
39 public void handleMessage(Message msg) {
40 switch (msg.what)
41 {
42 case MyTaskThread.MSG_FINISHED:
43 end_time = System.currentTimeMillis();
44 time = end_time - start_time;
45 String s = \"運行結束,Pi=\"+ MyTaskThread.pi+ \" 耗時:\"
46 + MyTaskThread.msTimeToDatetime(time);
47 tv_TaskOutputInfo.setText(s);
48 btn_ExitApp.setEnabled(true);
49 break;
50 default:break; } } };
……
61 private void startTask() {
62 myThread = new MyTaskThread(mHandler); //創(chuàng)建一個線程
63if (! myThread.isAlive())
64{
65 start_time = System.currentTimeMillis();
66 myThread.start(); // 啟動線程 } }
我們首先在16行-第18行定義了三個變量,任務的開始時間start_time,任務的結束時間end_time,以及任務的運行時長time。它們存在如下關系:time=end_time-start_time。
在第65行,我們在啟動任務線程的同時,將機器的當前時間記錄在start_time中。在第43行~第44行,當收到任務線程運行完畢的消息后,將機器的當前時間記錄在end_time中,兩者相減得到任務的運行時長。
2.3 使用GPA工具來驗證與評估原始應用的性能。上述工作完成后,我們將應用部署到實際的智能設備上進行測試。本例我們主要監(jiān)控分析兩個CPU的負載(即“CPU XX Load”指標)情況。在監(jiān)控期間,我們點擊“開始計算”按鈕開始運行計算,記錄下GPA的監(jiān)控信息,分析的結果如圖2所示。
(a)點擊“開始計算”按鈕后
(b)線程計算運行中 (c)線程計算結束
圖2 原始應用(SerialPi)GPA截圖
圖2(a)是點擊“開始計算”按鈕,計算(任務)線程運行開始時的顯示。圖2(b)是計算(任務)線程運行中的顯示。圖2(c)是計算(任務)線程運行結束時的顯示。從圖中可知,計算線程沒有運行或運行結束后,CPU的負載停留在較低水平。而一旦計算線程運行,CPU的負載急劇上升到100%的滿負荷水平。但是,在計算線程運行時,兩個CPU中始終只有一個CPU是滿負荷的,另一個處于低負載水平,也就是說總的負載率(即負載之和)只有一個CPU處于滿負荷狀態(tài)。
3 對原始應用的性能優(yōu)化
上面的例子,是從計算π的公式直接想到的代碼。這個例子有沒有可能實現(xiàn)優(yōu)化呢?怎么去優(yōu)化呢?在此,我們對算法進行改進,充分利用多核處理器的硬件特點來進行優(yōu)化,挖掘出多核處理器的性能潛力。
多核處理器擁有多核、多線程技術,支持多線程在物理上的并行運行。例如,對于上例的運行環(huán)境,這個多核處理器就可以支持兩個線程的并行運行。這就是我們算法優(yōu)化的切入點:我們采用“分治法”的思路,設法讓計算任務分攤到多個(本例是兩個)線程中來運行,這樣并行運行的線程就可以加快運行速度。
首先,我們分析一下以上例子的MyTaskThread類的run函數(shù)代碼。為了計算積分面積的累計值,我們在第24行讓計算步每次累計一步來計算。實際上,我們將積分累計區(qū)域分成“塊”,每個線程負責計算其中的一塊,最后將每個線程計算的累計面積匯總起來,這樣既可以得到最后的結果,又能“分而治之”完成任務的分派執(zhí)行。這就是我們實現(xiàn)算法優(yōu)化的思路。
我們把優(yōu)化后的應用命名為ThreadPi。該應用在計算積分面積的累計值時,讓每個線程的計算步每次累計步長增加總線程數(shù),這樣讓每個線程負責累計自己負責的條帶的面積。該應用運行的界面如圖3所示。
圖3 ThreadPi應用運行的界面
此應用運行的界面與原始應用一致。從上述運行界面可知,此應用計算π的時間大約為13秒多。其時間幾乎縮短到原始應用的一半。與原始應用不一樣的是,本應用使用兩個線程來計算π。
3.1 本應用是在修改原始應用代碼的基礎上得到的,其關鍵的修改如下。修改計算任務線程類MyTaskThread的源代碼文件如下:
1 package com.example.threadpi;
……
4 public class MyTaskThread extends Thread{
5 private Handler mainHandler;
6 public static final int MSG_FINISHED = 1; // 定義表示\"計算結束\"的消息類型
7 private static final long num_steps = 200000000;//公式中的 num_steps 變量,總步數(shù)
8 private static final double step = 1.0 / num_steps;//公式中的step變量,步長
9 public static double pi = 0.0; //π的計算結果
10 public static final int num_threads = 2; //線程數(shù)
11private int myNum; //線程號
12private static Object sharedVariable = new Object();//對pi變量的同步鎖變量
13private static int finishedThreadNum = 0; //完成計算的線程數(shù)
……
28 public void run()
29 {
30double x, partialSum = 0.0;
31 long i;
32for (i = myNum; i < num_steps; i += num_threads) {
33 x = (i + 0.5) * step;
34 partialSum += 4.0 / (1.0 + x * x);
35 }
36 synchronized (sharedVariable) {
37 pi+=partialSum * step;
38 finishedThreadNum++;
39 if (finishedThreadNum >= num_threads){//等全部線程運行結束發(fā)送消息
40 Message msg = new Message();
41 msg.what = MSG_FINISHED;//定義消息類型
42 mainHandler.sendMessage(msg); //發(fā)送消息
43 }}}
上述代碼中陰影標注文字是本應用與原始應用主要區(qū)別的地方。在第10行-第13行我們定義了多線程計算任務所需的變量。num_threads是計算任務開辟的線程數(shù)。
本例運行的設備為兩個邏輯CPU,故將此值設為2。myNum是計算線程的編號,它從0到num_threads-1范圍內選擇。sharedVariable變量是對pi變量施加同步鎖而引入的變量。finishedThreadNum是完成計算的線程數(shù),但是該值等于num_threads就認為所有的計算線程都已運行結束。
第30行-第43行是計算線程的原型代碼。其中第30行-第35行是計算π的直接代碼。對于原始應用的相應代碼可知,原始應用的sum變量被partialSum所代替,它表示本線程計算的和(面積)只是全部和(面積)的一部分。最重要的區(qū)別是第32行,這里步長變量i累加的不是1,而是num_threads,即每一次都是向前移動線程數(shù)步。而i起始位置也不是原始應用的從0開始,而是從線程號開始。
當線程計算完自己的和之后,需要將此數(shù)據(jù)放到總的和(pi變量)中去。這是一個多線程共享的變量,因此需要加同步鎖。這一步驟對應第36行~第44行。第36行加同步鎖,第37行將線程自己的計算結果加到公共結果pi中去。第38行將計算結束的線程數(shù)加一。第39行,通過比較完成計算的線程數(shù)與總的線程數(shù)來判斷全部計算線程是否都已結束,只有都結束了,才向主線程發(fā)送計算結束的消息。
3.2 修改主程序MainActivity的源代碼文件MainActivity.java如下:
1 package com.example.threadpi;
……
12 public class MainActivity extends Activity{
13 private MyTaskThread thrd[]=1;
14 private long end_time;
15private long time;
16private long start_time;
……
64 private void startTask() {
65thrd = new MyTaskThread[MyTaskThread.num_threads];
66 start_time = System.currentTimeMillis();
67 for( int i=0; i < MyTaskThread.num_threads;i++){
68 thrd[i] = new MyTaskThread(mHandler); //創(chuàng)建一個線程
69 thrd[i].setStepStartNum(i);
70 thrd[i].start();
71 } }
73 private void exitApp() {
74 for (int i = 0; i < MyTaskThread.num_threads thrd != 1; i++) {
75 try {
76 thrd[i].join(); //等待線程運行結束
77 } catch (InterruptedException e) {
78 }}
……
上述代碼中陰影標注文字是本應用與原始應用主要區(qū)別的地方。在第13行,原始應用的單個線程對象變量改為了線程數(shù)組。在啟動計算任務的第67行~第71行,原始應用啟動單個線程,改為啟動數(shù)組中的全部線程,并在啟動時設置線程的編號。在等待線程結束的第74行-第78行,原始應用中等待單個線程結束,改為等待線程數(shù)組中的全部線程結束。
上述工作完成后,我們同樣將應用部署到原始應用相同的目標機上。我們單獨運行該應用,來測試一下優(yōu)化后的應用運行時間,結果是:時間幾乎縮短到原來的一半(由原來的22秒減少到13秒)。
3.3 使用GPA工具來驗證與評估優(yōu)化的應用性能。下面使用GPA來分析此應用,分析過程與原始應用一致,分析的結果如圖4所示。
(a)點擊“開始計算”按鈕后
(b)線程計算運行中 (c)線程計算結束
圖4 優(yōu)化應用(ThreadPi)的GPA截圖
從圖中可以看出,點擊“開始計算”按鈕,計算(任務)線程運行開始后,兩個CPU的負載都從低負載立即上升到滿負荷狀態(tài)。計算完成后,兩個CPU的負載都重新回到低負荷狀態(tài)。與原始應用不同是,在計算任務運行期間,全部的CPU都處于滿負荷狀態(tài),不存在負載在兩個CPU中輪流的情況。這說明該應用的計算任務使兩個CPU并行工作在滿負荷狀態(tài),這是優(yōu)化后的應用速度提高(本例提高將近100%)的內在原因。
4 結束語
本文主要研究了基于移動平臺的多核系統(tǒng)的應用軟件優(yōu)化技術。其中,多線程、平行計算技術是移動平臺系統(tǒng)的研究重點、難點,合理正確的使用這些技術,可以讓多個處理器處于均衡負載狀態(tài),以提高系統(tǒng)的整體運行速度,從而達到性能優(yōu)化的目的,使我們的應用軟件運行更加流暢、體驗更加好。
今后,我們在移動智能多核系統(tǒng)實現(xiàn)上,還有一些方面需要不斷地完善和進一步研究的地方,比如:進一步完善移動智能多核平臺上的并行編譯,支持更廣泛的并行模式,并且支持更廣泛的移動智能多核結構。
參考文獻:
[1]Butler,M.Android:Changing the Mobile Landscape.PERVASIVE COMPUTING,IEEE,2011.
[2]Ian K.T.Tan,Ian Chai,Poo Kuan Hoong.Dynamic threshold for imbalance assessment on load balancing for multicore systems[J].Computers and Electrical Engineering,2012.
[3]R.Rakvic,Q.Cai,J.González,G.Magklis,P.Chaparro,A.González.Thread-management techniques to maximize efficiency in multicore and simultaneous multithreaded microprocessors[J].ACM Transactions on Architecture and Code Optimization(TACO),2010(02).
[4]史莉雯,樊曉婭,張盛兵.單片多處理器的研究[J].計算機應用研究,2007(09):46-49.
[5]李濤,高德遠,樊曉婭.高性能微處理器性能模型設計[J].航空電子技術2011(02):25-28.
[6]ShahidBokhari,JoelSaltz.Exploring the performance of massively multithreaded architectures[J].Concurrency Computat:Pract.Exper,2010(05).
[7]Stijn Eyerman,Lieven Eeckhout.Modeling critical sections in Amdahl’s law and its implications for multicore design[J].ACM SIGARCH Computer Architecture News,2010(03).
作者簡介:何偉文(1970-),男,廣東廣州人,碩士,高級工程師,研究方向:網(wǎng)絡安全,系統(tǒng)工程,項目管理。
作者單位:廣州涉外經濟職業(yè)技術學院 信息學院,廣州 510540