파이썬은 반환 값으로 만 사용되는 변수를 최적화합니까?


106

다음 두 코드 스 니펫간에 궁극적 인 차이점이 있습니까? 첫 번째는 함수의 변수에 값을 할당 한 다음 해당 변수를 반환합니다. 두 번째 함수는 값을 직접 반환합니다.

파이썬은 그것들을 동등한 바이트 코드로 바꾸나요? 그들 중 하나가 더 빠릅니까?

사례 1 :

def func():
    a = 42
    return a

사례 2 :

def func():
    return 42

5
dis.dis(..)둘 다 사용 하면 차이있음 을 알 수 있으므로 그렇습니다. 그러나 대부분의 실제 응용 프로그램에서 함수 처리 지연에 비해이 오버 헤드는 그다지 많지 않습니다.
Willem Van Onsem 2017

4
두 가지 가능성이 있습니다. (a)이 함수를 타이트 루프에서 여러 번 (즉, 최소 백만 번) 호출 할 것입니다. 이 경우 Python 함수를 전혀 호출해서는 안되지만 대신 numpy 라이브러리와 같은 것을 사용하여 루프를 벡터화해야합니다. (b)이 함수를 그렇게 여러 번 호출하지 않을 것입니다. 이 경우 이러한 기능 간의 속도 차이가 너무 적어서 걱정할 가치가 없습니다.
Arthur Tacca 2011

답변:


138

아니요, 그렇지 않습니다 .

CPython 바이트 코드에 대한 컴파일은 기본 최적화 만 수행하도록 설계된 작은 구멍 최적화 프로그램 을 통해서만 전달됩니다 ( 이러한 최적화에 대한 자세한 내용은 테스트 스위트의 test_peepholer.py 참조 ).

실제로 일어날 일 dis을 살펴 보려면 *를 사용하여 생성 된 지침을 확인하십시오. 할당을 포함하는 첫 번째 함수의 경우 :

from dis import dis
dis(func)
  2           0 LOAD_CONST               1 (42)
              2 STORE_FAST               0 (a)

  3           4 LOAD_FAST                0 (a)
              6 RETURN_VALUE

두 번째 기능의 경우 :

dis(func2)
  2           0 LOAD_CONST               1 (42)
              2 RETURN_VALUE

두 개의 (빠른) 명령어가 첫 번째에 사용됩니다 : STORE_FASTLOAD_FAST. 이것들 fastlocals은 현재 실행 프레임 의 배열에있는 값을 빠르게 저장하고 잡습니다 . 그런 다음 두 경우 모두 a RETURN_VALUE가 수행됩니다. 따라서 두 번째는 실행하는 데 필요한 명령이 적기 때문에 약간 더 빠릅니다.

일반적으로 CPython 컴파일러는 수행하는 최적화에서 보수적 이라는 점에 유의하십시오 . 그것은하지 않고 일을 시도하지 않습니다 (일반적으로, 또한 작업에 더 많은 정보를 가지고) 다른 컴파일러 스마트한다. 명백히 올바른 것 외에 주요 디자인 목표는 a) 단순하게 유지하고 b) 컴파일 단계가 존재한다는 사실조차 알지 못하도록 최대한 신속하게 컴파일하는 것입니다.

결국, 이와 같은 사소한 문제로 고민해서는 안됩니다. 속도의 이점은 작고 일정하며 Python이 해석된다는 사실로 인해 발생하는 오버 헤드로 인해 왜소합니다.

* dis는 코드를 분해하는 작은 Python 모듈입니다.이를 사용하여 VM이 실행할 Python 바이트 코드를 볼 수 있습니다.

참고 : @Jorn Vernee의 주석에서도 언급했듯이 이것은 Python의 CPython 구현에만 해당됩니다. 다른 구현은 원하는 경우 더 적극적인 최적화를 수행 할 수 있지만 CPython은 그렇지 않습니다.


11
파이썬 사람 (C ++)이 아니므로 후드 아래에서 어떻게 작동하는지 모르겠지만 첫 번째 경우가 두 번째 경우에 최적화되지 않아야합니까? 괜찮은 C ++ 컴파일러는 그 최적화를 만들 것입니다.
NathanOliver 2017-04-13

7
@NathanOliver 정말 그렇지 않습니다, 파이썬은 똑똑하게 플레이하려고 시도하지 않고도 여기에 말한대로 할 것입니다.
Dimitris Fasarakis Hilliard

80
이 질문에 대한 답에 대한 @NathanOliver의 완벽하고 합리적이고 지능적인 추측이 완전히 틀렸다는 사실은 이것이 대답 할 수있는 "자기 설명 적", "말도 안되는", "어리석은"질문이 아니라는 증거입니다. TigerhawkT3가 우리가 믿게하는 것처럼 "생각하는 시간을 가지십시오". 수년간 전문적인 Python 프로그래머 였음에도 불구하고 답을 확신 할 수 없었던 타당하고 흥미로운 질문입니다.
Mark Amery

파이썬의 컴파일러는 '매우 보수적'이 아니라 기껏해야 '보수적'입니다. 주요 디자인 목표는 "가능한 한 신속하게 ... 그래서 컴파일 단계가 존재한다는 사실조차 알지 못하는 것"이 ​​아닙니다. 그것은 "간단하게 유지"한 후 부차적 인 것입니다. "1 << (2 ** 34)"및 "b'x '* (2 ** 32)"와 같이 큰 상수가있는 함수는 컴파일하고 GB 크기의 상수를 생성하는 데 몇 초가 걸립니다. 운영. 큰 문자열은 심지어 컴파일러에 의해 버려 질 것입니다. 이러한 경우에 제안 된 수정 사항은 컴파일러를 너무 복잡하게 만들 수 있으므로 거부되었습니다.
Andrew Dalke 17 년

@AndrewDalke 내부자 의견에 감사드립니다. 지적한 문제를 해결하기 위해 문구를 수정했습니다.
Dimitris Fasarakis Hilliard

3

둘 다 기본적으로 동일하지만 첫 번째 경우 객체 42가 단순히 이름이 지정된 변수에 할당 a되거나, 즉 이름 (즉 a)이 값 (예 42)을 참조 한다는 점을 제외하면 동일합니다 . 데이터를 복사하지 않는다는 점에서 기술적으로 어떤 할당도하지 않습니다.

returning 동안 이 명명 된 바인딩 a은 첫 번째 경우 42에 반환되고 개체 는 두 번째 경우에 반환됩니다.

자세한 내용은 Ned Batchelder의 훌륭한 기사를 참조하십시오.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.