找回密码
 加入我们

QQ登录

只需一步,快速开始

搜索
查看: 5097|回复: 1

[其它源码] 仿C语言中的联合体union高效取高低字方法(VB6.0)

[复制链接]

275

主题

3017

回帖

1

精华

管理员

嗷嗷叫的老马

积分
17064

论坛牛人贡献奖关注奖最佳版主进步奖人气王疯狂作品奖精英奖赞助论坛勋章乐于助人勋章

QQ
发表于 2013-3-23 06:01:39 | 显示全部楼层 |阅读模式
在CSDN的一个帖子( 求高效的取高字节VB6的函数 )里见到问题,说是想要高效的方式取得一个Long的高低字.

原帖子里用计算的方式来得到高字值,但还想要再快.

于是我就想到了C里面的联合体union,要是VB6里有这玩意该多好,直接赋值就能取高低字....

不过想想以前好象写过这玩意,就翻了一下之前写的一个与单片机通讯的代码,里面也有用到这功能,于是整理一下吧 {:soso_e113:}

以下代码,新建一个EXE工程,再拖个按钮,复制进去就可以了.
  1. Option Explicit
  2. '仿C语言中的联合体union高效取高低字方法
  3. '
  4. '实现思路:
  5. '通过操作SafeArray来使一个数组的pvData指向目标变量,然后直接访问高低字,就像C里面的union一样
  6. '
  7. 'By 嗷嗷叫的老马
  8. 'http://www.m5home.com/

  9. Private Declare Function VarPtrArray Lib "msvbvm60.dll" Alias "VarPtr" (ByRef Ptr() As Any) As Long
  10. Private Declare Sub CopyMemory Lib "kernel32.dll" Alias "RtlMoveMemory" ( _
  11.      ByRef Destination As Any, _
  12.      ByRef Source As Any, _
  13.      ByVal Length As Long)

  14. Dim myIntegerArr() As Integer, myLong As Long
  15. Dim ppSA As Long, pSA As Long, pvDataOld As Long

  16. Private Sub Command1_Click()
  17.     myLong = &HAABBCCDD     '赋值.此变量已与数组"绑定"了.
  18.      
  19.     MsgBox Hex(myIntegerArr(0)) & vbCrLf & Hex(myIntegerArr(1))     '直接取得高低字
  20. End Sub

  21. Private Sub Form_Load()
  22.     Call initArray
  23. End Sub

  24. Private Sub Form_Unload(Cancel As Integer)
  25.     Call FreeArray
  26. End Sub

  27. Private Sub initArray()
  28.     '初始化数组及指针.
  29.     '
  30.     '将SafeArray结构的pvDara指针改为myLong变量的地址,这样就可以直接访问高低字了.
  31.     '
  32.     '简单来说就是"绑定"数组与myLong变量,让它们实际上使用同一个地址,相当于C里面的union
  33.     '
  34.     ReDim myIntegerArr(1)                           '与要访问的变量的长度相同.这里是要把Long分成两个Integer来访问,那就是0,1两个成员.
  35.     myLong = VarPtr(myLong)                         '把自己的地址放在自己里面
  36.      
  37.     ppSA = VarPtrArray(myIntegerArr)                '得到指向SafeArray结构指针的指针(有点绕,就是指针的指针...- -!!)
  38.     Call CopyMemory(pSA, ByVal ppSA, 4)             '得到SafeArray指针
  39.      
  40.     MsgBox "pSA=" & Hex(pSA)
  41.      
  42.     Call CopyMemory(pvDataOld, ByVal pSA + 12, 4)   '保存之前真数组的指针
  43.     Call CopyMemory(ByVal pSA + 12, myLong, 4)      '设置myLong的指针为真数组指针
  44. End Sub

  45. Private Sub FreeArray()
  46.     '恢复原来申请的真数组指针,并释放数组
  47.     '
  48.     Call CopyMemory(ByVal pSA + 12, pvDataOld, 4)   '恢复myLong的指针为之前申请的真数组指针
  49.     Erase myIntegerArr()
  50. End Sub
复制代码
我就是嗷嗷叫的老马了......

275

主题

3017

回帖

1

精华

管理员

嗷嗷叫的老马

积分
17064

论坛牛人贡献奖关注奖最佳版主进步奖人气王疯狂作品奖精英奖赞助论坛勋章乐于助人勋章

QQ
 楼主| 发表于 2013-3-23 16:07:26 | 显示全部楼层
性能测试.

