OCaml производительность исключений
Я часто читал, что исключения являются довольно медленными, и их следует избегать, если производительность является проблемой (например, в Java, F# и т. Д.). Это относится к общим функциям OCaml, таким как Hashtbl.find
, которые возвращают исключения для элементов не найдены?
В частности, если я хочу, чтобы мое приложение было эффективным, я должен всегда проверять членство элемента, используя, например, Hashtable.mem
перед звонком Hashtbl.find
? Или будет дополнительное сравнение mem
функция негативно влияет на производительность?
3 ответа
Обработка исключений в OCaml делает вызов и перехват исключений чрезвычайно быстрыми - смотрите этот поток SO, чтобы узнать, как это реализовано. Я не позаботился о том, чтобы точно измерить его, но мое случайное предположение состояло бы в том, что он находится на уровне косвенного вызова функции.
Известно, что исключения OCaml значительно быстрее, пропорционально остальному языку, чем, например, исключения F# - это приводило к проблемам с производительностью для людей, переносящих свой код с OCaml на F#. В OCaml исключения не вызывают проблем с производительностью.
призвание Hashtbl.mem
до Hashtbl.find
скорее всего будет медленнее, чем ловить исключение. Идиоматический стиль имеет тенденцию быть try Hashtbl.find .. with Not_found -> ...
,
Тем не менее, в сообществе OCaml есть разумное движение использовать более явный стиль обработки ошибок, используя option
типы, а не исключения. Обоснование основано не на производительности, а на том факте, что средство проверки типов не позволит вам забыть обработать ситуацию с ошибкой. Я бы предпочел это при разработке свежего нового API. При использовании сторонней функции, которая выдает исключения, убедитесь, что выловили все возможные исключения немедленно; в противном случае дизайн является запахом в целом и должен быть очень обоснованным.
Из-за их удобства исключения OCaml также часто используются в качестве механизма чистого потока управления (а не для сигнализации о состоянии редкого сбоя). Вы встретите такой код:
try
for i = 0 to .... do
if .. then raise Exit
done; false
with Exit -> true
Наконец, я чувствую, что вы, возможно, неправильно подходите к выбору реализации. Задавать общие микро-вопросы о выступлениях, как правило, не так. Сначала подумайте о правильности и удобочитаемости. Вопросы производительности, как правило, должны приходить позже, и только в тех случаях, когда они измеримы / профилируемы.
Чтобы сначала ответить на конкретный вопрос для Hashtbl.mem
до Hashtbl.find
: Не делайте этого, так как проверка на наличие элемента в таблице должна выполняться дважды; в то время как стоимость будет O(1) для обеих стратегий, вызывая Hashtbl.mem
first будет вычислять хеш-значение и поиск дважды - что может занять больше времени, чем выборка исключения.
Как общий совет, создавайте функции, которые вызывают исключения, только если вероятность исключения мала. Исключения не учитываются для системы типов, поэтому более надежные реализации будут иметь 'a option
возвращаемое значение вместо 'a
плюс возможное исключение. Основная библиотека ocaml использует суффикс _exn
прояснить, что функция может генерировать исключение, но обычно предпочитает не-исключающие.
Так что для хеш-таблицы вы должны (в идеальном мире) иметь две функции:
Hashtbl.find_exn : 'a t -> key -> 'a (* throws Not_found *)
Hashtbl.find : 'a t -> key -> 'a option
Если сомневаетесь, используйте второй. Если это не для чистой скорости, вы можете написать простую оболочку вокруг оригинальной хеш-таблицы:
find_safe h k = try Some (Hashtbl.find h k) with Not_found -> None
Я часто читал, что исключения являются довольно медленными, и их следует избегать, если производительность является проблемой (например, в Java, F# и т. Д.). Относится ли это к обычным функциям OCaml, таким как Hashtbl.find, которые возвращают исключения для не найденных элементов?
Нет. Фольклор вокруг медленных исключений и для исключительных обстоятельств в основных языках - это нонсенс. Исключения - это просто еще одна конструкция потока управления. Исключения OCaml бывают быстрыми и обычно используются в неисключительных обстоятельствах. Последнее, что я посмотрел (несколько лет назад): в OCaml исключения были примерно в 6 раз быстрее, чем в C++, примерно в 100 раз быстрее, чем в Java, и примерно в 600 раз быстрее, чем в.NET.
Люди иногда избегают исключений в OCaml не по причинам, связанным с производительностью, а потому, что им нужен явный локальный поток управления, обычно заставляющий вызывающего match
через тип объединения успех / сбой вместо потенциально нелокального распространения исключений. Они делают это, чтобы улучшить правильность, которая важнее производительности.