어제 목록 반전을 수행하는 여러 가지 다른 방법을 보여주기 위해리스트에 대해 두 가지 가능한 역기능을 작성했습니다. 그러나 분기 함수 (rev2
)를 사용하는 함수가 선형 함수 (rev1
)를 사용하는 함수보다 실제로 더 빠르다는 것을 알았습니다. 비록 분기 함수가 더 많은 호출을 끝내고 동일한 수의 호출 (빼기 하나)이 중요하지 않더라도 선형 재귀 함수의 부작용이없는 호출보다 (실제로는 계산 집약적입니다.) 명시 적으로 병렬 처리를 트리거하는 곳이 어디에도 없으므로 성능 차이는 어디에서 발생합니까? 더 많은 호출이 포함 된 함수가 더 짧은 시간 내에 수행됩니다.분기 재귀가 선형 재귀보다 더 빠른 이유 (예 : 목록 반전)
from sys import argv
from time import time
nontrivial_rev1_call = 0 # counts number of calls involving concatentation, indexing and slicing
nontrivial_rev2_call = 0 # counts number of calls involving concatentation, len-call, division and sclicing
length = int(argv[1])
def rev1(l):
global nontrivial_rev1_call
if l == []:
return []
nontrivial_rev1_call += 1
return rev1(l[1:])+[l[0]]
def rev2(l):
global nontrivial_rev2_call
if l == []:
return []
elif len(l) == 1:
return l
nontrivial_rev2_call += 1
return rev2(l[len(l)//2:]) + rev2(l[:len(l)//2])
lrev1 = rev1(list(range(length)))
print ('Calls to rev1 for a list of length {}: {}'.format(length, nontrivial_rev1_call))
lrev2 = rev2(list(range(length)))
print ('Calls to rev2 for a list of length {}: {}'.format(length, nontrivial_rev2_call))
print()
l = list(range(length))
start = time()
for i in range(1000):
lrev1 = rev1(l)
end = time()
print ("Average time taken for 1000 passes on a list of length {} with rev1: {} ms".format(length, (end-start)/1000*1000))
start = time()
for i in range(1000):
lrev2 = rev2(l)
end = time()
print ("Average time taken for 1000 passes on a list of length {} with rev2: {} ms".format(length, (end-start)/1000*1000))
예 전화 :
$ python reverse.py 996 calls to rev1 for a list of length 996: 996 calls to rev2 for a list of length 996: 995 Average time taken for 1000 passes on a list of length 996 with rev1: 7.90629506111145 ms Average time taken for 1000 passes on a list of length 996 with rev2: 1.3290061950683594 ms
각 호출은 목록을 복사하므로 * O (n) *에 복사가 발생합니다. –
하지만 두 기능 모두에서 그렇게합니다. 분기 버전은 여러 '병렬'호출을 통해로드를 분할합니다. –
두 루틴의 함수 호출 수를 계산하는 대신 각 루틴의 모든 호출에서 복사 된 총 목록 항목 수를 세어보십시오. –