代码:
  1. Option Explicit
  2. '仿C语言中的联合体union高效取高低字方法
  3. '
  4. '实现思路:
  5. '通过操作SafeArray来使一个数组的pvData指向目标变量,然后直接访问高低字,就像C里面的union一样
  6. '
  7. 'By 嗷嗷叫的老马
  8. 'http://www.m5home.com/

  9. Private Declare Function VarPtrArray Lib "msvbvm60.dll" Alias "VarPtr" (ByRef Ptr() As Any) As Long
  10. Private Declare Sub CopyMemory Lib "kernel32.dll" Alias "RtlMoveMemory" ( _
  11.      ByRef Destination As Any, _
  12.      ByRef Source As Any, _
  13.      ByVal Length As Long)
  14. Private Declare Function GetTickCount Lib "kernel32.dll" () As Long

  15. Private Const UB As Long = 100000000

  16. Dim myIntegerArr() As Integer, myByteArr() As Integer, myLong As Long
  17. Dim ppSA As Long, pSA As Long, pvDataOld As Long
  18. Dim ppSAByte As Long, pSAByte As Long, pvDataOldByte As Long

  19. Dim rndList() As Long, HiWords() As Integer

  20. Private Sub Command1_Click()
  21.     Dim I As Long, K As Long
  22.    
  23.     ReDim rndList(UB)
  24.    
  25.     K = GetTickCount
  26.    
  27.     For I = 0 To UB
  28.         myByteArr(0) = Rnd * 255
  29.         myByteArr(1) = Rnd * 255
  30.         myByteArr(2) = Rnd * 255
  31.         myByteArr(3) = Rnd * 255
  32.         
  33.         rndList(I) = myLong
  34.     Next
  35.    
  36.     MsgBox "生成随机数组时间=" & GetTickCount - K & "ms"
  37. End Sub

  38. Private Sub Command2_Click()
  39.     Dim I As Long, K As Long
  40.    
  41.     Erase HiWords()
  42.     ReDim HiWords(UB)
  43.    
  44.     K = GetTickCount
  45.    
  46.     For I = 0 To UB
  47.         myLong = rndList(I)
  48.         HiWords(I) = myIntegerArr(0)
  49.     Next
  50.    
  51.     MsgBox "模拟union方案时间=" & GetTickCount - K & "ms"
  52. End Sub

  53. Private Sub Command3_Click()
  54.     Dim I As Long, K As Long
  55.    
  56.     Erase HiWords()
  57.     ReDim HiWords(UB)
  58.    
  59.     K = GetTickCount
  60.    
  61.     For I = 0 To UB
  62.         HiWords(I) = zGetLngHigh(rndList(I))
  63.     Next
  64.    
  65.     MsgBox "zGetLngHigh方案时间=" & GetTickCount - K & "ms"
  66. End Sub

  67. Private Sub Command4_Click()
  68.     Dim I As Long, K As Long
  69.    
  70.     Erase HiWords()
  71.     ReDim HiWords(UB)
  72.    
  73.     K = GetTickCount
  74.    
  75.     For I = 0 To UB
  76.         HiWords(I) = GetHiWord(rndList(I))
  77.     Next
  78.    
  79.     MsgBox "GetHiWord方案时间=" & GetTickCount - K & "ms"
  80. End Sub

  81. Private Sub Command5_Click()
  82.     Dim I As Long, K As Long
  83.    
  84.     Erase HiWords()
  85.     ReDim HiWords(UB)
  86.    
  87.     K = GetTickCount
  88.    
  89.     For I = 0 To UB
  90.         HiWords(I) = aGetLongHigh(rndList(I))
  91.     Next
  92.    
  93.     MsgBox "aGetLongHigh方案时间=" & GetTickCount - K & "ms"
  94. End Sub

  95. Public Function zGetLngHigh(ByVal zLong As Long) As Integer
  96.     zGetLngHigh = (zLong And &H7FFF0000) \ &H10000 Or (((zLong And &H80000000) <> 0) And &H8000)
  97. End Function

  98. Public Function GetHiWord(ByRef lThis As Long) As Integer
  99.     If (lThis And &H80000000) = &H80000000 Then
  100.         GetHiWord = ((lThis And &H7FFF0000) \ &H10000) Or &H8000
  101.     Else
  102.         GetHiWord = (lThis And &HFFFF0000) \ &H10000
  103.     End If
  104. End Function

  105. Private Function aGetLongHigh(ByRef mLong As Long) As Integer
  106.     Dim MyBit(3) As Byte
  107.    
  108.     Call CopyMemory(MyBit(0), mLong, 4)
  109.     Call CopyMemory(aGetLongHigh, MyBit(2), 2)
  110.     Erase MyBit
  111. End Function

  112. Private Sub Form_Load()
  113.     Command1.Caption = "生成" & UB & "个随机数"
  114.     Command2.Caption = "模拟union方案"
  115.     Command3.Caption = "zGetLngHigh方案"
  116.     Command4.Caption = "GetHiWord方案"
  117.     Command5.Caption = "aGetLongHigh方案"
  118.    
  119.     Call initArray
  120. End Sub

  121. Private Sub Form_Unload(Cancel As Integer)
  122.     Call FreeArray
  123. End Sub

  124. Private Sub initArray()
  125.     '初始化数组及指针.
  126.     '
  127.     '将SafeArray结构的pvDara指针改为myLong变量的地址,这样就可以直接访问高低字了.
  128.     '
  129.     '简单来说就是"绑定"数组与myLong变量,让它们实际上使用同一个地址,相当于C里面的union
  130.     '
  131.     ReDim myIntegerArr(1)                           '与要访问的变量的长度相同.这里是要把Long分成两个Integer来访问,那就是0,1两个成员.
  132.     myLong = VarPtr(myLong)                         '把自己的地址放在自己里面
  133.    
  134.     ppSA = VarPtrArray(myIntegerArr)                '得到指向SafeArray结构指针的指针(有点绕,就是指针的指针...- -!!)
  135.     Call CopyMemory(pSA, ByVal ppSA, 4)             '得到SafeArray指针
  136.      
  137.     MsgBox "pSA=" & Hex(pSA)
  138.      
  139.     Call CopyMemory(pvDataOld, ByVal pSA + 12, 4)   '保存之前真数组的指针
  140.     Call CopyMemory(ByVal pSA + 12, myLong, 4)      '设置myLong的指针为真数组指针
  141.    
  142.     ReDim myByteArr(3)                              '与要访问的变量的长度相同.
  143.     myLong = VarPtr(myLong)                         '把自己的地址放在自己里面
  144.    
  145.     ppSAByte = VarPtrArray(myByteArr)               '得到指向SafeArray结构指针的指针(有点绕,就是指针的指针...- -!!)
  146.     Call CopyMemory(pSAByte, ByVal ppSAByte, 4)     '得到SafeArray指针
  147.      
  148.     MsgBox "pSAByte=" & Hex(pSAByte)
  149.      
  150.     Call CopyMemory(pvDataOldByte, ByVal pSAByte + 12, 4)   '保存之前真数组的指针
  151.     Call CopyMemory(ByVal pSAByte + 12, myLong, 4)          '设置myLong的指针为真数组指针
  152. End Sub

  153. Private Sub FreeArray()
  154.     '恢复原来申请的真数组指针,并释放数组
  155.     '
  156.     Call CopyMemory(ByVal pSA + 12, pvDataOld, 4)   '恢复myLong的指针为之前申请的真数组指针
  157.     Erase myIntegerArr()
  158.    
  159.     Call CopyMemory(ByVal pSAByte + 12, pvDataOldByte, 4)   '恢复myLong的指针为之前申请的真数组指针
  160.     Erase myByteArr()
  161. End Sub
