Почему x * x быстрее, чем x ** 2? [Дубликат]

Ответственность за загрузку файлов класса несет ClassLoader. Давайте посмотрим, что произойдет, когда мы напишем наши собственные классы.

Пример 1:

class StaticTest {

      static int a;
      int b;
      int c;
}

Теперь мы можем видеть, что класс «StaticTest» имеет 3 поля. Но на самом деле нет существования переменной b, c. Но почему ???. Ладно, посмотрим. Здесь b, c - переменная экземпляра. Поскольку переменная экземпляра получает память во время создания объекта. Итак, здесь b, c пока не получают никакой памяти. Вот почему не существует b, c. Итак, существует только существование a. Для ClassLoader у него есть только одна информация о. ClassLoader еще не распознает b, c, потому что объект еще не создан.

Давайте посмотрим другой пример: Пример 2:

class StaticTest {

      public void display() {
          System.out.println("Static Test");
      }


      public static void main(String []cmd) {

             display();       
      }

}

Теперь, если мы попытаемся скомпилировать этот код, компилятор даст Ошибка CE. CE: отображение нестатического метода () не может ссылаться на статический контекст.

Теперь для ClassLoader это выглядит так:

class StaticTest {

      public static void main(String []cmd) {

             display();       
      }

}

В примере 2 Ошибка CE - это потому, что мы вызываем нестационарный метод из статического контекста. Таким образом, ClassLoader не может распознавать метод display () во время компиляции. Так возникает ошибка компиляции.

25
задан Christwo 19 June 2009 в 21:20
поделиться

6 ответов

В основном наивное умножение - O (n) с очень низким постоянным множителем. Взятие мощности O (log n) с более высоким постоянным множителем (Существуют специальные случаи, которые необходимо проверить ... дробные показатели, отрицательные показатели и т. Д.). Edit: просто для того, чтобы быть ясным, это O (n), где n - показатель степени.

Конечно, наивный подход будет быстрее при малом n, вы на самом деле реализуете небольшое подмножество экспоненциальной математики, поэтому ваш постоянный коэффициент пренебрежимо мал.

21
ответ дан patros 27 August 2018 в 05:41
поделиться

Реализация b ^ p с бинарным возведением в степень

def power(b, p):
    """
    Calculates b^p
    Complexity O(log p)
    b -> double
    p -> integer
    res -> double
    """
    res = 1

    while p:
        if p & 0x1: res *= b
        b *= b
        p >>= 1

    return res
2
ответ дан Arnaldo P. Figueira Figueira 27 August 2018 в 05:41
поделиться
  • 1
    Я попробовал это, и потребовалось 3 раза время b**p, когда b и p были прилично большими целыми числами. Фактический код, который я оптимизировал, был: s = sum((-1)**(k-j)*scipy.special.comb(k, j, exact=True)*j**n for j in range(k+1))) с n ~ 500 и k между 5120 и 8192 для теста. – Eponymous 23 April 2018 в 10:54

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

Вот краткое доказательство концепции, которая делает такую ​​оптимизацию (можно использовать как декоратор). Примечание: вам понадобится модуль byteplay для его запуска.

import byteplay, timeit

def optimise(func):
    c = byteplay.Code.from_code(func.func_code)
    prev=None
    for i, (op, arg) in enumerate(c.code):
        if op == byteplay.BINARY_POWER:
            if c.code[i-1] == (byteplay.LOAD_CONST, 2):
                c.code[i-1] = (byteplay.DUP_TOP, None)
                c.code[i] = (byteplay.BINARY_MULTIPLY, None)
    func.func_code = c.to_code()
    return func

def square(x):
    return x**2

print "Unoptimised :", timeit.Timer('square(10)','from __main__ import square').timeit(10000000)
square = optimise(square)
print "Optimised   :", timeit.Timer('square(10)','from __main__ import square').timeit(10000000)

Что дает тайминги:

Unoptimised : 6.42024898529
Optimised   : 4.52667593956

[Edit] На самом деле, думая об этом немного больше, есть очень веская причина, почему этот оптимизатор не сделан. Нет никакой гарантии, что кто-то не создаст пользовательский класс, который переопределяет методы __mul__ и __pow__ и сделает что-то другое для каждого. Единственный способ сделать это безопасно, если вы можете гарантировать, что объект в верхней части стека имеет тот же результат, что и «x **2» и «x*x», но работа над этим намного сложнее. Например. в моем примере это невозможно, так как любой объект может быть передан квадратной функции.

