Подтвердить что ты не робот

Попробовать/уловить или проверить скорость?

Я работаю с 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. Кто-нибудь знает более быстрый метод?


EDIT:. Благодаря ответам я попробовал и протестировал следующее решение, которое специально проверяет имя класса перед добавлением объектов Vector():

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.


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

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

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)
4b9b3361

Ответ 1

Я поддержал ответ Matt Joiner, но хотел включить некоторые дополнительные замечания, чтобы дать понять, что наряду с несколькими другими факторами существует 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, конечно, пока у меня не было оснований подозревать, что этот фрагмент кода был фактическим узким местом производительности.

Ответ 2

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

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