【源码】VB6运用 【内联汇编代码 + CallWindowProc】 实现的移位操作

舉杯邀明月 2016-10-14 05:04:05
加精
【VB6 位移操作“第一弹”: 运用 【内联汇编代码 + CallWindowProc】 实现的移位操作】

  计算机指令的“移位”操作是比较常见的,机器指令(或者说:CPU、硬件)直接提供对“移位操作”的支持。
  很多计算机语言都是直接支持“移位操作”的语句,可以实现高效的移位运算。

  但是,在VB6中,却没有“移位操作”的语句和函数支持。因为“没有”,所以在VB6的程序代码中几乎是看不到进行
移位操作的语句的。虽然在VB6中没有移位操作,但并不代表写VB6的程序完全没有这个需求;可是当实实在在的有需要
时,我们只好“绕弯子”去实现移位操作,或者是用别的算法(逻辑)去避开“移位操作”的方式。
  如果需要在VB6中实现移位运算的支持,最根本的方法,就是通过“算术运算”来实现了。只是在VB6中没有“无符号”
整数、在算术运算时又有“溢出”问题,因此要“正确移位”,又得去计算“掩码”来屏蔽掉某些bit位。但计算“掩码”
只能是通过“幂运算”得到,这个运算是浮点运算,相对于整数运算来说要多消耗很多时间。记得好像是16倍:在8086芯
片上,一个整数运算是4个时钟周期,(即“1个基本指令周期”),但浮点运算需要64个时钟(也许完成像幂运算这种
“超元函数”需要的开销更大,所以当时就有一个浮点运算协处理器8087专门处理浮点运算)。现在的CPU都是在“流水管
线”中执行指令,很多指令都是“看起来1个时钟1条指令”了,但这浮点运算肯定还没达到这步,估计它这“16个时钟”
是少不了的 (具体不清楚,没看到过关于“流水管线”中各种机器指令所需耗时的资料)。
因此用算术运算来实现移位操作,过程比较复杂,运行效率也比较低。
  今天我在这儿分享一份源码,是参阅了网上不少的资料,然后自己整理、封装的一个移位操作接口模块。使用自己构
建的汇编代码(机器码),然后通过API函数CallWindowProc()去调用,执行移位操作,并返回操作结果。当然要说运行效
率,这个方法并不算高(后面有测试数据),因为CallWindowProc()这个API函数,并不是为了给你“通过地址进行函数调
用”而生的,它是“窗口消息操作”相关的函数,在使用时先得把相关参数传递给它,而“在它里面”会做很多有关窗口
操作的一些“准备工作”,因此执行了很多对“移位操作”来说根本无用的指令。虽然在调用时本来是“窗口句柄”的参
数,被一个“被操作数值”替代了,但是CallWindowProc()并不会去验证这个“句柄值”是否真的代表一个有效窗口,因
而我们的“伪造回调函数”才会得以被执行。总体来说,用这种方式实现的移位操作,运行效率还是比用算术运算来模拟
移位的方法高一些。
  这个移位操作模块,移位函数共有8个,就是32位/16位的、左移/右移、单向移/循环移,组合起来2×2×2就是8个了,
写这个模块的时候,应该是2013年5月份吧。当然这8个函数,都是基于“逻辑移位”来操作的,因为我觉得“算术移位”似
乎根本用不上。如果你觉得有这个需要,反正我把源码发布出来了,你可以“依葫芦画瓢”自己去添加。更早之前写的那个
“算术运算移位”模块(大概是2009年,不太清楚了,因为以前的代码基本没什么注释),也中封装的这样8个函数。当然,
今天这个源码中,算术移位的只有32位左移和右移的代码,因为“算术移位”仅仅是配角而已。
为了对比测试,才从之前的代码模块中复制了过来。 

  那个“算术运算移位”的代码,看起来“很不雅”,请大家勿见笑!因为我编写代码比较注重运行效率,当时就是出于