复制代码
结果:
  1. 把电脑设置为自动性能模式,不做编译优化,结果如下:

  2. 生成1亿个LONG随机数=53045ms
  3. 模拟union方案=1462ms
  4. zGetLngHigh方案=5703ms
  5. GetHiWord方案=9453ms
  6. aGetLongHigh方案=64188ms
复制代码
  1. 把电脑设置为最高性能模式,不做编译优化,结果如下:

  2. 生成1亿个LONG随机数=45047ms,提速17%
  3. 模拟union方案=1203ms,提速21%
  4. zGetLngHigh方案=4469ms,提速27%
  5. GetHiWord方案=6531ms,提速44%
  6. aGetLongHigh方案=46797ms,提速37%
复制代码
  1. 电脑性能设置为自动性能模式,再把编译选项中的高级优化全勾上,结果如下:

  2. 生成1亿个LONG随机数=54719ms,降低3%
  3. 模拟union方案=1094ms,提速33%
  4. zGetLngHigh方案=5281ms,提速7%
  5. GetHiWord方案=8015ms,提速17%
  6. aGetLongHigh方案=59641ms,提速7%
复制代码
  1. 电脑性能设置为最高性能,再把编译选项中的高级优化全勾上,火力全开了,结果如下:

  2. 生成1亿个LONG随机数=41015ms,提速29%
  3. 模拟union方案=703ms,提速107%
  4. zGetLngHigh方案=4094ms,提速39%
  5. GetHiWord方案=5890ms,提速60%
  6. aGetLongHigh方案=46515ms,提速37%
复制代码
有点意思,哈哈.

看来编译优化对于性能的提高还是很明显的啊.

不过对函数的调用几乎就没什么用了,比如生成随机数组时,RND函数是VB6内部已经编译好的,不会再二次编译了,所以没什么效果.
我就是嗷嗷叫的老马了......

您需要登录后才可以回帖 登录 | 加入我们

本版积分规则

快速回复 返回顶部 返回列表