Re[5]: Какие у исключений проблемы?
От: Cyberax Марс  
Дата: 05.11.14 19:52
Оценка:
Здравствуйте, WolfHound, Вы писали:

C>>DWARF-исключения заметно раздувают код для обработки ошибочных путей, а таблицы исключений заметно давят на кэш процессора. Так что использовать исключения для нормального flow-control'а — очень неэффективно.

WH>А для нормального их никто и не использует.
А хочется.

WH>Они нужны для того чтобы обработать ситуацию когда что-то пошло не так.

В этом и проблема. "Что-то пошло не так" может различаться с точки зрения автора кода и программиста, который этот код использует. Классика жанра — должен ли File.open() кидать исключение, если файл не найден?
Sapienti sat!
Re[6]: Какие у исключений проблемы?
От: dimgel Россия https://github.com/dimgel
Дата: 05.11.14 20:01
Оценка: +2
Здравствуйте, Cyberax, Вы писали:

WH>>А для нормального их никто и не использует.

C>А хочется.

Не-а.

WH>>Они нужны для того чтобы обработать ситуацию когда что-то пошло не так.

C>Классика жанра — должен ли File.open() кидать исключение, если файл не найден?

Голосовалку замути. Очередное ---или--- вслед за моей вчерашней. Я за исключение проголосую, потому что имена файлов, которые открывать собираюсь, не с бухты-барахты выдумываю и имею право ожидать, что они должны открыться успешно. Десктопники тоже — либо свои собственные файлы открывают, либо выбранное юзером в диалоге OpenFile — тоже обычно существующие. А вот когда приходилось на PHP низкоуровнево с файлами работать, то вся эта C-style муть с трёхстрочными проверками на ошибку на каждую строку собственно логики бесила.
Re[21]: Какие у исключений проблемы?
От: AlexRK  
Дата: 05.11.14 20:18
Оценка:
Здравствуйте, WolfHound, Вы писали:

ARK>>Мой тест показывает, что одно исключение уже медленнее, чем миллион проверок.

WH>Так ты даже корректный тест написать не можешь...

Да, кстати. А чем это мои тесты некорректны? 5 миллионов проверок есть? Есть. Что еще надо?
Напишите уже внятно, изложите свою позицию. Если, конечно, она у вас есть.
Re[22]: Какие у исключений проблемы?
От: WolfHound  
Дата: 05.11.14 20:51
Оценка:
Здравствуйте, AlexRK, Вы писали:

ARK>Ну так приведите корректный тест с правильными кодами возврата, а не сотрясайте воздух.

Код с исключениями в 4.7 раза быстрее.

Release:
No exceptions: 00:00:00.0079723 (1784293664), 00:00:00.0016705 (1784293664)
Single exception: 00:00:00.0082693 (1785293665), 00:00:00.0016904 (1785293665)
Two exceptions: 00:00:00.0077591 (1785293665), 00:00:00.0017407 (1785293665)

Debug:
No exceptions: 00:00:00.0449391 (1784293664), 00:00:00.0218418 (1784293664)
Single exception: 00:00:00.0438513 (1785293665), 00:00:00.0222914 (1785293665)
Two exceptions: 00:00:00.0436745 (1785293665), 00:00:00.0216939 (1785293665)

  Скрытый текст
using System;
using System.Diagnostics;
class Prog
{
  int func1_1(int a, out int result)
  {
    result = a + 1;

    if (a > 1000000)
      return -1;
    else
      return 0;
  }

  int func1_2(int a, out int result)
  {
    if (func1_1(a, out result) == 0)
      return 0;
    else
      return -1;
  }

  int func1_3(int a, out int result)
  {
    if (func1_2(a, out result) == 0)
      return 0;
    else
      return -1;
  }

  int func1_4(int a, out int result)
  {
    if (func1_3(a, out result) == 0)
      return 0;
    else
      return -1;
  }

  int func1_5(int a, out int result)
  {
    if (func1_4(a, out result) == 0)
      return 0;
    else
      return -1;
  }

  int func2_1(int a)
  {
    if (a > 1000000)
      throw new Exception("qq");

    return a + 1;
  }

  int func2_2(int a)
  {
    return func2_1(a);
  }

