深入探討用位掩碼代替分支(6):VB6速度測試


  前面我們測試了C系列語言,驗證了位掩碼算法的確實性能不錯。那么對於Basic系列語言,該算法的效率怎樣呢?於是本文對此進行探討。
  VB.Net與C#一樣,也是由.Net虛擬機執行的,沒有多大的測試價值。所以我決定測試VB6。

一、移植要點

  VB6的功能與C系列語言差很多。很多地方需要換另一種方法去實現,甚至不能實現。
  要點有——
1.VB6不支持控制台程序,只支持窗口程序。所以我們的測試程序得修改為窗口程序。
2.VB6不支持指針,所以依靠數組。幸好現在的操作比較簡單,用數組的性能損失不大。
3.VB6沒有帶符號移位運算法,所以無法實現f3_sar函數。
4.VB6和C#一樣不支持宏,所以得手動實現代碼。雖然可以將LIMITSU_BYTE這些宏改寫為函數,但函數調用開銷會影響性能。於是增加了f2_negB這個測試函數來驗證這一論斷。
5.VB6不支持條件運算符(?:)。所以只能利用單行If語句來實現f0_if函數。
6.雖然VB6提供了IIf函數,但它是靠變體類型來傳遞參數的,性能十分低下。於是增加了f1_iif這個測試函數來驗證這一論斷。
7.VB6的Not、And、Or、Xor運算符實際上是位運算符,可用來代替C語言中的~、&、|、^。
8.VB6默認的參數傳遞模式時ByRef(傳引用)模式。在編寫函數時,最好明確寫清楚參數傳遞模式。因為對數組元素進行操作時,ByRef會造成額外的鎖定/解鎖操作,影響性能。(例如將LIMITSU_BYTE的參數改為ByRef,速度會慢一些)
9.VB6不支持函數指針或委托,所以得為每一個測試函數編寫代碼。


二、全部代碼

  全部代碼——

' Win32 API
Private Declare Function timeGetTime Lib "winmm.dll" () As Long


' 數據規模
Private Const DATASIZE As Long = 16384 ' 128KB / (sizeof(signed short) * 4)

' 緩沖區
Private bufS(0 To DATASIZE * 4 - 1) As Integer ' 源緩沖區。64位的顏色(4通道,每通道16位)
Private bufD(0 To DATASIZE * 4 - 1) As Byte ' 目標緩沖區。32位的顏色(4通道,每通道8位)

' 用if分支做飽和處理
Private Sub f0_if(ByRef pbufD() As Byte, ByRef pbufS() As Integer, ByVal cnt As Long)
Dim i As Long
Dim p As Long ' 當前位置
p = 0
For i = 0 To cnt - 1
' 分別對4個通道做飽和處理
If pbufS(p) < 0 Then pbufD(p) = 0 Else If pbufS(p) > 255 Then pbufD(p) = 255 Else pbufD(p) = pbufS(p)
p = p + 1
If pbufS(p) < 0 Then pbufD(p) = 0 Else If pbufS(p) > 255 Then pbufD(p) = 255 Else pbufD(p) = pbufS(p)
p = p + 1
If pbufS(p) < 0 Then pbufD(p) = 0 Else If pbufS(p) > 255 Then pbufD(p) = 255 Else pbufD(p) = pbufS(p)
p = p + 1
If pbufS(p) < 0 Then pbufD(p) = 0 Else If pbufS(p) > 255 Then pbufD(p) = 255 Else pbufD(p) = pbufS(p)
p = p + 1
Next i
End Sub

' 用IIF函數做飽和處理
Private Sub f1_iif(ByRef pbufD() As Byte, ByRef pbufS() As Integer, ByVal cnt As Long)
Dim i As Long
Dim p As Long ' 當前位置
p = 0
For i = 0 To cnt - 1
' 分別對4個通道做飽和處理
pbufD(p) = IIf(pbufS(p) < 0, 0, IIf(pbufS(p) > 255, 255, pbufS(p)))
p = p + 1
pbufD(p) = IIf(pbufS(p) < 0, 0, IIf(pbufS(p) > 255, 255, pbufS(p)))
p = p + 1
pbufD(p) = IIf(pbufS(p) < 0, 0, IIf(pbufS(p) > 255, 255, pbufS(p)))
p = p + 1
pbufD(p) = IIf(pbufS(p) < 0, 0, IIf(pbufS(p) > 255, 255, pbufS(p)))
p = p + 1
Next i
End Sub