“尽可能快的出结果”,所以在函数是“能得到结果时”就拿到结果并Exit Function。因此函数中有很多Exit Function。
不过也无所谓了,因为这个代码早就成了“尘封的历史”,根本不会再用到它,也就不用去考虑它写得好不好、如何写能够
更好了。按我现在的编程风格,所有的Function几乎是没有“Exit Function”出现的,函数出口都是在“End Function”
那儿。自然,在“End Function”的前面那句,必然都是“函数名 = 返回值”这样的了。


  先贴个运行测试的效果图。可以看到,“左移位”的操作,算术运算(“右上角”那块。在下图中按“有焦点的按钮”
识别是哪个操作方法的结果)耗时是API操作(左上)的两倍,因为左移位时,算术运算要考虑溢出问题、必须要单独处理
“移到符号位去”的那一个bit位。而“右移位”,如果是“正整数”(左下),耗时也API操作相当,但是如果是“符号位
不为0”的数值,在VB6中也就是“负数”,那么算术运算移位时又得单独处理一下符号位了,消耗的时间立马出现“翻倍”
(右下)。特殊情况就是“被操作数值为0”和“移位位数为0”的这种情况了,因为可以立即返回结果,所以耗时极短。


  为什么这儿不比较“循环移位”呢?对于CPU来说,执行机器指令对数据进行移位,无论是左移、右移、循环移,要移
动多少位,都是没有任何区别的。但是用算术方法实现呢,这就完全不同了,上面已经有“单向移位”的比较结果了,如
果是要进行“循环移位”,无论左循环还是右循环,必须是“两个单向移位的组合操作”,所以不用比,结果都出来了:
总耗时将超过API操作的3到4倍,妥妥的……(当然仍然是“0”除外)。
  还有,用“算术运算移位”,大家也可以写自己的算法来比较、测试一下。唯一的要求:不能对操作数、移位位数的
“参数”有任何限制,必须要保证通用、正确。

  再说一下“单次调用消耗”,我的是1.7G的双核CPU,并且是“移动版”的,因此速度比较慢点。
  按照调用次数、对应耗时进行计算,调用API执行一次移位操作,消耗约270个时钟周期。不过实际应该没有这么多,
虽然CPU是双核四线程,毕竟Win7系统还有四、五十个的后台进程,论“线程数”,肯定是有一百多的了,不可能把CPU资
源全部耗在你这一个进程上。但这也能从侧面反映出,在VB6中要“移位运算”,开销不是一般般的大…………

  测试代码如下:
Option Explicit

Private Sub Command1_Click()
Dim t As Double
Dim i As Long
Dim v As Long
Dim n As Long

v = Val(Text1.Text)
n = 10000 * Val(Text2.Text)
t = Timer()
For i = 1& To n
n = LongSHL(v, 15)
Next
t = Timer() - t
Me.Cls
Me.Print "左移位操作: 15位"
Me.Print "运算结果:" & Hex$(n)
Me.Print "运算次数:" & (i - 1&)
Me.Print "消耗时间(ms):" & Int(1000 * t)
End Sub

Private Sub Command2_Click()
Dim t As Double
Dim i As Long
Dim v As Long
Dim n As Long

v = Val(Text1.Text)
n = 10000 * Val(Text2.Text)
t = Timer()
For i = 1& To n
n = LongLShift(v, 15)
Next
t = Timer() - t
Me.Cls
Me.Print "左移位操作: 15位"
Me.Print "运算结果:" & Hex$(n)
Me.Print "运算次数:" & (i - 1&)
Me.Print "消耗时间(ms):" & Int(1000 * t)
End Sub

Private Sub Command3_Click()
Dim t As Double
Dim i As Long
Dim v As Long
Dim n As Long

v = Val(Text1.Text)
n = 10000 * Val(Text2.Text)
t = Timer()
For i = 1& To n
n = LongSHR(v, 15)
Next
t = Timer() - t
Me.Cls
Me.Print "右移位操作: 15位"
Me.Print "运算结果:" & Hex$(n)
Me.Print "运算次数:" & (i - 1&)
Me.Print "消耗时间(ms):" & Int(1000 * t)
End Sub

Private Sub Command4_Click()
Dim t As Double
Dim i As Long
Dim v As Long
Dim n As Long