  int func2_3(int a)
  {
    return func2_2(a);
  }

  int func2_4(int a)
  {
    return func2_3(a);
  }

  int func2_5(int a)
  {
    return func2_4(a);
  }

  private string Run(string title, int max)
  {
    var start1 = Stopwatch.StartNew();
    int res1 = 0;
    for (int i = 0; i < max; i++)
    {
      int val;
      if (func1_5(i, out val) == 0)
      {
        func1_5(i, out val);
        res1 += val;
      }
      else
        break;
    }
    var end1 = start1.Elapsed;

    var start2 = Stopwatch.StartNew();
    int res2 = 0;
    try
    {
      for (int i = 0; i < max; i++)
      {
        res2 += func2_5(i);
      }
    }
    catch
    {
      // do nothing
    }
    var end2 = start2.Elapsed;

    return String.Format("{0}: {1} ({2}), {3} ({4})", title, end1, res1, end2, res2);
  }

  public static void Main()
  {
    var prog = new Prog();
    prog.Run("test run", 1000010);
    prog.Run("test run", 1000010);
    prog.Run("test run", 1000010);

    var line1 = prog.Run("No exceptions", 1000000);
    var line2 = prog.Run("Single exception", 1000002);
    var line3 = prog.Run("Two exceptions", 1000003);

    System.IO.File.WriteAllLines("c:\\temp\\temp.txt", new[] { line1, line2, line3 });
  }
}
... << RSDN@Home 1.2.0 alpha 5 rev. 62>>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
Re[23]: Какие у исключений проблемы?
От: artelk  
Дата: 05.11.14 21:04
Оценка:
Здравствуйте, WolfHound, Вы писали:

WH>Здравствуйте, AlexRK, Вы писали:


ARK>>Ну так приведите корректный тест с правильными кодами возврата, а не сотрясайте воздух.

WH>Код с исключениями в 4.7 раза быстрее.

WH>Release:

WH>No exceptions: 00:00:00.0079723 (1784293664), 00:00:00.0016705 (1784293664)
WH>Single exception: 00:00:00.0082693 (1785293665), 00:00:00.0016904 (1785293665)
WH>Two exceptions: 00:00:00.0077591 (1785293665), 00:00:00.0017407 (1785293665)

WH>Debug:

WH>No exceptions: 00:00:00.0449391 (1784293664), 00:00:00.0218418 (1784293664)
WH>Single exception: 00:00:00.0438513 (1785293665), 00:00:00.0222914 (1785293665)
WH>Two exceptions: 00:00:00.0436745 (1785293665), 00:00:00.0216939 (1785293665)

      if (func1_5(i, out val) == 0)
      {
        func1_5(i, out val);
        res1 += val;
      }

Убери лишний вызов и перезапусти плз.
Re[23]: Какие у исключений проблемы?
От: AlexRK  
Дата: 05.11.14 21:08
Оценка:
Здравствуйте, WolfHound, Вы писали:

ARK>>Ну так приведите корректный тест с правильными кодами возврата, а не сотрясайте воздух.

WH>Код с исключениями в 4.7 раза быстрее.

У вас тут мелкое жульничество в виде двойного вызова func1_5.
Кстати, чем это от моего последнего "некорректного" теста отличается?
Re[24]: Какие у исключений проблемы?
От: WolfHound  
Дата: 05.11.14 21:29
Оценка: 12 (1) +1
Здравствуйте, artelk, Вы писали:

A>Убери лишний вызов и перезапусти плз.

Копипаста. Но код с исключениями всёравно быстрее в 1.5 раза.

Release:
No exceptions: 00:00:00.0029851 (1784293664), 00:00:00.0019009 (1784293664)
Single exception: 00:00:00.0030534 (1785293665), 00:00:00.0022424 (1785293665)
Two exceptions: 00:00:00.0030700 (1785293665), 00:00:00.0019424 (1785293665)

Debug:
No exceptions: 00:00:00.0254283 (1784293664), 00:00:00.0222134 (1784293664)
Single exception: 00:00:00.0245588 (1785293665), 00:00:00.0232391 (1785293665)
Two exceptions: 00:00:00.0242956 (1785293665), 00:00:00.0234590 (1785293665)
... << RSDN@Home 1.2.0 alpha 5 rev. 62>>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
Re[24]: Какие у исключений проблемы?
От: WolfHound  
Дата: 05.11.14 21:29
Оценка:
Здравствуйте, AlexRK, Вы писали:

