Python if~or 조건에서 "=="와 in의 성능 차이 및 측정
🤔 궁금한 점
if 조건문에서 or 연산을 할 때, "=="를 여러 개 하는 것과, in 중 어느 것이 성능이 더 좋을까? 코딩테스트 코드를 작성하다가 문득 궁금해졌다. 말만 들어서는 헷갈릴 수 있다. 문제 상황은 아래와 같다
# 첫 번째, "==" 조건 여러 개
if string == "(" or string == ")" or string == "[" or string == "]":
# 두 번째, in 사용 - 문자열에서 사용
if string in "()[]":
# 세 번째, in 사용 - 리스트에서 사용
if string in ['(',')','[',']']
평소엔 생각 없이 조건이 1개~2개면 1번을 방법을 사용했고, 비교대상이 많다면 두 번째 방법을 사용했다. 세 번째 방법은 번거로워서 안 썼다. 아무 생각 없이 코드를 쓰다가.. 문득 어느 것의 성능이 가장 좋은지 궁금해졌다.
🖥️ 각 방식의 성능 측정
어느 것이 가장 빠른지 성능을 측정해보자. 파이썬의 내장 라이브러리인 `timeit`를 사용한다. `timeit.timeit()`는 코드를 문자열로 입력받고, 해당 코드(문자열)를 실행하여 얼마나 걸리는지 시간을 측정하여 반환하는 함수이다. 여러 줄을 사용하려면 세미콜론(;)을 사용하면 된다.
import timeit
from timeit import timeit
# 조건에 포함 되지 않는 경우
print(timeit("s='1'; s == '(' or s == ')' or s == '[' or s == ']'")) # 0.0716015999969386
print(timeit("s='1'; s in '()[]'")) # 0.021519800000532996
print(timeit("s='1'; s in ['(',')','[',']']")) # 0.04798849999860977
# 조건에 포함 되는 경우
print(timeit("s=']'; s == '(' or s == ')' or s == '[' or s == ']'")) # 0.07150259999980335
print(timeit("s=']'; s in '()[]'")) # 0.023601100001542363
print(timeit("s=']'; s in ['(',')','[',']']")) # 0.04115779999847291
문자열 `s`가 조건에 포함되는 경우와 아닌 경우 모두를 측정해보았다.
✅ 결과 및 성능 차이 원인
각 방식에 대한 결과는 아래와 같다. 순서대로 소요시간이 `0.07`, `0.02`, `0.04`로 두 번째 방법이 가장 빠른 것을 알 수 있었다.
- `==` 여러 개(or 여러 개): 0.07
- `in str`: 0.02 ✅
- `in list`: 0.04
WHY❓
문자열 연산이 `0.02`로 가장 빠른 것을 확인할 수 있다. 그렇다면 왜일까? 이유는 아래와 같다
- `==` 연산을 활용한 경우: or 연산으로 변수 's'가 여러 번 등장한다. 즉, 변수 `s`의 주소를 `==` 연산이 발생할 때마다 찾아가야하므로 성능이 느려진다.
- `in str`: 파이썬에서 문자열을 탐색할때는 내부적으로 C에서 최적화된 루프로 동작한다. 즉, 문자열을 앞에서부터 한 글자식 탐색한다. 이 경우 시간복잡도는 `O(n)`이다.
- `in list`: 리스트를 비교할 경우, 마찬가지로 각 요소를 순차적으로 비교하여 시간복잡도가 `O(n)`이지만, 리스트 자료형은 포인터 비교 + 메서드 호출이 발생하므로 문자열 비교에 비해 오버헤드가 크다.
📖 보충 설명: 오버헤드와 문자열 탐색 최적화
✔️ 오버헤드란?
- 오버헤드(Overhead)는 추가적으로 드는 비용을 의미한다.
- 파이썬은 고수준 언어이며, 내부에서 많은 추상화 작업을 처리한다.
- `s in list` 연산을 하게 되면 다음과 같이 동작한다.
- 각 요소를 하나씩 꺼냄(`__iter__` 호출) ➡️ Overhead
- `==` 연산자를 통해 비교
- 비교할 때마다 파이썬 내부 메서드 호출 발생 ➡️ Overhead
- 이 일련의 과정을 오버헤드라고 한다. 즉, 단순히 "비교한다"라는 동작 외에 수행되는 부수적인 연산을 의미한다.
- 리스트 비교 구현 방식은 파이썬 루프 + 메서드 호출로, 객체단위 비교이며, 오버헤드가 발생한다
✔️ 문자열 탐색 최적화란?
- 문자열은 기본적으로 불변 객체(immutable)이다.
- 따라서, 내부 구현 시 추가적인 오버헤드 없이 단순 비교가 가능하다.
- 단순히 고정된 문자열을 앞에서 부터 순서대로 한 번만 순회하여 비교한다.
- 단순하고 빠른 연산으로, C로 구현된 루프에서 처리된다.
즉, 겨우 코드 한 줄이지만, 문자열 비교의 경우 C 코드에서 매우 단순한 루프로 처리되고, 리스트 비교의 경우 내부적으로 `for`루프 + 비교 함수 호출을 포함하는 무거운 처리를 한다!!
📌 정리
비교 항목 | 문자열 비교 (`s in str`) | 리스트 비교 (` s in list`) |
내부 구현 | C 루프 | 파이썬 루프 + 메서드 호출 |
처리 방식 | 문자 단위 순회 | 객체 단위 비교 |
성능 | 빠름 (최적화됨) | 느림 (오버헤드 존재) |
이유 | 불변 + 단순 구조 | 가변 + 동적 구조 |
✅ 결론
문자열 하나가 포함되어있는지 확인하는 용도라면, `in str` 방식이 가장 빠르다. 이는 파이썬의 문자열 탐색 방식이 내부적으로 C 코드로 구현된 단순비교로 처리되기 때문이다.
가장 간단한 것이 가장 빠르다! 파이썬 답다. 모두들 Pythonic 하게 코드를 짜보자 ^^!