' 用位掩碼做飽和處理.用求負生成掩碼
Private Sub f2_neg(ByRef pbufD() As Byte, ByRef pbufS() As Integer, ByVal cnt As Long)
Dim i As Long
Dim p As Long ' 當前位置
p = 0
For i = 0 To cnt - 1
' 分別對4個通道做飽和處理
pbufD(p) = ((pbufS(p) And (pbufS(p) >= 0) Or (pbufS(p) >= 256)) And &HFF)
p = p + 1
pbufD(p) = ((pbufS(p) And (pbufS(p) >= 0) Or (pbufS(p) >= 256)) And &HFF)
p = p + 1
pbufD(p) = ((pbufS(p) And (pbufS(p) >= 0) Or (pbufS(p) >= 256)) And &HFF)
p = p + 1
pbufD(p) = ((pbufS(p) And (pbufS(p) >= 0) Or (pbufS(p) >= 256)) And &HFF)
p = p + 1
Next i
End Sub

Private Function LIMITSU_BYTE(ByVal n As Integer) As Byte
LIMITSU_BYTE = ((n And (n >= 0) Or (n >= 256)) And &HFF)
End Function

' 調用LIMITSU_BYTE函數
Private Sub f2_negB(ByRef pbufD() As Byte, ByRef pbufS() As Integer, ByVal cnt As Long)
Dim i As Long
Dim p As Long ' 當前位置
p = 0
For i = 0 To cnt - 1
' 分別對4個通道做飽和處理
pbufD(p) = LIMITSU_BYTE(pbufS(p))
p = p + 1
pbufD(p) = LIMITSU_BYTE(pbufS(p))
p = p + 1
pbufD(p) = LIMITSU_BYTE(pbufS(p))
p = p + 1
pbufD(p) = LIMITSU_BYTE(pbufS(p))
p = p + 1
Next i
End Sub

Private Sub OutLog(ByRef s As String)
txtOut.Text = txtOut.Text & s & Chr(13) & Chr(10)
txtOut.Refresh
DoEvents ' 響應界面事件
End Sub

' 進行測試
Private Sub runTest(ByVal bFull As Boolean)
Const MaxI As Long = 3 ' 多次測試
Const MaxJ As Long = 4000 ' 重復運算幾次延長時間,避免計時精度問題
Dim tm0 As Long, tm1 As Long ' 執行時 間
Dim i As Long, j As Long

' begin
btnTest.Enabled = False
btnTestFull.Enabled = False
txtOut.Text = ""
Call OutLog("== noif:VB6 ==")

' f0_if
For i = 1 To MaxI ' 多次測試
tm0 = timeGetTime()
For j = 1 To MaxJ ' 重復運算幾次延長時間,避免計時精度問題
Call f0_if(bufD, bufS, DATASIZE)
Next j
tm1 = timeGetTime() - tm0
Call OutLog("f0_if[" & i & "]:" & Chr(9) & tm1)
Next i

' f1_iif
If bFull Then
For i = 1 To MaxI ' 多次測試
tm0 = timeGetTime()
For j = 1 To MaxJ ' 重復運算幾次延長時間,避免計時精度問題
Call f1_iif(bufD, bufS, DATASIZE)
Next j
tm1 = timeGetTime() - tm0
Call OutLog("f1_iif[" & i & "]:" & Chr(9) & tm1)
Next i
End If

' f2_neg
For i = 1 To MaxI ' 多次測試
tm0 = timeGetTime()
For j = 1 To MaxJ ' 重復運算幾次延長時間,避免計時精度問題
Call f2_neg(bufD, bufS, DATASIZE)
Next j
tm1 = timeGetTime() - tm0
Call OutLog("f2_neg[" & i & "]:" & Chr(9) & tm1)
Next i

