파이썬의 pop 연산 속도 비교
파이썬에서 list
를 사용시에 가장 오른쪽에 있는 요소를 제거하는 방법은 여러 가지가 있다.
list = list[:-1]
: 슬라이스를 사용해서 끝의 원소를 제거한 리스트를 반환받는 것이다.list.pop()
: 가장 끝에 있는 원소를pop
하는 것이다.
실험
50000개의 요소를 가진 리스트를 리스트가 비워질 때까지 계속 끝의 원소를 제거하고 그 시간을 측정한다.
실험 전 생각
아마 pop
을 사용하는 방법이 가장 빠를 것으로 예상된다. 1번처럼 슬라이스를 사용하여 반환받을 경우 리스트를 새로 대입해야하기 때문이다.
1
2
import time
LIST_RANGE = 50000
1
2
3
4
5
6
7
8
9
# list.pop()
test = [i for i in range(LIST_RANGE)]
start = time.time()
while test:
test.pop()
end = time.time()
print(f'result: {end-start}')
>>> result: 0.011001348495483398
1
2
3
4
5
6
7
8
9
# list = list[:-1]
test = [i for i in range(LIST_RANGE)]
start = time.time()
while test:
test = test[:-1]
end = time.time()
print(f'result: {end-start}')
>>> result: result: 5.4392759799957275
여러 번 계산 후에 평균을 내려고 했으나 너무 오래걸려서 그냥 한번만 돌렸다. 그래도 결과는 너무 자명했다. 끝의 원소를 inplace
로 제거하고싶을 경우 절대로 슬라이스를 쓰지말고 pop()
를 쓰자.
번외 실험
deque
가 훨씬 빠른 것은 파이썬의 큐 모듈 속도 비교에서 해 보았다시피 deque
가 훨씬 빨랐다. 그렇다면 list
를 deque
에 넣고 모두 pop
하는 것과 list
를 pop
하는 것중 무엇이 더 빠를까?
실험
$2^k$ 개의 원소를 집어넣은 list
와 해당 list
를 deque
에 넣고 모두 빼는 연산의 속도를 비교한다. $k \in {8,9, 10, 11,12 }$
각 실험은 10번의 평균값을 낸다.
실험 전 생각
list.pop
이 빠를 것 같지만 pop
만 보면 deque
가 더 빠르기 때문에 요소 수에 따라 결과가 달라질 수도 있다는 생각을 했다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
import time
from collections import deque
import numpy as np
k_list = [1 << i for i in range(8, 13)]
K_LENGTH = len(k_list)
ITER = 10
only_list_time = np.zeros((K_LENGTH, ITER))
for k_idx, k in enumerate(k_list):
for i in range(ITER):
test = [i for i in range(k)]
start = time.time()
while test:
test.pop()
end = time.time()
only_list_time[k_idx][i] = start-end
result_only_list = only_list_time.mean(axis=1)
list_deque_time = np.zeros((K_LENGTH, ITER))
for k_idx, k in enumerate(k_list):
for i in range(ITER):
test = [i for i in range(k)]
start = time.time()
while test:
test = deque(test)
test.pop()
end = time.time()
list_deque_time[k_idx][i] = start-end
result_list_deque = list_deque_time.mean(axis=1)
for i in range(K_LENGTH):
only_list = result_only_list[i]
list_deque = result_list_deque[i]
if only_list > list_deque:
print(f'only list is {list_deque/only_list:.3f}% fast')
elif only_list < list_deque:
print(f'only list is {only_list/list_deque:.3f}% fast')
else:
print('same speed')
[RESULT]
1
2
3
4
5
only list is -inf% fast
only list is -inf% fast
only list is 33.014% fast
only list is 97.508% fast
only list is 102.678% fast
더 빠른 곳에 대입하지 않고 바로 pop
하는 것이 무조건 빠르다.
다른 곳에 대입하는 것이 생각보다 엄청난 연산을 요하는 것 같다. 많은 연산시 더 빨라지지 않을 까 했는데 더 많을 경우 더 많은 수를 옮겨야 하기 때문에 훨씬 더 오래걸렸다.