В чем разница между следующими строками:
if not x == 'val':
Code language: JavaScript (javascript)
И
if x != 'val':
Code language: JavaScript (javascript)
Есть ли способ проверить, является ли одно из них более эффективным, чем другое?
Или, может быть, следующий блок будет лучше?
if x == 'val':
pass
else:
Code language: PHP (php)
Содержание
Ответы.
Ответ 1.
Разбираемся.
Если мы воспользуемся дизассемблером для Python (dis), то увидим, что все три компилируются в одинаковое количество инструкций, и отличаются только POP_JUMP_IF_TRUE или POP_JUMP_IF_FALSE.
!=
0 LOAD_FAST 0 (x)
3 LOAD_CONST 1 ('val')
6 COMPARE_OP 3 (!=)
9 POP_JUMP_IF_FALSE 15
Code language: JavaScript (javascript)
not ==
0 LOAD_FAST 0 (x)
3 LOAD_CONST 1 ('val')
6 COMPARE_OP 2 (==)
9 POP_JUMP_IF_TRUE 15
Code language: JavaScript (javascript)
== else
0 LOAD_FAST 0 (x)
3 LOAD_CONST 1 ('val')
6 COMPARE_OP 2 (==)
9 POP_JUMP_IF_FALSE 15
Code language: JavaScript (javascript)
Профиль
Используя cProfile, мы можем измерить время выполнения и провести сравнительный анализ:
ncalls tottime percall cumtime percall filename:lineno(function)
not == 5000000 1.589 0.000 1.589 0.000 ./prog.py:8(en)
== else 5000000 1.562 0.000 1.562 0.000 ./prog.py:12(ee)
!= 5000000 1.508 0.000 1.508 0.000 ./prog.py:4(ne)
Code language: JavaScript (javascript)
Отсортировав от самого медленного к самому быстрому, мы получим следующее:
- if not x == ‘val’ является самым медленным.
if x == 'val'
+else
на ~1,7% быстрее.- if x != ‘val’ на ~3,6% быстрее.
- Между самым медленным и самым быстрым мы экономим 81 мс.
Вывод
Условие if x != ‘val’ немного более эффективно.
Однако, на мой взгляд, разница не настолько существенна, чтобы менять стиль кодирования.
Я предпочитаю любой ценой сохранить код как можно более легко читаемым, и я бы подумал об учете этого результата только в чрезвычайно интенсивных вычислениях, требующих экономии каждой возможной доли секунды.
Ответ 2.
Все три делают одно и то же с точки зрения наблюдаемого поведения. Поэтому компилятор или интерпретатор python может сгенерировать/выполнить совершенно одинаковый код во всех трех случаях. В этом случае на выполнение потребуется точно такое же время, поскольку это один и тот же сгенерированный код. А наблюдаемые различия были бы шумом.
Может возникнуть соблазн разобрать сгенерированный код и увидеть, что он отличается во всех трех случаях. Но выводы, сделанные таким образом, будут применимы только к той версии python, с которой проводились тесты. Они не могут быть применимы в целом.
Давайте посмотрим, как использование двух разных методов выполнения этих инструкций дает разные результаты.
import time
def not_same():
if not x == 'val':
x
def different():
if x != 'val':
x
def same_else():
if x == 'val':
pass
else:
x
def test(tam):
start = time.time()
i = 0
while (i<tam) :
i = i + 1
end = time.time()
empty = end-start
start = time.time()
i = 0
while (i<tam) :
not_same()
i = i + 1
end = time.time()
print( 'Время not == ' + str(end-start-empty) )
start = time.time()
i = 0
while (i<tam) :
different()
i = i + 1
end = time.time()
print( 'Время != ' + str(end-start-empty) )
start = time.time()
i = 0
while (i<tam) :
same_else()
i = i + 1
end = time.time()
print( 'Время == else ' + str(end-start-empty) )
x='val'
test(10000000)
Code language: PHP (php)
При запуске с помощью интерпретатора python были получены следующие результаты:
Время== 1.71001195908
Время!= 1.9246609211
Время== else 1.75712680817
Code language: JavaScript (javascript)
И если я запускаю его несколько раз, то получаю похожие результаты. Кажется, что победитель not ==.
Теперь я компилирую его в C с помощью cpython. А код на C я компилирую в двоичный файл ELF для linux, который я запускаю
diam@HP:~/t$ gcc -O2 -I /usr/include/python3.5m -o a a.c -lpython3.5m -lpthread -lm -lutil -ldl
diam@HP:~/t$ ./a
Время== 0.6007065773010254
Время!= 0.6298408508300781
Время== else 0.6232995986938477
diam@HP:~/t$ ./a
Время== 0.5901763439178467
Время!= 0.6861095428466797
Время== else 0.6084094047546387
diam@HP:~/t$ ./a
Время== 0.6090543270111084
Время!= 0.6182305812835693
Время== else 0.6077189445495605
Code language: JavaScript (javascript)
Теперь иногда побеждает один, а иногда другой.
Но я могу получить разные результаты не только в разных средах выполнения. При одинаковой среде выполнения результаты могут быть разными в зависимости от входных данных. Давайте изменим строку x=’val’ на x=’xxx’ и запустим снова с помощью команды :
diam@HP:~/t$ gedit a.py diam@HP:~/t$ python a.py Время not == 2.22967505455 Время != 2.49930405617 Время == else 2.18658304214
Теперь побеждает == else.
Когда я запускаю его несколько раз, этот результат повторяется. А я запускаю его с точно такой же системой, где раньше победитель был not ==, изменились только входные данные.
Ни один из предыдущих бенчмарков не подходит.
Во всех тестах значение x всегда одинаково от начала до конца теста.
Следует проводить тестирование на случайных данных или, что еще лучше, на данных, полученных из ситуации, аналогичной той, в которой, как ожидается, будет работать наша программа.