如题,不要使用copymemory函数,效率低。

解决方案 »

  1.   

    我有俩问题:
    1、获取什么变量的高字节?
    2、API函数的效率都低,那还有什么更高?
      

  2.   


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

  3.   

    谢谢 c631106233请问和下面函数比,谁效率稍高?Public Function GetHiWord(ByRef lThis As Long) As Integer
       If (lThis And &H80000000) = &H80000000 Then
           GetHiWord = ((lThis And &H7FFF0000) \ &H10000) Or &H8000
       Else
           GetHiWord = (lThis And &HFFFF0000) \ &H10000
       End If
     End Function
      

  4.   

    我上面函数和直接调用copymemory  比,大约快80%
      

  5.   

    速度不知道,因为我没怎么用过,只是一个比较普通的用法,不过你的应该比我的快,byref应该是比byval快的
    就算几千万的计算应该也只差1,2秒,这个对我来说没什么用,没地方用copymemory 应该没那么慢的,具体看你的代码是怎么写的?
      

  6.   

    最快就是不经过运算,直接取每个字节的内容,就是C里面的联合体union.VB里不支持联合体,所以想要使用这个功能得绕点弯,操作SafeArray来实现,但最终效果是一样的.Option Explicit
    '仿C语言中的联合体union高效取高低字方法
    '
    '实现思路:
    '通过操作SafeArray来使一个数组的pvData指向目标变量,然后直接访问高低字,就像C里面的union一样
    '
    'By 嗷嗷叫的老马
    'http://www.m5home.com/Private Declare Function VarPtrArray Lib "msvbvm60.dll" Alias "VarPtr" (ByRef Ptr() As Any) As Long
    Private Declare Sub CopyMemory Lib "kernel32.dll" Alias "RtlMoveMemory" ( _
         ByRef Destination As Any, _
         ByRef Source As Any, _
         ByVal Length As Long)Dim myIntegerArr() As Integer, myLong As Long
    Dim ppSA As Long, pSA As Long, pvDataOld As LongPrivate Sub Command1_Click()
        myLong = &HAABBCCDD     '赋值.此变量已与数组"绑定"了.
        
        MsgBox Hex(myIntegerArr(0)) & vbCrLf & Hex(myIntegerArr(1))     '直接取得高低字
    End SubPrivate Sub Form_Load()
        Call initArray
    End SubPrivate Sub Form_Unload(Cancel As Integer)
        Call FreeArray
    End SubPrivate Sub initArray()
        '初始化数组及指针.
        '
        '将SafeArray结构的pvDara指针改为myLong变量的地址,这样就可以直接访问高低字了.
        '
        '简单来说就是"绑定"数组与myLong变量,让它们实际上使用同一个地址,相当于C里面的union
        '
        ReDim myIntegerArr(1)                           '与要访问的变量的长度相同.这里是要把Long分成两个Integer来访问,那就是0,1两个成员.
        myLong = VarPtr(myLong)                         '把自己的地址放在自己里面
        
        ppSA = VarPtrArray(myIntegerArr)                '得到指向SafeArray结构指针的指针(有点绕,就是指针的指针...- -!!)
        Call CopyMemory(pSA, ByVal ppSA, 4)             '得到SafeArray指针
        
        MsgBox "pSA=" & Hex(pSA)
        
        Call CopyMemory(pvDataOld, ByVal pSA + 12, 4)   '保存之前真数组的指针
        Call CopyMemory(ByVal pSA + 12, myLong, 4)      '设置myLong的指针为真数组指针
    End SubPrivate Sub FreeArray()
        '恢复原来申请的真数组指针,并释放数组
        '
        Call CopyMemory(ByVal pSA + 12, pvDataOld, 4)   '恢复myLong的指针为之前申请的真数组指针
        Erase myIntegerArr()
    End Sub
      

  7.   

    支持一下先,不过不是很懂为什么要绕那么多弯?看代码是用MyLong的地址指针替换掉myIntegerArr数组的地址指针,不知道可不可这样:Option Explicit
    Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long)Private Sub Command1_Click()
      MsgBox Hex(aGetLongHigh(&HAABBCCDD))
      MsgBox Hex(bGetLongHigh(&HAABBCCDD))
    End SubPrivate Function aGetLongHigh(ByRef mLong As Long) As Integer
      Dim MyBit(3) As Byte
        Call CopyMemory(MyBit(0), mLong, 4)
        Call CopyMemory(aGetLongHigh, MyBit(2), 2)
        Erase MyBit
    End FunctionPrivate Function bGetLongHigh(ByRef mLong As Long) As Integer
      Call CopyMemory(bGetLongHigh, ByVal VarPtr(mLong) + 2, 2)
    End Function好像结果也是对的,但是不知道效率上有什么区别没有?哪位大大愿意解释一下吗?谢谢……
      

  8.   


    差别很大,HOHO.我绕那么多弯,目的是让myIntegerArr()与myLong都指向同一个地址,类似C语言中的union.一旦这一步完成,以后要取myLong的高低字时,直接到myIntegerArr()中取就行了,因为myIntegerArr()中已经就是myLong的内容了,因此除了赋值操作,没有别的操作了.而你的方法应该就是楼主所说的内存复制法,要调用多次API,虽然API很快,但是调用开销加上执行开销,肯定是比联合体慢得多的.
      

  9.   

    做个测试吧.......Option Explicit
    '仿C语言中的联合体union高效取高低字方法
    '
    '实现思路:
    '通过操作SafeArray来使一个数组的pvData指向目标变量,然后直接访问高低字,就像C里面的union一样
    '
    'By 嗷嗷叫的老马
    'http://www.m5home.com/Private Declare Function VarPtrArray Lib "msvbvm60.dll" Alias "VarPtr" (ByRef Ptr() As Any) As Long
    Private Declare Sub CopyMemory Lib "kernel32.dll" Alias "RtlMoveMemory" ( _
         ByRef Destination As Any, _
         ByRef Source As Any, _
         ByVal Length As Long)
     Private Declare Function GetTickCount Lib "kernel32.dll" () As LongPrivate Const UB As Long = 100000000Dim myIntegerArr() As Integer, myByteArr() As Integer, myLong As Long
    Dim ppSA As Long, pSA As Long, pvDataOld As Long
    Dim ppSAByte As Long, pSAByte As Long, pvDataOldByte As LongDim rndList() As Long, HiWords() As IntegerPrivate Sub Command1_Click()
        Dim I As Long, K As Long
        
        ReDim rndList(UB)
        
        K = GetTickCount
        
        For I = 0 To UB
            myByteArr(0) = Rnd * 255
            myByteArr(1) = Rnd * 255
            myByteArr(2) = Rnd * 255
            myByteArr(3) = Rnd * 255
            
            rndList(I) = myLong
        Next
        
        MsgBox "生成随机数组时间=" & GetTickCount - K & "ms"
    End SubPrivate Sub Command2_Click()
        Dim I As Long, K As Long
        
        Erase HiWords()
        ReDim HiWords(UB)
        
        K = GetTickCount
        
        For I = 0 To UB
            myLong = rndList(I)
            HiWords(I) = myIntegerArr(0)
        Next
        
        MsgBox "模拟union方案时间=" & GetTickCount - K & "ms"
    End SubPrivate Sub Command3_Click()
        Dim I As Long, K As Long
        
        Erase HiWords()
        ReDim HiWords(UB)
        
        K = GetTickCount
        
        For I = 0 To UB
            HiWords(I) = zGetLngHigh(rndList(I))
        Next
        
        MsgBox "zGetLngHigh方案时间=" & GetTickCount - K & "ms"
    End SubPrivate Sub Command4_Click()
        Dim I As Long, K As Long
        
        Erase HiWords()
        ReDim HiWords(UB)
        
        K = GetTickCount
        
        For I = 0 To UB
            HiWords(I) = GetHiWord(rndList(I))
        Next
        
        MsgBox "GetHiWord方案时间=" & GetTickCount - K & "ms"
    End SubPrivate Sub Command5_Click()
        Dim I As Long, K As Long
        
        Erase HiWords()
        ReDim HiWords(UB)
        
        K = GetTickCount
        
        For I = 0 To UB
            HiWords(I) = aGetLongHigh(rndList(I))
        Next
        
        MsgBox "aGetLongHigh方案时间=" & GetTickCount - K & "ms"
    End SubPublic Function zGetLngHigh(ByVal zLong As Long) As Integer
        zGetLngHigh = (zLong And &H7FFF0000) \ &H10000 Or (((zLong And &H80000000) <> 0) And &H8000)
    End FunctionPublic Function GetHiWord(ByRef lThis As Long) As Integer
        If (lThis And &H80000000) = &H80000000 Then
            GetHiWord = ((lThis And &H7FFF0000) \ &H10000) Or &H8000
        Else
            GetHiWord = (lThis And &HFFFF0000) \ &H10000
        End If
    End Function
     
    Private Function aGetLongHigh(ByRef mLong As Long) As Integer
        Dim MyBit(3) As Byte
        
        Call CopyMemory(MyBit(0), mLong, 4)
        Call CopyMemory(aGetLongHigh, MyBit(2), 2)
        Erase MyBit
    End FunctionPrivate Sub Form_Load()
        Command1.Caption = "生成" & UB & "个随机数"
        Command2.Caption = "模拟union方案"
        Command3.Caption = "zGetLngHigh方案"
        Command4.Caption = "GetHiWord方案"
        Command5.Caption = "aGetLongHigh方案"
        
        Call initArray
    End Sub
     
    Private Sub Form_Unload(Cancel As Integer)
        Call FreeArray
    End Sub
     
    Private Sub initArray()
        '初始化数组及指针.
        '
        '将SafeArray结构的pvDara指针改为myLong变量的地址,这样就可以直接访问高低字了.
        '
        '简单来说就是"绑定"数组与myLong变量,让它们实际上使用同一个地址,相当于C里面的union
        '
        ReDim myIntegerArr(1)                           '与要访问的变量的长度相同.这里是要把Long分成两个Integer来访问,那就是0,1两个成员.
        myLong = VarPtr(myLong)                         '把自己的地址放在自己里面
        
        ppSA = VarPtrArray(myIntegerArr)                '得到指向SafeArray结构指针的指针(有点绕,就是指针的指针...- -!!)
        Call CopyMemory(pSA, ByVal ppSA, 4)             '得到SafeArray指针
         
        MsgBox "pSA=" & Hex(pSA)
         
        Call CopyMemory(pvDataOld, ByVal pSA + 12, 4)   '保存之前真数组的指针
        Call CopyMemory(ByVal pSA + 12, myLong, 4)      '设置myLong的指针为真数组指针
        
        ReDim myByteArr(3)                              '与要访问的变量的长度相同.
        myLong = VarPtr(myLong)                         '把自己的地址放在自己里面
        
        ppSAByte = VarPtrArray(myByteArr)               '得到指向SafeArray结构指针的指针(有点绕,就是指针的指针...- -!!)
        Call CopyMemory(pSAByte, ByVal ppSAByte, 4)     '得到SafeArray指针
         
        MsgBox "pSAByte=" & Hex(pSAByte)
         
        Call CopyMemory(pvDataOldByte, ByVal pSAByte + 12, 4)   '保存之前真数组的指针
        Call CopyMemory(ByVal pSAByte + 12, myLong, 4)          '设置myLong的指针为真数组指针
    End Sub
     
    Private Sub FreeArray()
        '恢复原来申请的真数组指针,并释放数组
        '
        Call CopyMemory(ByVal pSA + 12, pvDataOld, 4)   '恢复myLong的指针为之前申请的真数组指针
        Erase myIntegerArr()
        
        Call CopyMemory(ByVal pSAByte + 12, pvDataOldByte, 4)   '恢复myLong的指针为之前申请的真数组指针
        Erase myByteArr()
    End Sub四种方案比较.在我机器上,生成1亿个LONG随机数要53秒左右,物理内存/虚拟内存分别占了593M/588M.结果:模拟union方案=1462ms
    zGetLngHigh方案=5703ms
    GetHiWord方案=9453ms
    aGetLongHigh方案=64188ms
    看来API调用开销真大啊....
      

  10.   

    兴起,做了个测试.注意,测试全都是编译后的结果.在IDE中我这里申请内存失败了,估计是没那么多的逻辑内存了.把电脑设置为最高性能模式,不做编译优化,结果如下:生成1亿个LONG随机数=45047ms,提速17%
    模拟union方案=1203ms,提速21%
    zGetLngHigh方案=4469ms,提速27%
    GetHiWord方案=6531ms,提速44%
    aGetLongHigh方案=46797ms,提速37%电脑性能设置为之前的自动,再把编译选项中的高级优化全勾上,结果如下:生成1亿个LONG随机数=54719ms,降低3%
    模拟union方案=1094ms,提速33%
    zGetLngHigh方案=5281ms,提速7%
    GetHiWord方案=8015ms,提速17%
    aGetLongHigh方案=59641ms,提速7%电脑性能设置为最高性能,再把编译选项中的高级优化全勾上,火力全开了,结果如下:生成1亿个LONG随机数=41015ms,提速29%
    模拟union方案=703ms,提速107%
    zGetLngHigh方案=4094ms,提速39%
    GetHiWord方案=5890ms,提速60%
    aGetLongHigh方案=46515ms,提速37%
    有点意思,哈哈.看来编译优化对于性能的提高还是很明显的啊.不过对函数的调用几乎就没什么用了,比如生成随机数组时,RND函数是VB6内部已经编译好的,不会再二次编译了,所以没什么效果.
      

  11.   

    谢谢老马……知道怎么回事了,你的做法是直接让myIntegerArr真数组的地址指向mylong的地址,所以直接对mylong赋值就可以取得了,而我的是copy了mylong的值……
      

  12.   

    没错.
    其实在C语言里就没这么麻烦,因为直接就可以定义union,VB6里由于没有原生提供这个特性,只能绕个大弯了由于没有任何运算过程,性能当然是最好的.
      

  13.   

    有个更快的办法准备一个 long数组 * 4 的字节数组 byte, 如果是高字节用byte,如果是高位字用 ingeter
    copymemory 可以直接得到 long 数组的首元素地址再一步copymemory 把long数组首元素地址直接放到 byte 的起始地址去
    然后直接读 byte 数组的指定维就可以了比如:
    long(0) 的高位字节就是 byte(3)
    long(1) 的高位字节就是 byte(7)
    long(2) 的高位字节就是 byte(11)
      

  14.   


    用copymemory是最慢的了。况且是要取long的高字,你搞4个BYTE就离题了。
      

  15.   

    你这个测试有问题的,zGetLngHigh()和GetHiWord(),谈不上谁快,我在VB6环境下编译时候,连续循环执行1000万次,基本一致的,有时第一块,有时第二个快,但都比内存拷贝快近一倍,你这个内存拷贝用SafeArray肯定还要慢得多的。
      

  16.   

    本帖最后由 bcrun 于 2013-03-25 17:03:42 编辑
      

  17.   

    先不说zGetLngHigh()与GetHiWord()的区别.你大概还没看我的代码吧
      

  18.   

    不要看到调用了CopyMemory就说慢,看清楚是怎么用的再下结论吧.另外别光看,运行代码试试.
      

  19.   

    楼主没发现我的代码与老马的代码的区别吗?我的每次都需要copymemory,但是老马的只需要一次,以后不管你执行几千万还是几亿次都不需要再copymemory了,因为他们的地址共用了……
      

  20.   

    哎,楼上的,你们把这么多初始化数组的copymemory,不算时间吗?不符合实际情况!!
    况且也不算快,即使这样也只快了4倍。我稍微把zGetLngHigh修改后,可以快10倍,凡是使用API的,肯定最慢,包括初始化数组。其实最快是inline
      

  21.   


    其实你的代码就是最好代码,VC中有宏的,宏代替函数,或者用内联函数inline, 其实就是函数展开。调用API是很消耗CPU的,同理,很短的函数体同样非常消耗CPU,消耗在函数调用上了,本身你的函数代码只有一行,在VC中使用宏或内联就解决问题了。VB很遗憾没有支持宏,需手工替换了
      

  22.   

    本帖最后由 bcrun 于 2013-03-25 17:02:37 编辑
      

  23.   


    没有,你这个方案我测试了一下,比函数zGetLngHigh展开,快了80%,思路不错的。从测试中看出,不管调用API和函数都是相对慢的,追求高效率的地方,尽量少用。
      

  24.   

    老马有没测试一下collection的速度,感觉也非常慢,在几十万级以下的应用非常慢,比哈希要慢5-10倍之间。但哈希在百万级以上的速度,反而没有collection快。
      

  25.   

    酷心的函数由于全是使用的位运算符,CPU中直接就有相应的数学指令,编译后应该是CPU的直接运算,使用的应该也是CPU的内部缓存.展开与不展开的区别主要还是在函数调用的开销上,调用函数时涉及到内存的操作的话,内存速度肯定没CPU内部缓存快.而我的模拟UNION方案主要是内存复制,并未使用运算符,编译后就应该是一堆的MOV指令,完全是基于内存的.那么这两种方案谁快谁慢是与具体电脑有关的.如果CPU强大,那么酷心的方案应该会更快.如果CPU不是很强,但内存频率高的话,就应该是模拟UNION快了.
      

  26.   

    跟踪了一下,确实如此.以下是模拟UNION方案的反汇编:00402891   > /B9 00E1F505   mov ecx,5F5E100
    00402896   . |3BC1          cmp eax,ecx
    00402898   . |7F 22         jg short Project1.004028BC
    0040289A   . |8B4F 34       mov ecx,dword ptr ds:[edi+34]
    0040289D   . |8B51 14       mov edx,dword ptr ds:[ecx+14]
    004028A0   . |8B49 0C       mov ecx,dword ptr ds:[ecx+C]
    004028A3   . |D1E2          shl edx,1
    004028A5   . |2BCA          sub ecx,edx
    004028A7   . |8947 3C       mov dword ptr ds:[edi+3C],eax
    004028AA   . |66:8B51 02    mov dx,word ptr ds:[ecx+2]
    004028AE   . |8B8D 48FFFFFF mov ecx,dword ptr ss:[ebp-B8]
    004028B4   . |66:8955 E4    mov word ptr ss:[ebp-1C],dx
    004028B8   . |03C1          add eax,ecx
    004028BA   .^\EB D5         jmp short Project1.00402891
    以下是酷心的全指令运算方案的反汇编:004030B6   > /BA 00E1F505   mov edx,5F5E100
    004030BB   . |3BC2          cmp eax,edx
    004030BD   . |7F 2D         jg short Project1.004030EC
    004030BF   . |8BD0          mov edx,eax
    004030C1   . |81E2 00000080 and edx,80000000
    004030C7   . |F7DA          neg edx
    004030C9   . |1BD2          sbb edx,edx
    004030CB   . |F7DA          neg edx
    004030CD   . |0FBFD2        movsx edx,dx
    004030D0   . |8BD8          mov ebx,eax
    004030D2   . |F7DA          neg edx
    004030D4   . |C1FB 10       sar ebx,10
    004030D7   . |81E2 0080FFFF and edx,FFFF8000
    004030DD   . |81E3 FF7F0000 and ebx,7FFF
    004030E3   . |0BD3          or edx,ebx
    004030E5   . |8955 E0       mov dword ptr ss:[ebp-20],edx
    004030E8   . |03C1          add eax,ecx
    004030EA   .^\EB CA         jmp short Project1.004030B6全运算方案只在最后得到结果时才写了一次内存,其它时刻全是用寄存器,只要CPU不是特别垃圾,应该都是很快的。而模拟UNOIN方案几乎全是在操作内存,虽然几乎没有CPU运算的参与,但内存操作还是没有在CPU内部快.
      

  27.   

    另外你的思路我本来想封装成cUnion来测试,结果,测试太杯具,非常慢。直接速度下降到8%,下降了1o多倍VB6中只要涉及到类函数,类属性操作,真不是一般的慢呀。受打击了。VB6封装成类函数,如果函数体很小,损耗实在是太大了。VC的宏和内联函数确实是个好东西。
      

  28.   


    使用数组做指针的时候,不就使用copymemory了?
      

  29.   

    Public Function HiWord(ByVal DWord As Long) As Integer
      HiWord = (DWord And &HFFFF0000) \ &H10000
    End Function
    扯远了吧。
    要满足楼主的要求,仅需非常简单的函数。
    再想快一点,直接写表达式,省去函数调用的栈操作。
      

  30.   

    可以更简单:
    Public Function HiWord(ByVal DWord As Long) As Integer
      HiWord = DWord  \H10000 
    End Function
      

  31.   

    你以为 Long 是无符号整数啊!用 &HFFFFFFFF 试试,结果多少?
      

  32.   

    我36楼的代码,你如果不理解就做个测试,与 CopyMemory 的结果比较。
    没有调查不要轻易下结论!
      

  33.   

    这东西有什么争吵的,就是 HiWord01 = (lDWord And &HFFFF0000) \ &H10000    这个最快这样简单的计算,不要写成函数调用的,直接内嵌。\ &H10000 这样的计算,VB的编译器编译后会直接编译为移位的。 移位和位运算时最简单的CPU指令了。
    http://www.xbeat.net/vbspeed/c_HiWord.htm
      

  34.   

    老兄好久没来了,玩.Net去了! 赵老虎那个函数还真有出处啊,佩服!
      

  35.   

    我天天在CSDN啊,没啥感兴趣的问题就不会冒泡的。
      

  36.   

    当年编基础函数库的时候参考过 VBSpeed,只要性能不是很离谱,大多数选用易用易懂的版本。
    HiWord01 是少有的易用易懂又高效的。
    比如4楼这种判断符号位的,当年也是一种版本,不过被 HiWord01 完败,下架了!又: MakeDWord 函数,VB5/VB6 还有不同的冠军呢。
    HiWord01 称得上完美。
      

  37.   


    HiWord = (DWord And &HFFFF0000) \ &H10000  堪称最完美,但如果integer去高BYTE,这个用不上了,必须参考zGetLngHigh = (zLong And &H7FFF0000) \ &H10000 Or (((zLong And &H80000000) <> 0) And &H8000) 
      

  38.   

    Public Function MakeDWord05(ByVal LoWord As Integer, ByVal HiWord As Integer) As Long
    ' by G.Beckmann, [email protected], 20001207
      MakeDWord05 = (HiWord * &H10000) Or (LoWord And &HFFFF&)
    End Function
      
     
    MakeDWord06 Public Function MakeDWord06(ByVal LoWord As Integer, ByVal HiWord As Integer) As Long
    ' by Karl E. Peterson, http://www.mvps.org/vb, 20001207
      ' High word is coerced to Long to allow it to
      ' overflow limits of multiplication which shifts
      ' it left.
      MakeDWord06 = (CLng(HiWord) * &H10000) Or (LoWord And &HFFFF&)
    End Function 谁来猜猜,谁快?
      

  39.   

    integer高BYTE?
    这不是 HiByte 的干活?
    照样用整除!
      

  40.   

    integer高BYTE?
    这不是 HiByte 的干活?
    照样用整除!别想当然,自己测试去,直接死掉
      

  41.   

    integer高BYTE?
    这不是 HiByte 的干活?
    照样用整除!还是有点区别,不过思路确实一样byteA = ((Word And &HFF00) \ &H100) And &HFF
      

  42.   

    是叫你去看现成的 HiByte,照样可以用整除。
    哪有说直接复制过来就用的?
      

  43.   

    数据量稍大的情况下,copymemory 字节数组映射法 无疑是最快的
    位运算的情况仅适合小数据量
    虽然位运算看似只用了寄存器而没用内存,但也并不一定就快,这要看指令
    2~3次的copymemory就完全把一个long数组数据映射到byte数组中,数据就出来了,大数据量的情况下只有copymemory的耗时,其它则没有一丝一毫的耗时,位运算反而会慢,因为他要一个一个的算