Попробовать / поймать или проверить скорость? - PullRequest
36 голосов
/ 08 апреля 2011

Я работаю с Python и всякий раз, когда мне приходилось проверять ввод функции, я предполагал, что ввод работает, а затем обнаруживал ошибки.

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

Этот код используется для raytracer, поэтому любые повышения скорости велики.

Вот упрощенная версия моего Vector()класс:

class Vector:
  def __init__(self, x, y, z):
    self.x = x
    self.y = y
    self.z = z

  def __add__(self, other):
    try:
      return Vector(self.x + other.x, self.y + other.y, self.z + other.z)
    except AttributeError:
      return Vector(self.x + other, self.y + other, self.z + other)

В настоящее время я использую метод try...except.Кто-нибудь знает более быстрый метод?


РЕДАКТИРОВАТЬ: Благодаря ответам, я попробовал и протестировал следующее решение, которое проверяет специально для имени класса перед добавлением Vector() objects:

class Vector:
  def __init__(self, x, y, z):
    self.x = x
    self.y = y
    self.z = z

  def __add__(self, other):
    if type(self) == type(other):
      return Vector(self.x + other.x, self.y + other.y, self.z + other.z)
    else:
      return Vector(self.x + other, self.y + other, self.z + other)

Я провел тест скорости с этими двумя блоками кода, используя timeit, и результаты были довольно значительными:

 1.0528049469 usec/pass for Try...Except
 0.732456922531 usec/pass for If...Else
 Ratio (first / second): 1.43736090753

Я не тестировал Vector() класс с нет проверки ввода вообще (т. Е. Перемещал проверку из класса в реальный код), но я бы предположил, что это даже быстрее, чемif...else метод.


Позднее обновление : оглядываясь назад на этот код, это не оптимальное решение.

ООПделает это еще быстрее:

class Vector:
  def __init__(self, x, y, z):
    self.x = x
    self.y = y
    self.z = z

  def __add__(self, other):
    return Vector(self.x + other.x, self.y + other.y, self.z + other.z)

class Color(Vector):
  def __add__(self, other):
    if type(self) == type(other):
      return Color(self.x + other.x, self.y + other.y, self.z + other.z)
    else:
      return Color(self.x + other, self.y + other, self.z + other)

Ответы [ 2 ]

80 голосов
/ 08 апреля 2011

Я проголосовал за ответ Мэтта Джоунера, но хотел включить некоторые дополнительные наблюдения, чтобы прояснить, что наряду с парой других факторов при выборе между условиями предварительной проверки имеют значение 4 раз ( известный как LBYL или "Look Before You Leap") и просто обрабатывающий исключения (известный как EAFP или "Проще просить прощения, чем разрешения").

Это время:

  • Время, когда проверка успешна с LBYL
  • Сроки, когда проверка не удалась с LBYL
  • Сроки, когда исключение не выбрасывается с помощью EAFP
  • Время, когда исключение выбрасывается с помощью EAFP

Дополнительные факторы:

  • Типичное соотношение успешных / неудачных проверок или исключительных / не выброшенных случаев
  • Независимо от того, существует ли состояние гонки, препятствующее использованию LBYL

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

if <dir does not exist>:
    <create dir> # May still fail if another process creates the target dir

Поскольку LBYL не исключает исключения, такие случаи не дают никакой реальной выгоды, и нет необходимости принимать суждение: EAFP - единственный подход, который будет корректно обрабатывать состояние гонки.

Но если нет расы, любой подход потенциально жизнеспособен. Они предлагают различные компромиссы:

  • если не возникает исключение, то EAFP близко к свободному
  • однако, это сравнительно дорого, если возникает исключение, так как при размотке стека, создании исключения и сравнении его с предложениями по обработке исключения довольно много обработки
  • LBYL, напротив, несет потенциально высокую фиксированную стоимость: дополнительная проверка всегда выполняется, независимо от успеха или неудачи

Это приводит к следующим критериям принятия решения:

  • Известно ли, что этот фрагмент кода критичен для скорости приложения? Если нет, то не беспокойтесь о том, какая из двух программ быстрее, а о том, какую из них легче читать.
  • Является ли предварительная проверка более дорогой, чем стоимость сбора и отлова исключения? Если да, то EAFP всегда быстрее и его следует использовать.
  • Все становится более интересным, если ответ "нет". В этом случае, что быстрее, будет зависеть от того, является ли успех или случай ошибки более распространенным, и относительные скорости предварительной проверки и обработки исключений. Для окончательного ответа требуются измерения реального времени.

Как правило:

  • , если есть потенциальное состояние гонки, используйте EAFP
  • если скорость не критична, просто используйте то, что считаете более удобным для чтения
  • если предварительная проверка стоит дорого, используйте EAFP
  • если вы ожидаете, что операция в большинстве случаев будет успешной *, используйте EAFP
  • если вы ожидаете, что операция завершится неудачей более половины времени, используйте LBYL
  • если сомневаешься, измерить

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

5 голосов
/ 08 апреля 2011

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

В приведенном вами примере я бы пошел за исключением.

...