Расчет времени короткого замыкания в Python дает неожиданные результаты - PullRequest
0 голосов
/ 11 июля 2020
import time as dt 

success = True
can_test = True 


time = 0

for i in range(10000000):
  start = dt.time()
  if success and can_test:
    stop = dt.time()
    time+= stop-start


print(f'"and" operation took: {time} seconds')


time = 0

for i in range(10000000):
  start = dt.time()
  if success or can_test:
    stop = dt.time()
    time += stop-start


print(f'"or" operation took: {time} seconds')

Когда я запускаю вышеуказанную программу python, я ожидаю, что операция and будет медленнее, чем операция or (потому что я узнал, что короткое замыкание сокращает время выполнения). Однако результат не только оказывается прямо противоположным, но и колеблется. Я понимаю колебания! (из-за фоновых процессов). Но почему результаты получаются противоположными! что происходит?

Вот пример результата.


"and" operation took: 5.200342893600464 seconds
"or" operation took: 5.3243467807769775 seconds

Ответы [ 2 ]

1 голос
/ 11 июля 2020

Это был интересный вопрос, поэтому я решил тщательно изучить вашу главную проблему.

# required modules line_profiler, matplotlib, seaborn abd scipy
import time as dt 
from line_profiler import LineProfiler
import matplotlib.pyplot as plt
import seaborn as sns
from scipy import stats

success = True
can_test = True 
def and_op():
    for x in range(2000):
        s = success and can_test
def or_op():
    for x in range(2000):
        s = success or can_test
or_op_list = []
for x in range(0,1000):
    lp = LineProfiler()
    lp_wrapper = lp(or_op)
    lp_wrapper()
    lstats = lp.get_stats()
    total_time = 0
    for v in lstats.timings.values():
        for op in v:
            total_time += op[-1]
            final = op[-1]
        operator = final/total_time
    or_op_list.append(operator)

and_op_list = []
for x in range(0,1000):
    lp = LineProfiler()
    lp_wrapper = lp(and_op)
    lp_wrapper()
    lstats = lp.get_stats()
    total_time = 0
    for v in lstats.timings.values():
        for op in v:
            total_time += op[-1]
            final = op[-1]
        operator = final/total_time
    and_op_list.append(operator)
sns.kdeplot(and_op_list, label = 'AND')
sns.kdeplot(or_op_list, label = 'OR')
plt.show()
print(stats.ttest_ind(and_op_list,or_op_list, equal_var = False))

введите описание изображения здесь

pvalue = 1.8293386245013954e-103

Действительно, «или» статистически значимо и отличается от операции «и»

0 голосов
/ 11 июля 2020

Когда я запускаю ваш код на своей машине, он иногда печатает, что True and True тоже быстрее, чем True or True.

Причина поскольку это явление состоит в том, что dt.time() в вашем коде измеряет время в шкале «микросекунды» (т.е. 1000 наносекунд), но это микросекундная шкала слишком мало, чтобы измерить время, затрачиваемое на каждое выполнение из if success and can_test: или if success or can_test:. В большинстве случаев время, затрачиваемое if success and can_test: или if success or can_test:, составляет меньше 1 микросекунды .

Таким образом, в частях вашего кода ниже:

for i in range(10000000):
   start = dt.time()
       if success and can_test:  # a dust particle
       stop = dt.time()
       time += stop - start  # measured by a normal scale ruler
for i in range(10000000):
   start = dt.time()
       if success or can_test:  # a dust particle
       stop = dt.time()
       time += stop - start  # measured by a normal scale ruler

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

Для дальнейшего исследования, если мы выполним код ниже (d записывает затраченное время с его частотой):

import time as dt
from pprint import pprint

success = True
can_test = True

time = 0
d = {}
for i in range(10000000):
    start = dt.time_ns()
    if success and can_test:  # a dust particle
        stop = dt.time_ns()
        diff_time = stop - start  # measurement by a normal scale ruler
        d[diff_time] = d.get(diff_time, 0) + 1
        time += diff_time
print(f'"and" operation took: {time} ns')
print('"and" operation time distribution:')
pprint(d)
print()

time = 0
d = {}
for i in range(10000000):
    start = dt.time_ns()
    if success or can_test:  # a dust particle
        stop = dt.time_ns()
        diff_time = stop - start  # measurement by a normal scale ruler
        d[diff_time] = d.get(diff_time, 0) + 1
        time += diff_time
print(f'"or" operation took: {time} ns')
print('"or" operation time distribution:')
pprint(d)

Он будет напечатан следующим образом:

"and" operation took: 1467442000 ns
"and" operation time distribution:
{0: 8565832,
 1000: 1432066,
 2000: 136,
 3000: 24,
 4000: 12,
 5000: 15,
 6000: 10,
 7000: 12,
 8000: 6,
 9000: 7,
 10000: 6,
 11000: 3,
 12000: 191,
 13000: 722,
 14000: 170,
 15000: 462,
 16000: 23,
 17000: 30,
 18000: 27,
 19000: 10,
 20000: 12,
 21000: 11,
 22000: 61,
 23000: 65,
 24000: 9,
 25000: 2,
 26000: 2,
 27000: 3,
 28000: 1,
 29000: 4,
 30000: 4,
 31000: 2,
 32000: 2,
 33000: 2,
 34000: 3,
 35000: 3,
 36000: 5,
 37000: 4,
 40000: 2,
 41000: 1,
 42000: 2,
 43000: 2,
 44000: 2,
 48000: 2,
 50000: 3,
 51000: 3,
 52000: 1,
 53000: 3,
 54000: 1,
 55000: 4,
 58000: 1,
 59000: 2,
 61000: 1,
 62000: 4,
 63000: 1,
 84000: 1,
 98000: 1,
 1035000: 1,
 1043000: 1,
 1608000: 1,
 1642000: 1}

"or" operation took: 1455555000 ns
"or" operation time distribution:
{0: 8569860,
 1000: 1428228,
 2000: 131,
 3000: 31,
 4000: 22,
 5000: 8,
 6000: 8,
 7000: 6,
 8000: 3,
 9000: 6,
 10000: 3,
 11000: 4,
 12000: 173,
 13000: 623,
 14000: 174,
 15000: 446,
 16000: 28,
 17000: 22,
 18000: 31,
 19000: 9,
 20000: 11,
 21000: 8,
 22000: 42,
 23000: 72,
 24000: 7,
 25000: 3,
 26000: 1,
 27000: 5,
 28000: 2,
 29000: 2,
 31000: 1,
 33000: 1,
 34000: 2,
 35000: 4,
 36000: 1,
 37000: 1,
 38000: 2,
 41000: 1,
 44000: 1,
 45000: 2,
 46000: 2,
 47000: 2,
 48000: 2,
 49000: 1,
 50000: 1,
 51000: 2,
 53000: 1,
 61000: 1,
 64000: 1,
 65000: 1,
 942000: 1}

И мы видим, что около 85,7% попыток для измерения времени (8565832 / 10000000 равно 0.8565832 и 8569860 / 10000000 равно 0.8569860) не удалось, так как было измерено только 0 наносекунд. И около 14,3% попыток измерить время (1432066 / 10000000 равно 0.1432066 и 1428228/10000000 равно 0.1428228) измеряют 1000 наносекунд. И, разумеется, остальные (менее 0,1%) попытки измерить время также привели к продаже 1000 наносекунд. Мы видим, что в масштабе микросекунд слишком мало для измерения времени, затрачиваемого на каждое выполнение .

Но мы все еще можем использовать линейку с нормальным масштабом . собрав вместе частицы пыли и используя линейку измерьте шар . Вместо этого мы можем попробовать следующий код:

import time as dt

success = True
can_test = True

start = dt.time()
for i in range(10000000):  # getting together the dust particles
    if success and can_test:  # a dust particle
        pass
stop = dt.time()
time = stop - start  # measure the size of the dustball
print(f'"and" operation took: {time} seconds')

start = dt.time()
for i in range(10000000):  # getting together the dust particles
    if success or can_test:  # a dust particle
        pass
stop = dt.time()
time = stop - start  # measure the size of the dustball
print(f'"or" operation took: {time} seconds')

Он будет напечатан следующим образом:

"and" operation took: 0.6261420249938965 seconds
"or" operation took: 0.48876094818115234 seconds

Или мы можем использовать тонкую линейку dt.perf_counter(), которая может точно измерить размер каждой частицы пыли , например:

import time as dt

success = True
can_test = True

time = 0
for i in range(10000000):
    start = dt.perf_counter()
    if success and can_test:  # a dust particle
        stop = dt.perf_counter()
        time += stop - start  # measured by a fine-scale ruler
print(f'"and" operation took: {time} seconds')

time = 0
for i in range(10000000):
    start = dt.perf_counter()
    if success or can_test:  # a dust particle
        stop = dt.perf_counter()
        time += stop - start  # measured by a fine-scale ruler
print(f'"or" operation took: {time} seconds')

Он будет напечатан следующим образом:

"and" operation took: 1.6929048989996773 seconds
"or" operation took: 1.3965214280016083 seconds

Конечно, True or True быстрее, чем True and True!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...