ARK>У вас тут мелкое жульничество в виде двойного вызова func1_5.

Исправил. Коды возврата всё равно сливают.

ARK>Кстати, чем это от моего последнего "некорректного" теста отличается?

А в цикле код возврата проверять, кто будет? Пушкин?
... << RSDN@Home 1.2.0 alpha 5 rev. 62>>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
Re[25]: Какие у исключений проблемы?
От: artelk  
Дата: 05.11.14 22:10
Оценка: +1
Здравствуйте, WolfHound, Вы писали:

WH>Здравствуйте, artelk, Вы писали:


A>>Убери лишний вызов и перезапусти плз.

WH>Копипаста. Но код с исключениями всёравно быстрее в 1.5 раза.

Надо бы еще NoInlining понавтыкать для чистоты эксперимента:
http://msdn.microsoft.com/en-us/library/system.runtime.compilerservices.methodimploptions(v=vs.110).aspx

Не думаю, что в .net сильно заморачивались с оптимизацией исключений. Но если их использовать по назначению (т.е. для исключительных ситуаций), то это не проблема — обычно после них что-то где-то логгируется, что делает тормоза исключений пренебрежимо малой величиной.
Re[3]: Какие у исключений проблемы?
От: uncommon Ниоткуда  
Дата: 06.11.14 03:36
Оценка:
Здравствуйте, dimgel, Вы писали:

D>Ограничить дальность полёта можно только одним способом: ловить на каком-нибудь промежуточном слое и (опционально) заворачивать в нечто более понятное слою верхнему.


Открой для себя Boost.Exception, который позволяет присоединять к исключению информацию о контексте. Ничего заворачивать и не нужно.
Re[2]: Какие у исключений проблемы?
От: uncommon Ниоткуда  
Дата: 06.11.14 03:46
Оценка:
Здравствуйте, jazzer, Вы писали:

J>Здравствуйте, vsb, Вы писали:


vsb>>На мой взгляд это проблема исключительно С++, а точнее его двух особенностей — отсутствие GC и запрет исключений в деструкторах.


J>Нет такого запрета.


Запрета нет, но классы, выбрасывающие из деструктора, регулярными считаться не не могут. Они разве что могут играть роль утилит с очень специфичным поведением. Недаром в C++11 деструкторы являются noexcept(true) по умолчанию.
Re[25]: Какие у исключений проблемы?
От: AlexRK  
Дата: 06.11.14 06:45
Оценка: :))
Здравствуйте, WolfHound, Вы писали:

WH>Исправил. Коды возврата всё равно сливают.


В случае с одним исключением результат практически одинаковый. Без исключений вообще — да, коды возврата слегка медленнее.
С двумя исключениями у exception-based code был бы слив, просто в тесте после вылета первого исключения замер прекращается.

ARK>>Кстати, чем это от моего последнего "некорректного" теста отличается?

WH>А в цикле код возврата проверять, кто будет? Пушкин?

Это не имеет отношения к корректности теста. Можно было и не писать с многозначительным видом напыщенные фразы про некорректность.
Просто вместо 5 миллионов ифов стало 6. Можно и 7 сделать, если добавить еще один уровень вложенности.

Ну и самое главное осталось без комментариев.
А именно:
1) реальное приложение выполняет реальную работу, и проверки на ее фоне будут не видны в микроскоп;
2) не предоставлено обоснования, что exception-based code быстрее, чем retval-based code. Тесты показывают только одно — для дотнета одно исключение по времени соответствует 5 миллионам проверок.
Re[7]: Какие у исключений проблемы?
От: Miroff Россия  
Дата: 06.11.14 07:16
Оценка: 2 (1)
Здравствуйте, vsb, Вы писали:

vsb>Это Checked Exceptions в Java.


Для концептуальной законченности им не хватает нескольких вещей:

1. Полного запрета бросать руками RuntimeException
2. Перевода всех RuntimeException в либо в checked либо в error.
3. Синтаксического сахара позволяющего конвертировать необработанные исключения при выбрасывании их вверх по стеку

