Исключения C ++ / Catch Optimization для C ++ - PullRequest
3 голосов
/ 22 июля 2010

Мне кажется, что если у вас есть код C ++, подобный этому:

int f()
{
  try {
    if( do_it() != success ) {
      throw do_it_failure();
    }
  } catch( const std::exception &e ) {
    show_error( e.what() );
  }
}

Компилятор C ++ должен уметь оптимизировать бросок и поймать почти простое движение.

Однако из моего опыта просмотра разборки и пошагового выполнения кода мне кажется, что компиляторы всегда перепрыгивают через очень грязные библиотеки обработки исключений.

Почему они это делают? Есть ли какое-то требование к языку, которое мешает оптимизации? Что если бы это было:

int f()
{
  try { throw std::runtime_error("Boo!"); }
  catch ( const std::exception &e ) { std::cout << e.what() << std::endl; }
}

Почему компилятор не просто переписывает это как

int f()
{
  std::cout << "Boo!" << std::endl;
}

Ответы [ 3 ]

5 голосов
/ 22 июля 2010

Почему они это делают?

Поскольку исключение C ++ предназначено для, ну, исключительных обстоятельств и производительность при исключительных обстоятельствах на самом деле не имеет значения.

C ++ 'исключения были разработаны с учетом этого, чтобы поставщики компиляторов могли обеспечить почти оптимальную производительность в общем случае, когда исключений не было, за счет худшей производительности в нечетном случае.когда создаются исключения.

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

5 голосов
/ 22 июля 2010

Поскольку do_it() может выдать другое исключение, перед тем как вы бросите do_it_failure();

Что касается вашего второго примера, компилятор может это сделать, но это должно рассматриваться как особый случайзачем беспокоиться о таком патологическом случае?

1 голос
/ 11 июля 2019

Я думаю, что принятый ответ является довольно неинформативным, если не неправильным, поэтому даже после стольких лет я чувствую необходимость предложить правильный ответ.

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

Кроме того, этот фрагмент кода может казаться настолько изобретательным, что не стоит обдумывать, но это не так: он может быть результатом встраивания и оптимизации гораздо более сложного кода, а его оптимизация может привести к более простому коду, который позволяет выполнять другую оптимизацию переходит в огонь, или содержащая функция для дальнейшего встраивания. Подобные этапы оптимизации, когда они правильные и эффективные для реализации, всегда заслуживают, по крайней мере, рассмотрения, независимо от того, насколько вычурным может выглядеть оригинальный фрагмент кода. В противном случае можно избежать даже фундаментальных проходов, таких как устранение мертвого кода , потому что "мертвый код не должен быть написан в первую очередь". Что, очевидно, не так.

Следовательно, я просто не согласен с принятым ответом. Тот факт, что исключения должны создаваться исключительно, является , а не причиной, по которой этот код не оптимизирован.

Причина носит чисто технический характер и объясняется в этом электронном письме из списка рассылки clang development: http://lists.llvm.org/pipermail/cfe-dev/2015-March/042035.html

Подводя итог, язык позволяет коду, вызываемому внутри блока catch, перебрасывать исключение в любой точке, "даже не видя" объект исключения:

void g() { throw; }

Следовательно, рассмотрим код OP:

int f()
{
  try { throw std::runtime_error("Boo!"); }
  catch ( const std::exception &e ) { std::cout << e.what() << std::endl; }
}

Что касается компилятора, e.what() или два вызова operator<< могут перебросить исключение, следовательно, оптимизация кода обработки исключений нарушит семантику программы.

Чтобы этого не произошло, потребуются «знания всей программы», как написано в приведенном выше сообщении электронной почты. Даже в простых случаях можно оптимизировать , например:

int func() {
  try {
    throw 42;
  }catch(int x) {
    return x;
  }
}

Код выше можно преобразовать в return 42. Нет никаких технических причин, которые мешают этому.

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

В любом случае, в сообщении ничего не говорится о том, примут ли они патч для этого.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...