3
ответ дан Brian 27 August 2018 в 05:41
поделиться
  • 1
    Выглядит увлекательно. Мне нужно будет почитать на байте, о и оптимизировать глазок! Поэтому я предполагаю, что это требует знания о том, как работает байт-код Python. – Christwo 20 June 2009 в 00:18
  • 2
    IMHO, пользовательский класс, который не сохранил математическое отношение между mul и pow, был бы пародией. Особенно для малых целых степеней, таких как pow = 2. Такой класс заслуживает того, что с ним происходит. – ToolmakerSteve 15 December 2013 в 07:21

Я подозреваю, что никто не ожидал, что это будет так важно. Обычно, если вы хотите делать серьезные вычисления, вы делаете их в Fortran или C или C ++ или что-то в этом роде (и, возможно, называете их с Python).

Рассмотрение всего как exp (n * log (x) ) хорошо работает в случаях, когда n не является интегральным или довольно большим, но относительно малоэффективен для малых целых чисел.

Проверить, действительно ли проверка стоит, зависит от ожидаемых экспонентов, насколько важно получить максимальную производительность здесь, а также стоимость дополнительной сложности. По-видимому, Гвидо и остальная часть банды Python решили, что чек не стоит делать.

Если вам нравится, вы можете написать свою собственную функцию повторного умножения.

1
ответ дан David Thornley 27 August 2018 в 05:41
поделиться

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

Это зависит от конкретной реализации, если этот тип не задан языком.

Python не знает, какое распределение экспонентов вам он собирается его кормить. Если это будет 99% нецелых значений, вы хотите, чтобы код каждый раз проверял целое число, делая выполнение еще медленнее?

6
ответ дан Nosredna 27 August 2018 в 05:41
поделиться
  • 1
    Компилятор не смог выполнить эту проверку, так как он зависит от значения времени выполнения экспоненты. – David Thornley 19 June 2009 в 21:11
  • 2
    Я уточнил свой ответ относительно постоянных экспонентов. Спасибо Дэвиду. – Nosredna 19 June 2009 в 21:19
  • 3
    Я бы согласился с вами, если между ними не было значительной разницы в скорости, но x * x в несколько раз быстрее, чем x ** 2 для всех значений x, которые я пробовал. Конечно, добавление крошечной проверки для индекса int не стоило бы дорого и принесло бы большие улучшения производительности во многих случаях! – Christwo 19 June 2009 в 21:29
  • 4
    @Christwo. Я бы никогда не использовал x ** 2, если бы мог использовать x * x, так что это всегда навредило бы мне. Проверка может быть сделана вами вручную бесплатно для меня. – Nosredna 19 June 2009 в 21:32
  • 5
    Между 0 и 10, сколько целых чисел? Сколько плавает? Случайные поплавки редко бывают целыми числами. Таким образом, это действительно зависит от вашего конкретного использования для возведения в степень. Без доказательств того, что люди подают некоторый относительно большой процент ints в экспоненту, это просто дополнительный код и дополнительное время. – Nosredna 19 June 2009 в 21:35

как насчет x x x x x? это все еще быстрее, чем x ** 5?

, поскольку индексы int увеличиваются, причем полномочия могут быть быстрее, чем умножение. но число, в котором происходит фактический кроссовер, зависит от различных условий, поэтому, на мой взгляд, именно поэтому оптимизация не была выполнена (или не может быть выполнена) на уровне языка / библиотеки. Но пользователи могут по-прежнему оптимизировать некоторые специальные случаи:)

0
ответ дан wooohoh 27 August 2018 в 05:41
поделиться
  • 1
    Действительно, x2 = x * x; x3 = x2 * x; x5 = x2 * x3; (используя синтаксис Си, а не Python) использует одно меньшее умножение. – David Thornley 19 June 2009 в 21:12
  • 2
    Ага. это правда :) – wooohoh 19 June 2009 в 21:15
  • 3
    просто примечание. если скорость действительно важна, разработчики могут создать пул-таблицу при запуске или любые математические функции, если на то пошло ... вот что делают разработчики игр. – wooohoh 19 June 2009 в 21:20
  • 4
    @David, однако даже это разворачиваемое размножение рук на самом деле медленнее, чем вызов ** 5 (вероятно, главным образом потому, что он также оплачивает накладные расходы байт-кода python, а не выполняет всю работу на C). Compare & quot; timeit.Timer ('x ** 5', 'x = 10'). Timeit () & quot; с "timeit.Timer ('a = x * x; a * = a; a * = x', 'x = 10'). timeit ()". Я получаю ~ 18% медленнее для второго. – Brian 19 June 2009 в 22:35
Другие вопросы по тегам:

Похожие вопросы: