Лучший способ заменить использование тяжелых исключений?

#c #exception #optimization

#c #исключение #оптимизация

Вопрос:

У меня есть старый проект на C , который я сделал некоторое время назад. Ну, это эмулятор процессора. Всякий раз, когда происходит сбой процессора (например, деление на ноль или прерывание точки останова отладки и т.д.) В моем коде, он просто выполняет throw , и в моем основном цикле у меня есть что-то вроде этого:

 try{
    *(uint32_t*)amp;op_cache=ReadDword(cCS,eip);
    (this-&&t;*Opcodes[op_cache[0]])();
    eip=(uint16_t)eip 1;
}
catch(CpuInt_excp err){
    err.codeamp;=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(**re&s16[AX]==0){
  throw CpuInt_excp(0); //throw divide by zero error
}
  

Что в основном делает этот код, так это просто считывает код операции и, если возникло исключение, затем вызывает соответствующее прерывание (в CPU, которое просто изменяет EIP)

Что ж, поскольку это происходит в основном цикле, try{}catch{} накладные расходы действительно увеличиваются. Это не преждевременная оптимизация, я профилировал ее и вспомогательные функции исключения &cc (даже без каких-либо сбоев и, следовательно, без бросков), и вспомогательные функции заняли более 10% от общего времени выполнения долго работающей эмулируемой программы.

Итак! Каким был бы наилучший способ замены исключений в этом случае? Я бы предпочел не отслеживать возвращаемые значения, потому что у меня уже написана тонна кода, и потому что отслеживать их действительно сложно, когда функции становятся действительно глубокими.

Комментарии:

1. Хм, я бы предложил setjmp и lon&jmp , но это плохо сочетается со многими функциями C . Возможно, вам придется стиснуть зубы, отслеживать возвращаемые значения и усвоить в качестве урока на будущее: исключения предназначены для исключительных условий.

2. Я собираюсь попробовать переместить блок try в другое место, чтобы посмотреть, поможет ли это, а если нет… что ж, тогда, я думаю, вы правы : (

3. Это случайно не GCC 3? Если это GCC 4, вы пробовали -fprofile-arcs этот вариант? Ваше профилирование сообщит компилятору, что исключений не происходит.

4. Я пробовал это с обоими, но никогда не пробовал использовать этот флаг.

Ответ №1:

Вы не показали свой цикл, но я предполагаю, что в псевдокоде это:

 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 , но я думаю, что вы разрешаете продолжать после исключений без паники). Естественно, если вам не нужно продолжать, вам нужен только один цикл.

Внешний цикл просто перезапускает работу после исключения. Он может проверять то же условие, что и внутренний цикл, если проверка этого условия не требует больших затрат (например, это программный счетчик или что-то в этом роде), или у него может быть флаг, как в приведенном выше случае, если проверка условия требует больших затрат.

Ответ №2:

Как насчет удаления try / catch из вашего внутреннего цикла? Потребовалось бы больше бухгалтерии в catch и некоторые дополнительные накладные расходы на возврат в ваш innerloop без запуска сверху, но вы бы платили только тогда, когда исключение действительно срабатывает, а не все время.

Комментарии:

1. Добавление внешнего цикла, который перезапустит внутренний цикл в случае исключения и убедится, что все переменные внутреннего цикла, фактически определенные во внешнем цикле, позаботятся о бухгалтерии и проблеме «начинать сверху».

2. @vava: Да, я тоже об этом думал. Но только earlz знает, насколько это практично.

3. Да, на самом деле это в форме библиотеки, где основное приложение делает что-то вроде cpu-&&t;exec() выполнения одного кода операции в основном цикле. Так что это потребует некоторого переосмысления того, как это работает…

4. Если вы можете сохранить свое состояние в структуре (или классе), это может помочь.

Ответ №3:

Похоже, что вы записываете состояние процессора в переменные-члены, поскольку ваши методы кода операции не принимают никаких параметров. Рискуя заявить очевидное, почему бы не сделать то же самое для состояния исключения? Тогда вам не придется иметь дело с возвращаемым значением. Вместо этого вам просто понадобятся функции кода операции, чтобы установить состояние исключения, вернуть и позволить основному циклу проверить состояние исключения.