v = Val(Text1.Text)
n = 10000 * Val(Text2.Text)
t = Timer()
For i = 1& To n
n = LongRShiftLG(v, 15)
Next
t = Timer() - t
Me.Cls
Me.Print "右移位操作: 15位"
Me.Print "运算结果:" & Hex$(n)
Me.Print "运算次数:" & (i - 1&)
Me.Print "消耗时间(ms):" & Int(1000 * t)
End Sub

Private Sub Form_Load()
Call InitASM
Text1.Text = "&H4567ABCD"
Text2.Text = "500"
AutoRedraw = True
End Sub


再贴张图片展示一点代码: 


  这个运行效率可以说是“实在太低”,有没有能更高效率的实现方法呢?
  答案是肯定的:用 COM对象+ASM …………  
  这个COM对象,当然不是在VB6工程中“加一个class模块”了事,而是用代码自己“制造出来”的,实现起来要复杂点。
  最近进行了一下基础测试,效果还不错。效率嘛,说了出来可能会吓你们一跳。当然不知道稳定性如何,还需要进一步
的分析、测试(但估计没什么问题),目前也不准备公布这份源码(给大家留点悬念,哈哈……)
  所以呢,在这儿我也学学别的网友,来个什么“第一季、第二季”呀,“第一弹、第二弹”什么的。
  在以后合适的情况下,有可能也会公布出来。

  等会儿,我把这个工程(主要目的是发布“移位操作接口模块”)上传到资源中,有兴趣的朋友们可以下载。
...全文
6657 77 打赏 收藏 转发到动态 举报
写回复
用AI写文章
77 条回复
切换为时间正序
请发表友善的回复…
发表回复
qianxu050 2017-09-16
  • 打赏
  • 举报
回复
将近多少年过去了,楼主这么热心于VB6,真是让人感动,楼主还是学生吗?
舉杯邀明月 2016-12-23
  • 打赏
  • 举报
回复
引用 77 楼 m2200 的回复:
[quote=引用 75 楼 Chen8013 的回复:] [quote=引用 74 楼 PixelDemon 的回复:] 楼主还在执着于VB啊。
我又不是专门搞程序开发的,只是写一些小程序玩玩。 其实我觉得用VB6写程序挺好的,盲目“追随潮流”不是我的风格。 某些把VB6说得一无是处的人,其实是根本不懂VB6而已。 [/quote] 那你是管理岗位?或者教学的?[/quote] 都不是,我只是对这方面有兴趣,自己学了一下编程而已。
赵4老师 2016-12-23
  • 打赏
  • 举报
回复
使用VB6完全可以写出Docker
爱睡觉的阿狸 2016-12-23
  • 打赏
  • 举报
回复
引用 75 楼 Chen8013 的回复:
[quote=引用 74 楼 PixelDemon 的回复:] 楼主还在执着于VB啊。
我又不是专门搞程序开发的,只是写一些小程序玩玩。 其实我觉得用VB6写程序挺好的,盲目“追随潮流”不是我的风格。 某些把VB6说得一无是处的人,其实是根本不懂VB6而已。 [/quote] 那你是管理岗位?或者教学的?
用户 昵称 2016-12-22
  • 打赏
  • 举报
回复
引用 69 楼 Chen8013 的回复:
[quote=引用 68 楼 jennyvenus 的回复:] 成本很高。
什么意思? [/quote] vb使用成本很高啊,你这大牛,如果站队到了c体制内,整个互联网上的资源都可以利用,用vb总要突破各种限制,精力都放在这上面了。
PixelDemon 2016-12-22
  • 打赏
  • 举报
回复
RLIShiftRight RLIShiftLeft 系统有这样的API函数的,不过很慢。 可以参考这里的数据在比较下: http://www.xbeat.net/vbspeed/c_ShiftLeft.htm
舉杯邀明月 2016-12-22
  • 打赏
  • 举报
回复
引用 74 楼 PixelDemon 的回复:
楼主还在执着于VB啊。
我又不是专门搞程序开发的,只是写一些小程序玩玩。 其实我觉得用VB6写程序挺好的,盲目“追随潮流”不是我的风格。 某些把VB6说得一无是处的人,其实是根本不懂VB6而已。
PixelDemon 2016-12-22
  • 打赏
  • 举报