Что-то типа:
class UserDao {
    exception SQLException translate to DaoException;

    User getUser(long id) throws DaoException {
        //code can cause SQL exception
    }
}


vsb>Во-вторых ряд исключений не вылетит вообще никогда. Например Integer.parseInt("1"). Никогда тут не вылетит исключение NumberFormatException. А компилятор попросит обложить try-catch-ем.


Never say "never". Завтра пользователь поставит локаль где все числа римские, а программист поправит parseInt чтобы он использовал локаль и здравствуй исключение. Большинство таких ситуация вызваны кривым дизайном языка, а не фундаментальными проблемами.

vsb>В-третьих ряд исключений могут вылететь в любом месте. В разных языках по-разному. Например NullPointerException теоретически может вылететь на любой строчке, где есть ссылки или вызывается функций. StackOverflowError может вылететь опять же везде, где вызывается любая функция. Что с ними делать?


Запретить null как класс. Вместо него использовать либо Option, либо вообще ничего не использовать. Как в Scala:

[code=scala]
val userOption:Option[User] = userDao.getUser(userId);

for (user <- userOption) {
doSmth(user);
}
[/code]

vsb>теперь примеряем его на FileInputStream, а оказыавется, что close там хочет кидать IOException. Чего нам делать? Либо добавляем в throws IOException, либо перекидыаем как непроверяемое исключение. И имеем опять что имеем.


По уму нужно было объявлять интерфейс как

class CloseException extends Exception {};

interface Closeable {
    void close() throws CloseException;
}


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

vsb>Если добваляем throws IOException, то куча классов этот IOException никогда кидать не будет. StringWriter, например, пишет во внутренний буффер-строку, никакого там IO нет. А вот при закрытии — изволь, обработай IOException, мало ли, вдруг кинется, в сигнатуре написано же.


LSP никто не отменял. Завтра StringWriter заменят на BufferedFileWriter а вызывающий код должен работать как прежде. Единственный способ достичь этого это корректно обработать исключенние даже если сейчас оно не бросается.
Re[3]: Какие у исключений проблемы?
От: jazzer Россия Skype: enerjazzer
Дата: 06.11.14 07:26
Оценка:
Здравствуйте, uncommon, Вы писали:

U>Здравствуйте, jazzer, Вы писали:


J>>Здравствуйте, vsb, Вы писали:


vsb>>>На мой взгляд это проблема исключительно С++, а точнее его двух особенностей — отсутствие GC и запрет исключений в деструкторах.


J>>Нет такого запрета.


U>Запрета нет, но классы, выбрасывающие из деструктора, регулярными считаться не не могут. Они разве что могут играть роль утилит с очень специфичным поведением. Недаром в C++11 деструкторы являются noexcept(true) по умолчанию.


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

А насчет регулярности типов — на этот счет есть очень разные мнения.
Например, Степанов в своей книге включает в определение регулярности типа наличие копирования, присваивания и конструктора по умолчанию (плюс, емнип, сравнение на равенство и больше-меньше — чтоб тип можно было использовать в качестве ключа ассоциативного контейнера. Ну и хэш тогда тоже, наверное, заодно).
Ну и, плюс, не факт, что регулярность так уж необходима.
jazzer (Skype: enerjazzer) Ночная тема для RSDN
Автор: jazzer
Дата: 26.11.09

You will always get what you always got
  If you always do  what you always did
Re[2]: Какие у исключений проблемы?
От: elmal  
Дата: 06.11.14 07:53
Оценка: +2
Здравствуйте, LaptevVV, Вы писали:

LVV>Одна из серьезных проблем, которую практически не поминают — это парадигма обработки исключений.

LVV>По сути это событийное программирование.
LVV>И вот получается, что в одной проге смешаны как минимум две, а то и три парадигмы: ООП+событийное (и очень часто процедурное).
Да хоть 10 парадигм в одной проге — ничего страшного. Главное парадигмы применять правильно и тогда, когда это имеет смысл. А проблемы с исключениями возникают тогда, когда с их помощью начинают хреначить логику, и они становятся не исключениями, а нормальным потоком выполнения программы.

