다른 날 파이썬 벤치마킹을하던 중 흥미로운 것을 발견했습니다. 다음은 거의 동일한 작업을 수행하는 두 개의 루프입니다. 루프 1은 실행하는 데 루프 2의 약 두 배가 걸립니다.
루프 1 :
int i = 0
while i < 100000000:
i += 1
루프 2 :
for n in range(0,100000000):
pass
첫 번째 루프가 왜 그렇게 느린가요? 나는 그것이 사소한 예라는 것을 알고 있지만 그것은 내 관심을 불러 일으켰습니다. 같은 방식으로 변수를 증가시키는 것보다 더 효율적으로 만드는 range () 함수에 특별한 것이 있습니까?
답변
파이썬 바이트 코드의 분해를 참조하면 더 구체적인 아이디어를 얻을 수 있습니다.
while 루프 사용 :
1 0 LOAD_CONST 0 (0)
3 STORE_NAME 0 (i)
2 6 SETUP_LOOP 28 (to 37)
>> 9 LOAD_NAME 0 (i) # <-
12 LOAD_CONST 1 (100000000) # <-
15 COMPARE_OP 0 (<) # <-
18 JUMP_IF_FALSE 14 (to 35) # <-
21 POP_TOP # <-
3 22 LOAD_NAME 0 (i) # <-
25 LOAD_CONST 2 (1) # <-
28 INPLACE_ADD # <-
29 STORE_NAME 0 (i) # <-
32 JUMP_ABSOLUTE 9 # <-
>> 35 POP_TOP
36 POP_BLOCK
루프 본체에는 10 개의 op가 있습니다.
사용 범위 :
1 0 SETUP_LOOP 23 (to 26)
3 LOAD_NAME 0 (range)
6 LOAD_CONST 0 (0)
9 LOAD_CONST 1 (100000000)
12 CALL_FUNCTION 2
15 GET_ITER
>> 16 FOR_ITER 6 (to 25) # <-
19 STORE_NAME 1 (n) # <-
2 22 JUMP_ABSOLUTE 16 # <-
>> 25 POP_BLOCK
>> 26 LOAD_CONST 2 (None)
29 RETURN_VALUE
루프 본문에는 3 개의 작업이 있습니다.
C 코드를 실행하는 시간은 정수기보다 훨씬 짧으며 무시할 수 있습니다.
답변
range()
C로 구현되지만 i += 1
해석됩니다.
사용하면 많은 xrange()
수의 경우 훨씬 더 빠르게 처리 할 수 있습니다. Python 3.0부터는 range()
이전과 동일합니다 xrange()
.
답변
while 루프에서 많은 객체 생성 및 파괴가 진행되고 있다고 말해야합니다.
i += 1
와 같다:
i = i + 1
그러나 Python int는 불변이기 때문에 기존 객체를 수정하지 않습니다. 오히려 그것은 새로운 가치를 가진 새로운 물건을 창조합니다. 기본적으로 다음과 같습니다.
i = new int(i + 1) # Using C++ or Java-ish syntax
가비지 수집기는 또한 많은 양의 정리 작업을 수행해야합니다. “객체 생성은 비싸다”.
답변
인터프리터에서 C로 작성된 코드로 더 자주 실행되기 때문입니다. 즉, i + = 1은 Python에 있으므로 (비교적으로) 느리지 만 range (0, …)은 하나의 C 호출이며 for 루프는 대부분 C에서도 실행됩니다.
답변
대부분의 Python의 내장 메서드 호출은 C 코드로 실행됩니다. 해석해야하는 코드는 훨씬 느립니다. 메모리 효율성과 실행 속도 측면에서 그 차이는 엄청납니다. 파이썬 내부는 극도로 최적화되었으며 이러한 최적화를 활용하는 것이 가장 좋습니다.
답변
여기에 대한 대답은 다른 대답이 제안하는 것보다 조금 더 미묘하다고 생각하지만 그 요점은 정확하지만 for 루프는 더 많은 작업이 C에서 발생하고 Python에서는 덜 발생하기 때문에 더 빠릅니다 .
보다 구체적으로, for 루프의 경우 C에서 while 루프에서 Python으로 처리되는 두 가지 일이 발생합니다.
-
while 루프에서는 비교
i < 100000000
가 Python에서 실행되는 반면, for 루프에서는 작업이의 반복자에 전달되어range(100000000)
내부적으로 C에서 반복 (따라서 경계 검사)을 수행합니다. -
while 루프에서 루프 업데이트
i += 1
는 Python에서 발생하는 반면 for 루프에서는 다시range(100000000)
C로 작성된 의 반복자가i+=1
(또는++i
)을 수행합니다.
차이를 확인하기 위해 수동으로 다시 추가하여 for 루프를 더 빠르게 만드는 것이이 두 가지의 조합임을 알 수 있습니다.
import timeit
N = 100000000
def while_loop():
i = 0
while i < N:
i += 1
def for_loop_pure():
for i in range(N):
pass
def for_loop_with_increment():
for i in range(N):
i += 1
def for_loop_with_test():
for i in range(N):
if i < N: pass
def for_loop_with_increment_and_test():
for i in range(N):
if i < N: pass
i += 1
def main():
print('while loop\t\t', timeit.timeit(while_loop, number=1))
print('for pure\t\t', timeit.timeit(for_loop_pure, number=1))
print('for inc\t\t\t', timeit.timeit(for_loop_with_increment, number=1))
print('for test\t\t', timeit.timeit(for_loop_with_test, number=1))
print('for inc+test\t', timeit.timeit(for_loop_with_increment_and_test, number=1))
if __name__ == '__main__':
main()
나는 숫자 100000000 리터럴 상수와 N
더 일반적인 변수 인 변수 로 이것을 시도했습니다 .
# inline constant N
while loop 3.5131139
for pure 1.3211338000000001
for inc 3.5477727000000003
for test 2.5209639
for inc+test 4.697028999999999
# variable N
while loop 4.1298240999999996
for pure 1.3526357999999998
for inc 3.6060175
for test 3.1093069
for inc+test 5.4753364
보시다시피 두 경우 모두 while
시간은 for inc+test
및 의 차이에 매우 가깝습니다 for pure
. 또한 N
변수를 사용하는 경우 while
의 값을 반복적으로 조회하기 위해 추가 속도가 느려지 N
지만 for
그렇지 않습니다.
이러한 사소한 수정으로 인해 코드 속도가 3 배 이상 빨라진다 는 사실은 정말 미쳤습니다 .하지만 그것은 Python입니다. 그리고 루프를 통해 빌트인을 사용할 수있을 때 시작하지 마십시오 ….