回复
楼主还在执着于VB啊。
舉杯邀明月 2016-12-22
  • 打赏
  • 举报
回复
引用 71 楼 jennyvenus 的回复:
[quote=引用 69 楼 Chen8013 的回复:] [quote=引用 68 楼 jennyvenus 的回复:] 成本很高。
什么意思? [/quote] vb使用成本很高啊,你这大牛,如果站队到了c体制内,整个互联网上的资源都可以利用,用vb总要突破各种限制,精力都放在这上面了。 [/quote] 原来你是说这个“成本”啊。 确实,在某些时候,是得“想办法突破一些限制”。但是封装好之后,也是一劳永逸的事情。 不过,有了“应用经验”,类似的使用就算“轻车熟路”了, 就比如最近研究的某游戏数据的解密算法, 它这个要用到“32位无符号整数”的加法和乘法, 但VB6中没有无符号整数,并且对“任意两个32位数”进行运算时,都可能面临“溢出”问题。 如果完全凭VB6的“自身的基本语句代码”来完成这种加法或乘法运算, 会遇到比较麻烦的“处理逻辑”,相应的运行效率也变得很低了。 有了之前“移位操作接口”做轻量COM对象的经验,这回我同样用轻量COM对象来处理, 代码简洁、高效, 实现起来也 So easy . . . . . . .
舉杯邀明月 2016-12-22
  • 打赏
  • 举报
回复
引用 70 楼 PixelDemon 的回复:
RLIShiftRight RLIShiftLeft 系统有这样的API函数的,不过很慢。 可以参考这里的数据在比较下: http://www.xbeat.net/vbspeed/c_ShiftLeft.htm
这个API,我早就知道啦………… 看本贴子34楼、35楼,我都已经把用这种API实现移位操作的封装代码上传了。 用这组API实现移位,只是比用“内联汇编代码 + CallWindowProc”快几倍、十来倍而已, 速度并不算很快,并且“循环移位”也只能快四、五倍的样子。  另外你看看我在24楼的回复: 我用“机器指令注入”方式,可以比“内联汇编代码 + CallWindowProc”快70几、80倍。 只是这个方法不能在IDE中使用,必须编译后才有效。 也有点担心“系统原因”造成注入失败。因此不会轻易用这个。 另外一个就是“轻量级COM对象”实现的了,比“内联汇编代码 + CallWindowProc”快60倍左右, 但是这个方法在IDE和编译后都可以使用,平时我也就是用的这种方法。 这种方法的速度,每次移位调用比“汇编语言代码”多执行三、四条指令而已,可以说速度够快的了。
舉杯邀明月 2016-12-21
  • 打赏
  • 举报
回复
引用 68 楼 jennyvenus 的回复:
成本很高。
什么意思? 
用户 昵称 2016-12-21
  • 打赏
  • 举报
回复
成本很高。
westmood 2016-12-20
  • 打赏
  • 举报
回复
我是来参观的
line_us 2016-12-13
  • 打赏
  • 举报
回复
但是,在VB6中,却没有“移位操作”的语句和函数支持。
爱睡觉的阿狸 2016-12-12
  • 打赏
  • 举报
回复
版主现在都能全站推了,
舉杯邀明月 2016-12-12
  • 打赏
  • 举报
回复
引用 62 楼 m2200 的回复:
我擦,我竟然还能推荐技术贴
关键这个不是你这个“版主所在的版块”呀,居然还能被你推荐? 不过这个帖子没什么推荐价值了,本来是计划写个“系列”的, 但实在没什么人气,可能很大家都不在意这些东西, 后面的我也没兴趣写了。
_nives 2016-12-12
  • 打赏
  • 举报
回复
不错不错
爱睡觉的阿狸 2016-12-12
  • 打赏
  • 举报
回复
我擦,我竟然还能推荐技术贴
望断雁南飞 2016-12-12
  • 打赏
  • 举报
回复
我只是来水经验的
舉杯邀明月 2016-12-11
  • 打赏
  • 举报
回复
热烈欢迎楼上两位大水B光临 VB版块 !!!
加载更多回复(57)

7,763

社区成员

发帖
与我相关
我的任务
社区描述
VB 基础类
社区管理员
  • VB基础类社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