назначение подкачки медленнее, чем введение новой переменной? - PullRequest
0 голосов
/ 18 июня 2020

Хорошо, надеюсь, название имело какой-то смысл. Я сделал код быстрого преобразования Фурье и хочу посмотреть, как он может быть на go быстрее. Итак, вот код ('code1'), в котором я выполняю замену, как диктует БПФ:

stp='''
import numpy as nn
D=2**18
N=12
w=(nn.linspace(0,D-1,D)%2**N<(2**N/2))-.5 #square wave
f=w[0:2**N]+nn.zeros(2**N)*1j #it takes only one wavelength from w
'''
code1='''
for m in range(N):
    for l in range(2**m):
        for k in range(2**(N-1-m)):
            t=f[k+l*2**(N-m)]
            f[k+l*2**(N-m)]=(t+f[k+l*2**(N-m)+2**(N-1-m)])
            f[k+l*2**(N-m)+2**(N-1-m)]=(t-f[k+l*2**(N-m)+2**(N-1-m)])*nn.e**(-2*nn.pi*1j*k*2**(m)/2**N)
'''
print(timeit.timeit(setup=stp,stmt=code1,number=1))

, где я ввожу новую переменную 't'. Это выводит время 0,132.

Итак, я подумал, что он должен go быстрее, если бы я сделал (настройка такая же, как и раньше):

code2='''
for m in range(N):
    for l in range(2**m):
        for k in range(2**(N-1-m)):
            f[k+l*2**(N-m)],f[k+l*2**(N-m)+2**(N-1-m)]=f[k+l*2**(N-m)]+f[k+l*2**(N-m)+2**(N-1-m)],(f[k+l*2**(N-m)]-f[k+l*2**(N-m)+2**(N-1-m)])*nn.e**(-2*nn.pi*1j*k*2**(m)/2**N)
'''
print(timeit.timeit(setup=stp,stmt=code2,number=1))

, так как теперь я выполняю два назначения вместо из трех (я так думал). Но похоже, что на самом деле это медленнее (0,152). У кого-нибудь есть идея, почему? И знает ли кто-нибудь способ сделать эту замену быстрее, чем t=a,a=f(a,b),b=g(t,b), который я представил ранее, поскольку мне трудно поверить, что это наиболее эффективный способ.

EDIT: добавлен фактический код вместо псевдокод.

БОЛЬШЕ РЕДАКТИРОВАНИЯ: я пробовал запустить то же самое, не используя Numpy. Оба они быстрее, так что это положительно, но снова метод t=a,a=f(a,b),b=g(t,b) оказывается быстрее (0,104), чем метод a,b=f(a,b),g(a,b) (0,114). Так что загадка остается.

новый код:

stpsansnumpy='''
import cmath as mm
D=2**18
N=12
w=[0]*D
for i in range(D):
    w[i]=(i%2**N<(2**N/2))-.5 #square wave
f=w[0:2**N]+[0*1j]*2**N #it takes only one wavelength from w
'''
code1math='''
for m in range(N):
    for l in range(2**m):
        for k in range(2**(N-1-m)):
            t=f[k+l*2**(N-m)]
            f[k+l*2**(N-m)]=(t+f[k+l*2**(N-m)+2**(N-1-m)])
            f[k+l*2**(N-m)+2**(N-1-m)]=(t-f[k+l*2**(N-m)+2**(N-1-m)])*mm.exp(-2*mm.pi*1j*k*2**(m)/2**N)
'''
print(timeit.timeit(setup=stpsansnumpy,stmt=code1math,number=1))

и:

code2math='''
for m in range(N):
    for l in range(2**m):
        for k in range(2**(N-1-m)):
            f[k+l*2**(N-m)],f[k+l*2**(N-m)+2**(N-1-m)]=f[k+l*2**(N-m)]+f[k+l*2**(N-m)+2**(N-1-m)],(f[k+l*2**(N-m)]-f[k+l*2**(N-m)+2**(N-1-m)])*mm.exp(-2*mm.pi*1j*k*2**(m)/2**N)
'''
print(timeit.timeit(setup=stpsansnumpy,stmt=code2math,number=1))

Ответы [ 2 ]

3 голосов
/ 18 июня 2020

Было бы неплохо, если бы вы поделились, почему, по вашему мнению, один медленнее другого, и если бы ваш код был правильно отформатирован как Python код должен быть.

Примерно так:

from timeit import timeit


def f(a, b):
    return a


def g(a, b):
    return a


def extra_var(a, b):
    t = a
    a = f(a, b)
    b = g(t, b)
    return a, b


def swap_direct(a, b):
    a, b = f(a, b), g(a, b)
    return a, b


print(timeit(lambda: extra_var(1, 2)))
print(timeit(lambda: swap_direct(1, 2)))

Однако, если бы вы это сделали, вы, вероятно, нашли бы те же результаты, что и я:

0.2162299
0.21171479999999998

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

Итак, вы бы увеличили громкость:

print(timeit(lambda: extra_var(1, 2), number=10000000))
print(timeit(lambda: swap_direct(1, 2), number=10000000))

И загадка уходит:

2.1527828999999996
2.1225841

Прямая замена на самом деле немного быстрее , как и ожидалось. Чем отличается то, что вы делали, что давало вам другие результаты?

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

В большинстве случаев оказывается, что кто-то ошибся, и все идет так, как ожидалось. В некоторых случаях вы получите интересный ответ.

1 голос
/ 18 июня 2020

В первой версии я вижу 5 операций индексации, а во второй - 6. Я не удивлен, что 6 операций индексации (со всеми используемыми в них вычислениями) дороже, чем 5.

Создание временной переменной или создание временного кортежа - пустяки по сравнению со всеми вычислениями, которые вы выполняете в этих фрагментах кода.

...