Ответственность за загрузку файлов класса несет 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 () во время компиляции. Так возникает ошибка компиляции.
В основном наивное умножение - O (n) с очень низким постоянным множителем. Взятие мощности O (log n) с более высоким постоянным множителем (Существуют специальные случаи, которые необходимо проверить ... дробные показатели, отрицательные показатели и т. Д.). Edit: просто для того, чтобы быть ясным, это O (n), где n - показатель степени.
Конечно, наивный подход будет быстрее при малом n, вы на самом деле реализуете небольшое подмножество экспоненциальной математики, поэтому ваш постоянный коэффициент пренебрежимо мал.
Реализация 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
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
», но работа над этим намного сложнее. Например. в моем примере это невозможно, так как любой объект может быть передан квадратной функции.
mul
и pow
, был бы пародией. Особенно для малых целых степеней, таких как pow = 2
. Такой класс заслуживает того, что с ним происходит.
– ToolmakerSteve
15 December 2013 в 07:21
Я подозреваю, что никто не ожидал, что это будет так важно. Обычно, если вы хотите делать серьезные вычисления, вы делаете их в Fortran или C или C ++ или что-то в этом роде (и, возможно, называете их с Python).
Рассмотрение всего как exp (n * log (x) ) хорошо работает в случаях, когда n не является интегральным или довольно большим, но относительно малоэффективен для малых целых чисел.
Проверить, действительно ли проверка стоит, зависит от ожидаемых экспонентов, насколько важно получить максимальную производительность здесь, а также стоимость дополнительной сложности. По-видимому, Гвидо и остальная часть банды Python решили, что чек не стоит делать.
Если вам нравится, вы можете написать свою собственную функцию повторного умножения.
Добавление чека также является расходами. Вы всегда этого хотите? Скомпилированный язык может сделать проверку для постоянного экспонента, чтобы узнать, является ли это относительно небольшим целым числом, потому что нет затрат времени исполнения, а просто затраты времени компиляции.
Это зависит от конкретной реализации, если этот тип не задан языком.
Python не знает, какое распределение экспонентов вам он собирается его кормить. Если это будет 99% нецелых значений, вы хотите, чтобы код каждый раз проверял целое число, делая выполнение еще медленнее?
как насчет x x x x x? это все еще быстрее, чем x ** 5?
, поскольку индексы int увеличиваются, причем полномочия могут быть быстрее, чем умножение. но число, в котором происходит фактический кроссовер, зависит от различных условий, поэтому, на мой взгляд, именно поэтому оптимизация не была выполнена (или не может быть выполнена) на уровне языка / библиотеки. Но пользователи могут по-прежнему оптимизировать некоторые специальные случаи:)