결론
시프트연산이 더 빠릅니다. (미미하게)
시프트 연산은 int형이 32bits이므로 사용하는데 제한이 있습니다. 물론 언어별로 사용할 수 있는 방법이 상이한 것으로 알고 있습니다.
필요한 상황에 따라, 본인 스타일에 따라 적절히 사용하시면 되겠습니다.
Python 디스어셈블러
내부적으로 어떻게 다른지 간단하게 살펴보겠습니다.
코드는 위의 소스코드를 사용했으며, 나누기 연산을 호출할 때와 시프트를 호출할 때 디스어셈블러로 살펴보았습니다.
차이점은 BINARY_TRUE_DIVIDE와 BINARY_RSHIFT를 호출 하는 것입니다.
내부적인 동작을 살펴보려 했으나 자세한 내용이 설명된 DOC이 없어서 생략하겠습니다.
결론적으로 파이썬3(3.6~3.8)에선 별도의 최적화 과정은 없었습니다.
C++ 디스어셈블러
이번엔 C++ 10.2 버전을 살펴보겠습니다.
int div(int num) {
return num/2;
}
int shift(int num){
return num>>1;
}
Python3와 마찬가지로 나누기와 시프트 연산의 내부 구조가 다른 것을 볼 수 있습니다.
Python3에서는 BINARY_TRUE_DIVIDE와 BINARY_RSHIFT만 보였고 doc을 찾아봐도 자세한 정보는 알 수 없었으나
역시 C++은 적나라하게 보여주는군요.
나누기 함수에서 7번째 줄 shr이 보이시나요?
이 어셈블리어는 SHIFT를 호출하는 것 입니다. 시프트연산과 동일한 과정을 수행하지만 결국 추가적인 명령을 수행하는 것을 볼 수 있습니다.
shr : 부호가 없는 연산 (오른쪽으로 시프트)
sar : 부호가 있는 연산 (오른쪽으로 시프트)
만약, 부호가 없다면(unsigned int) 시프트와 나누기 연산은 내부적으로 완전히 동일한 기능을 수행합니다.
위 예시 코드에서 결론은 shift 연산과 동일한 과정이지만, 추가적인 명령을 수행한다고 볼 수 있습니다.
VSCode에서 Python3 테스트
이제 나누기 연산과 시프트 연산의 시간 차이를 눈으로 확인해 보겠습니다.
import time
count = 100000000
avg = [0]*100
for i in range(100):
value = 10000000
start = time.time_ns()
for _ in range(count):
value/=2
div = time.time_ns()-start
value = 10000000
start = time.time_ns()
for _ in range(count):
value>>=1
shift = time.time_ns()-start
avg[i] = div-shift
print(avg)
print(sum(avg)/100)