А типичная обработка исключений — это вывести пользователю сообщение об ошибке или матернуться в лог о том, что случилась какая то хрень. 99 процентов всех обработок исключений. В ряде случаев приходится некоторую логику навешивать. Например для вебсервисов внезапно могут куки авторизации стать невалидными, например по причине перезапуска сервиса. В этом случае нужно перелогиниться и повторить попытку. Соответствуем декорируем вызов метода, и обработчик исключения сделает перелогин и вызовет метод еще раз.
Re[8]: Какие у исключений проблемы?
От: enji  
Дата: 06.11.14 10:19
Оценка:
Здравствуйте, Miroff, Вы писали:

M>Запретить null как класс. Вместо него использовать либо Option, либо вообще ничего не использовать. Как в Scala:


M>[code=scala]

M>val userOption:Option[User] = userDao.getUser(userId);

M>for (user <- userOption) {

M> doSmth(user);
M>}
M>[/code]

и в чем тут профит? Вместо эксепшена в строке user->doSmth(), doSmth молча не будет выполнено, что выстрелит где-то позже и в другом месте...

опять же, сравни с

val user = userDao.getUser(userId);
if (user)
  user->doSmth();


по строчкам — тоже самое, по символам — короче

Если добавить чуток сахара, можно было бы писать user?.doSmth(), как в груви. И тут кстати user — обычная ссылка, а не Option<User>
Re[8]: Какие у исключений проблемы?
От: enji  
Дата: 06.11.14 10:24
Оценка:
Здравствуйте, Miroff, Вы писали:

M>Запретить null как класс. Вместо него использовать либо Option, либо вообще ничего не использовать.


И кстати — как его запретить? Вообще запретить простые ссылки? Т.е. везде только Option?

И во что тогда превратится такое: obj1.calc().doIt().getSome() ?
Re[9]: Какие у исключений проблемы?
От: vsb Казахстан  
Дата: 06.11.14 10:36
Оценка:
Здравствуйте, enji, Вы писали:

M>>Запретить null как класс. Вместо него использовать либо Option, либо вообще ничего не использовать.


E>И кстати — как его запретить? Вообще запретить простые ссылки? Т.е. везде только Option?


E>И во что тогда превратится такое: obj1.calc().doIt().getSome() ?


Можно сделать два вида ссылок — nullable и nonnullable. Компилятор легко может проверить все места, где возможны проблемы, и заставить вставить проверки.
Re[9]: Какие у исключений проблемы?
От: AlexRK  
Дата: 06.11.14 11:08
Оценка: +1
Здравствуйте, enji, Вы писали:

M>>Запретить null как класс. Вместо него использовать либо Option, либо вообще ничего не использовать.

E>И кстати — как его запретить? Вообще запретить простые ссылки? Т.е. везде только Option?
E>И во что тогда превратится такое: obj1.calc().doIt().getSome() ?

Если каждый из уровней может потенциально вернуть Nothing или его аналог — то придется выписывать руками проверки. Ну или засахарить как-то типа obj1?.calc()?.doIt()?.getSome().
Но фишка в том, что обычно такие вызовы не будут возвращать Nothing. А там, где все-таки возвращают — как раз в коде и будет хорошо видно.
Re[9]: Какие у исключений проблемы?
От: dimgel Россия https://github.com/dimgel
Дата: 06.11.14 12:33
Оценка: -1
Здравствуйте, enji, Вы писали:

M>>[code=scala]

M>>for (user <- userOption) {
M>> doSmth(user);
M>>}
M>>[/code]

E>и в чем тут профит? Вместо эксепшена в строке user->doSmth(), doSmth молча не будет выполнено, что выстрелит где-то позже и в другом месте...


Пожалуйста: doSmth(userOption.get). Здесь get может вылетить NoSuchElementException.

E>опять же, сравни с


E>
E>val user = userDao.getUser(userId);
E>if (user)
  user->>doSmth();
E>


E>по строчкам — тоже самое, по символам — короче


E>Если добавить чуток сахара, можно было бы писать user?.doSmth(), как в груви. И тут кстати user — обычная ссылка, а не Option<User>


Можно так: userOption.foreach(doSmth)
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.