str과 list의 문자열 인덱스 참조 속도 비교
경로 찾기같은 문제에서 격자형 공간이 주어질 때가 있다.
그럴 경우 입력형태는 보통 두 가지 형태가 있다.
1
2
3
4
5
6
7 8 5 5 2 7
7 1 9 6 2 3
4 4 8 7 3 4
7 8 1 4 9 6
4 6 6 5 6 2
9 0 4 9 6 7
1
2
3
4
5
6
785527
719623
448734
781496
466562
904967
첫 번째 같이 공백으로 값이 구분된 경우 split
을 하면 되지만 두번째 같은 경우는 리스트를 List[List[Any]]
식으로 저장할지 List[str]
와 같이 저장할지 고민이 될 때가 있다. (int
로 변환하지 않는다고 하자)
그리하여 str
을 직접 참조하는 것과, 리스트로 바꾼 후에 참조하는 것 중 무엇이 더 빠른지 비교하는 코드를 작성하였다.
독립변수(변화시키는 것)는 다음과 같다.
- 배열의 크기
- 배열의 차원(1차원, 2차원)
- 참조 횟수
- 리스트 참조, 문자열 참조
실험 방식은 다음과 같다.
- 배열(문자열)을 선언한다.
- 리스트로 참조할 경우 해당 배열을 리스트로 감싸서 리스트 객체로 만든다.
- 인덱스를 이용하여 객체를 참조하고 언더바 변수(무의미)
_
에 저장한다. - 3번을 지정된 횟수만큼 반복한다.
먼저 1차원을 살펴보자
Base 10 | Symbol (name) |
---|---|
$10^{−3}$ | m (milli) |
$10^{−6}$ | µ (micro) |
$10^{−9}$ | n (nano) |
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
# --------------------
L = 100
array = "1"*L
# --------------------
for i in range(L):
_ = array[i]
# --------------------
>>> 5.61 µs ± 486 ns per loop
# --------------------
t_list = list(array)
for i in range(L):
_ = t_list[i]
# --------------------
>>> 6.17 µs ± 2.11 µs per loop
# --------------------
for a in range(100):
for i in range(L):
_ = array[i]
# --------------------
>>> 623 µs ± 110 µs per loop
# --------------------
t_list = list(array)
for a in range(100):
for i in range(L):
_ = t_list[i]
# --------------------
>>> 389 µs ± 6 µs per loop
# --------------------
실험 결과는 다음과 같다.
- 요소를 한 번씩만 참조할 경우 리스트로 변환하지 않고 직접 문자열을 참조하는 것이 더 빠르다.
- 참조 횟수가 많을 경우 리스트로 변환하는 것이 리스트로 변환하는 시간을 상쇄할 정도로 빠르다.
배열의 크기를 늘려도 이 추세가 유지되는지 확인해보자.
각 셀별로 %%timeit
을 활용해서 시간을 측정하였다. 셀은 주석창으로 구분되며 각 셀의 %%timeit
은 가독성을 위해 지웠다.
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
# --------------------
L = 10000
array = "1"*L
# --------------------
for i in range(L):
_ = array[i]
# --------------------
>>> 835 µs ± 290 µs per loop
# --------------------
t_list = list(array)
for i in range(L):
_ = t_list[i]
# --------------------
>>> 560 µs ± 9.67 µs per loop
# --------------------
for a in range(100):
for i in range(L):
_ = array[i]
# --------------------
>>> 83.9 ms ± 27.2 ms per loop
# --------------------
t_list = list(array)
for a in range(100):
for i in range(L):
_ = t_list[i]
# --------------------
>>> 48.3 ms ± 720 µs per loop
# --------------------
앞선 실험보다 다른 결과가 있는데 한 번만 참조할 때도 리스트가 더 빨랐다는 것이다. 이로써 다음 결과를 도출하였다.
- 문자열을 리스트로 바꾸는 것은 문자열을 하나씩 인덱싱하는 것에 비해 시간 복잡도가 훨씬 낮다.
2차원에서도 이 결과가 유지되는지 확인해보자
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
# --------------------
L = 10
array = ["1"*L for _ in range(L)]
# --------------------
for r in range(L):
for c in range(L):
_ = array[r][c]
# --------------------
>>> 10.8 µs ± 3.27 µs per loop
# --------------------
t_list = [list(array[i]) for i in range(L)]
for r in range(L):
for c in range(L):
_ = t_list[r][c]
# --------------------
>>> 10.2 µs ± 94.1 ns per loop
# --------------------
for i in range(100):
for r in range(L):
for c in range(L):
_ = array[r][c]
# --------------------
>>> 1.06 ms ± 344 µs per loop
# --------------------
t_list = [list(array[i]) for i in range(L)]
for i in range(100):
for r in range(L):
for c in range(L):
_ = t_list[r][c]
# --------------------
>>> 906 µs ± 272 µs per loop
# --------------------
이전 실험에서의 가설과 실험결과가 일치한다. 배열의 크기를 늘려보자
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
# --------------------
L = 100
array = ["1"*L for _ in range(L)]
# --------------------
for r in range(L):
for c in range(L):
_ = array[r][c]
# --------------------
>>> 683 µs ± 13.9 µs per loop
# --------------------
t_list = [list(array[i]) for i in range(L)]
for r in range(L):
for c in range(L):
_ = t_list[r][c]
# --------------------
>>> 753 µs ± 225 µs per loop
# --------------------
for i in range(10):
for r in range(L):
for c in range(L):
_ = array[r][c]
# --------------------
>>> 6.96 ms ± 116 µs per loop
# --------------------
t_list = [list(array[i]) for i in range(L)]
for i in range(10):
for r in range(L):
for c in range(L):
_ = t_list[r][c]
# --------------------
>>> 5.61 ms ± 127 µs per loop
# --------------------
2차원의 경우 2차원 리스트를 구성하는 데 좀 더 많은 시간이 걸린 것을 확인할 수 있다.
이로써 다음과 같은 결과를 도출하였다.
List[str]
->List[List[str]]
변환시 속도는 ‘열’의 개수보다 ‘행’의 개수에 좌우된다.- 참조 횟수가 적을 경우 리스트와 문자열의 참조 속도를 비교하기 힘들다 왜냐하면 리스트의 경우 문자열을 리스트로 바꾸는 과정이 존재하기 때문이다.
- 참조 횟수가 많을 경우 무조건 리스트로 바꾸는 것이 유리하다.