' f2_negB
For i = 1 To MaxI ' 多次測試
tm0 = timeGetTime()
For j = 1 To MaxJ ' 重復運算幾次延長時間,避免計時精度問題
Call f2_negB(bufD, bufS, DATASIZE)
Next j
tm1 = timeGetTime() - tm0
Call OutLog("f2_negB[" & i & "]:" & Chr(9) & tm1)
Next i

' end
btnTest.Enabled = True
btnTestFull.Enabled = True
End Sub

Private Sub btnTest_Click()
Call runTest(False)
btnTest.SetFocus
End Sub

Private Sub btnTestFull_Click()
Call runTest(True)
btnTestFull.SetFocus
End Sub

Private Sub Form_Load()
Dim i As Long
' 初始化
Randomize
For i = 0 To DATASIZE * 4 - 1
bufS(i) = CInt(Rnd() * 512) - 128 ' 使數值在 [-128, 383] 區間
Next i
Exit Sub
End Sub

Private Sub Form_Resize()
' 是文本框自適應窗口尺寸
Call txtOut.Move(0, txtOut.Top, Me.ScaleWidth, Me.ScaleHeight - txtOut.Top)
End Sub

Private Sub Form_Unload(Cancel As Integer)
'
End Sub

 


三、配置與編譯

3.1 配置編譯優化

  步驟如下——
1.點擊菜單欄 “工程”->“屬性”,打開工程屬性對話框。
2.點擊“編譯”,切換到“編譯”頁面。
3.點擊選擇“編譯為本機代碼”、“代碼速度優化”。

4.點擊“高級優化”,打開“高級優化”對話框。
5.勾選所有的復選框,點擊“確定”關閉“高級優化”對話框。

6.點擊“確定”關閉工程屬性對話框。


3.2 獲取編譯器生成的匯編代碼

  獲取編譯器生成的匯編代碼有助於分析程序性能。雖然VB6集成開發環境沒有提供該功能,但可以通過替換“C2.exe”來獲得匯編代碼。詳見——
http://sunh.hosp.ncku.edu.tw/~cww/html/q00545.html
獲取VB編譯后的LST文件


3.3 編譯

  雖然可以在開發環境中按F5運行,但它實際上是以解釋方式運行的。
  應該點擊菜單欄 “文件”->“發布”,生成編譯后的exe。
  因現在已經用上了“C2.exe”補丁,所以在編譯過程中會提醒保存lst文件。

四、測試結果

  運行編譯后的“noifVB6.exe”。在32位winXP上的測試結果——

== noif:VB6 ==
f0_if[1]: 2844
f0_if[2]: 2859
f0_if[3]: 2844
f1_iif[1]: 83422
f1_iif[2]: 83453
f1_iif[3]: 83422
f2_neg[1]: 1078
f2_neg[2]: 1063
f2_neg[3]: 1078
f2_negB[1]: 2125
f2_negB[2]: 2125
f2_negB[3]: 2140

 

  在64位win7上的測試結果——

== noif:VB6 ==
f0_if[1]: 2839
f0_if[2]: 2824
f0_if[3]: 2839
f1_iif[1]: 90137
f1_iif[2]: 90137
f1_iif[3]: 90199
f2_neg[1]: 1061
f2_neg[2]: 1061
f2_neg[3]: 1061
f2_negB[1]: 2090
f2_negB[2]: 2090
f2_negB[3]: 2090

 

  硬件環境——
CPU:Intel Core i3-2310M, 2100 MHz
內存:DDR3-1066


源碼下載——
http://files.cnblogs.com/zyl910/noifVB6.rar

(建議閱讀編譯器生成的匯編代碼,位於“FrmNoifVB6.lst”)

 

下一篇——

http://www.cnblogs.com/zyl910/archive/2012/04/09/noifopex7.html
深入探討用位掩碼代替分支(7):MMX指令集速度測試


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM