我有一个旧的C ++项目,我做了一会儿。好吧,它是一个CPU模拟器。每当我的代码中发生CPU故障(例如除以零或调试断点中断等)时,它只会执行throw
并且在我的主循环中我有这样的事情:
try{
*(uint32_t*)&op_cache=ReadDword(cCS,eip);
(this->*Opcodes[op_cache[0]])();
eip=(uint16_t)eip+1;
}
catch(CpuInt_excp err){
err.code&=0x00FF;
switch(err.code){
case 0:
case 1: //.....
Int16(err.code);
break;
default:
throw CpuPanic_excp("16bit Faults",(err.code|0xF000)|TRIPLE_FAULT_EXCP);
break;
}
}
一个简单的操作码示例(凭空掏出)
if(**regs16[AX]==0){
throw CpuInt_excp(0); //throw divide by zero error
}
这段代码基本上只是读取操作码,如果发生异常,则调用适当的中断(在CPU中,只更改EIP)
好吧,这是在主循环中,try{}catch{}
开销确实增加了。这不是一个过早的优化,我描述了它和gcc的异常辅助函数(甚至没有做任何错误,因此没有抛出),并且辅助函数占用了长时间运行的模拟程序的总执行时间的10%以上。
原来如此! 在这种情况下替换异常的最佳方法是什么?我宁愿不必跟踪返回值,因为我已经编写了大量代码,并且因为跟踪它们真的是当函数变得非常深刻时很难。
答案 0 :(得分:3)
你没有展示你的循环,但我猜测它是伪代码:
while (some_condition) {
// Your try..catch block:
try {
// Do an op
}
catch (CpuInt_excp err) {
// Handle the exception
}
}
您可以将try..catch
移出一个级别:
done = false;
while (!done) {
try {
while (some_condition) {
// Do an op
}
done = true;
}
catch (CpuInt_excp err) {
// Handle the exception
}
}
我已经包含了两个循环,因为我认为如果发生异常,你希望能够继续(很难在不知道你在Int16
做什么的情况下告诉我,但我认为你'允许在非恐慌例外之后继续进行)。当然,如果你不需要继续,你只需要一个循环。
外部循环只是在异常后重新启动。它可以检查与内部循环相同的条件,如果检查的条件不是很昂贵(例如,它是程序计数器或其他东西),或者如果检查条件很昂贵,它可以有上面的标志。
答案 1 :(得分:1)
如何将try / catch移出内循环?这将需要更多的记账,并且一些额外的开销会在没有从顶部开始的情况下重新进入你的内部循环,但是只有在实际触发的异常而不是所有时间时才会支付。
答案 2 :(得分:0)
看起来你正在成员变量中记录CPU状态,因为你的操作码方法不带任何参数。有明确说明的风险,为什么不对异常状态做同样的事情呢?那么您将不必处理返回值。相反,您只需要操作码函数来设置异常状态,返回,并让主循环检查异常状态。