#include <iostream>
#include "windows.h"
using namespace std;
int main()
{
__try
{
// edx : eax = 0x2000000000
// (edx : eax) / 1 = 0x2000000000 > 0x100000000
__asm
{
mov edx, 0x20
mov ecx, 1
idiv ecx
}
cout << "Not Overflow\n";
}
__except (GetExceptionCode() == EXCEPTION_INT_OVERFLOW)
{
cout << "Integer Overflow\n";
}
cin.get();
return 0;
}
运行结果
Integer Overflow
原因应该是明显的吧,idiv指令运算的结果无法容纳在一个32位寄存器里,所以抛出了溢出异常
我想问一下有没有办法能让windows不去检测idiv的结果的溢出情况(我的代码中会自行处理),不要抛出异常
说明一下:尽管可以每次都忽略异常并让程序继续运行,不过这个除法嵌套在一个反复执行的循环中(1千万次以上),效率很重要,所以每次让操作系统抛出异常,然后再在异常处理中去处理的效率很低下,有没有办法直接让windows不去进行检测溢出
21 个解决方案
#1
为什么不研究用循环指令啊?
#2
这个异常,不是 Windows 检测或触发的吧,而是由 cpu 的执行机构抛出,被 Windows 的异常例程接管进行处理而已。资料上说,除0 异常不能被禁止。
#3
你是指用循环指令模拟除法指令?
那样不是会慢好多么?
那样不是会慢好多么?
#4
正好相反,游戏程序的优化就是尽量把耗时的乘除法改为循环,因为乘除法用时很长,循环则很短,你看指令手册。
#5
当然用循环做除法对结果有点偏差。
#6
这里是只是溢出而已,不是除0异常
#7
64位数除32位数的除法的软实现比硬实现快?
#8
都是一样的。看 div 指令的说明里,#DE 发生的情况,1. 是除数为 0 ,2. 是商太大,寄存器放不下。
除非的除数比较特异,比如是 1/2/4 之类的,可用简单地通过一两个移位指令实现,否则只是个普通数值又无甚规律的话,移位操作不会比除法指令更具优势。毕竟,现在的 cpu 对 div r32 类指令,也就几个 uop 而已。
#9
关于快慢问题你可以查指令手册,不多说。
我发现,你是不是程序写错了,0x2000000000没有超过32位啊,你应该将它放在EAX中,然后扩展一下EAX,再做有符号除法啊。
#10
对不起,我看错位数了
#11
你们所说的指令手册是指《Intel® 64 and IA-32 Architectures Software Developer’s Manual》吗,我看了下idiv指令部分没有关于指令执行速度的说明?
#12
新的我也没找到,这里有一个还可以参考一下
http://bbs.chinaunix.net/thread-2133668-1-1.html
#13
你说的还是有一定道理的,老机器的处理器乘除法占时是比较长,后来硬件速度的确提高不少。
#14
指令时序表,最新的到 2011-06-08,可用在
http://www.agner.org/optimize/ 4. 里下载到;或者直接
http://www.agner.org/optimize/instruction_tables.pdf
#15
http://bbs.chinaunix.net/thread-2133668-1-1.html
上面的参考资料看了,一条除法指令才46个时钟周期,请问一下用什么算法能超过这个速度?
用模拟硬件除法的方法来写除法平均计算每一位两条指令好像不够...
上面的参考资料看了,一条除法指令才46个时钟周期,请问一下用什么算法能超过这个速度?
用模拟硬件除法的方法来写除法平均计算每一位两条指令好像不够...
#16
我做了个程序看了一下,用.lst 列表看了一下,在我的机子上
0010 2 -- -- shr ax,1
0012 2 -- -- rol ax,1
0014 113+ -- -- idiv table
idiv 113+ ,加两个只有2
#17
咦,哪个汇编器能给出这个信息,好像还是基于 8086 的?看上面提及的那个手册里,45nm 制程的 Core2 象 div/idiv 这样的指令,除数是寄存器的话,16/32 的是 7 个微时序,8 位的才 4 个。其它的普通算术运算指令,对寄存器的操作,一般也需要 1-2 个时序的。
还真不能拿上古时代的资料来衡量当下的配置的。
还真不能拿上古时代的资料来衡量当下的配置的。
#18
可以改成浮点除法运算,或者用SSE除法指令。
#19
据我所知,SSE好像没有除法指令。不过,如果除数是一个固定的数。可以采用以乘代除法,速度比直接使用除法快上很多。
#20
divss、divsd、divps、divpd
#21
sorry, 我没说清楚,我指的是SSE指令中没有整数除法指令。
#1
为什么不研究用循环指令啊?
#2
这个异常,不是 Windows 检测或触发的吧,而是由 cpu 的执行机构抛出,被 Windows 的异常例程接管进行处理而已。资料上说,除0 异常不能被禁止。
#3
你是指用循环指令模拟除法指令?
那样不是会慢好多么?
那样不是会慢好多么?
#4
正好相反,游戏程序的优化就是尽量把耗时的乘除法改为循环,因为乘除法用时很长,循环则很短,你看指令手册。
#5
当然用循环做除法对结果有点偏差。
#6
这里是只是溢出而已,不是除0异常
#7
64位数除32位数的除法的软实现比硬实现快?
#8
都是一样的。看 div 指令的说明里,#DE 发生的情况,1. 是除数为 0 ,2. 是商太大,寄存器放不下。
除非的除数比较特异,比如是 1/2/4 之类的,可用简单地通过一两个移位指令实现,否则只是个普通数值又无甚规律的话,移位操作不会比除法指令更具优势。毕竟,现在的 cpu 对 div r32 类指令,也就几个 uop 而已。
#9
关于快慢问题你可以查指令手册,不多说。
我发现,你是不是程序写错了,0x2000000000没有超过32位啊,你应该将它放在EAX中,然后扩展一下EAX,再做有符号除法啊。
#10
对不起,我看错位数了
#11
你们所说的指令手册是指《Intel® 64 and IA-32 Architectures Software Developer’s Manual》吗,我看了下idiv指令部分没有关于指令执行速度的说明?
#12
新的我也没找到,这里有一个还可以参考一下
http://bbs.chinaunix.net/thread-2133668-1-1.html
#13
你说的还是有一定道理的,老机器的处理器乘除法占时是比较长,后来硬件速度的确提高不少。
#14
指令时序表,最新的到 2011-06-08,可用在
http://www.agner.org/optimize/ 4. 里下载到;或者直接
http://www.agner.org/optimize/instruction_tables.pdf
#15
http://bbs.chinaunix.net/thread-2133668-1-1.html
上面的参考资料看了,一条除法指令才46个时钟周期,请问一下用什么算法能超过这个速度?
用模拟硬件除法的方法来写除法平均计算每一位两条指令好像不够...
上面的参考资料看了,一条除法指令才46个时钟周期,请问一下用什么算法能超过这个速度?
用模拟硬件除法的方法来写除法平均计算每一位两条指令好像不够...
#16
我做了个程序看了一下,用.lst 列表看了一下,在我的机子上
0010 2 -- -- shr ax,1
0012 2 -- -- rol ax,1
0014 113+ -- -- idiv table
idiv 113+ ,加两个只有2
#17
咦,哪个汇编器能给出这个信息,好像还是基于 8086 的?看上面提及的那个手册里,45nm 制程的 Core2 象 div/idiv 这样的指令,除数是寄存器的话,16/32 的是 7 个微时序,8 位的才 4 个。其它的普通算术运算指令,对寄存器的操作,一般也需要 1-2 个时序的。
还真不能拿上古时代的资料来衡量当下的配置的。
还真不能拿上古时代的资料来衡量当下的配置的。
#18
可以改成浮点除法运算,或者用SSE除法指令。
#19
据我所知,SSE好像没有除法指令。不过,如果除数是一个固定的数。可以采用以乘代除法,速度比直接使用除法快上很多。
#20
divss、divsd、divps、divpd
#21
sorry, 我没说清楚,我指的是SSE指令中没有整数除法指令。