동적으로 유형이 지정된 언어의 컴파일러 작성시 입력과 관련된 문제는 무엇입니까?


9

에서 이 이야기 , 귀도 반 로섬 (Guido van Rossum)는 말에 주석, 파이썬 코드에 대한 컴파일러를 작성하는 시도에 대한 이야기 (27:30)입니다 :

멋진 동적 타이핑 속성을 모두 유지하고 프로그램의 의미 론적 정확성을 유지하는 컴파일러를 작성하는 것은 쉽지 않습니다. 커버 아래에서 어딘가에서 실제로 수행하고 실제로 실행하는지에 상관없이 실제로 동일한 작업을 수행합니다. 더 빠른

파이썬과 같이 동적으로 유형이 지정된 언어의 컴파일러 작성시 입력과 관련된 (가능한) 문제는 무엇입니까?


이 경우 동적 입력이 가장 큰 문제는 아닙니다. 파이썬의 경우 동적 범위 지정입니다.
SK-logic

다른 사람들이 플랫폼에 역동적 인 타이핑을하는 것이 정답이라고 주장 할 가치가 있습니다. Microsoft는 이러한 이유로 DLR 에 많은 돈을 투자했습니다. NeXT / Apple은 수십 년 동안 그 반 마차에 반쯤있었습니다. CPython에는 도움이되지 않지만 IronPython은 효과적으로 정적으로 파이썬을 컴파일 할 수 있음을 증명하고 PyPy는 그럴 필요가 없음을 증명합니다.
abarnert

2
@ SK-logic Python에서 동적 범위 지정? 마지막으로, 언어의 모든 구문은 어휘 범위를 사용합니다.

1
@ SK-logic 코드를 동적으로 생성하고 실행할 수 있지만 해당 코드는 어휘 범위가있는 코드도 실행됩니다. Python 프로그램의 모든 단일 변수에 대해 AST를 검사하여 변수가 속하는 범위를 쉽게 결정할 수 있습니다. 3.0 이후로 사라진 exec성명서에 대해 생각할 수도 있습니다 . 예를 들어 주시겠습니까? "동적 범위 지정"에 대한 정의가 [내와 다름] (en.wikipedia.org/wiki/Dynamic_scoping) 인 경우

1
@ SK-logic 저에게 구현 세부 사항이있는 유일한 것은 locals()호출에서 지속 값을 반환 하는 것 locals입니다. 문서화되었지만 구현 세부 사항이 아닌 것은 각 변수가 조회되는 범위를 고르지 locals않거나 globals변경할 수 없다는 것입니다. 변수를 사용할 때마다 참조되는 범위는 정적으로 결정됩니다. 그것은 확실히 어휘 범위를 결정합니다. (그리고 btw, eval그리고 exec분명히 구현 세부 사항도 아닙니다-내 대답을보십시오!)

답변:


16

당신은 당신의 질문을 표현할 때 귀도의 진술을 지나치게 단순화했습니다. 문제는 동적 형식 언어의 컴파일러를 작성하지 않습니다. 문제는 (기준 1) 항상 정확하고 (기준 2) 동적 타이핑을 유지 하며 (기준 3)은 상당한 양의 코드에 대해 눈에 띄게 빠른 것을 작성하는 것입니다.

Python의 90 % (실패 기준 1)를 쉽게 구현하고 일관되게 빠릅니다. 마찬가지로 정적 타이핑 (실패 기준 2)으로 더 빠른 Python 변형을 쉽게 만들 수 있습니다. 100 %를 구현하는 것도 쉽지만 (복잡한 언어를 구현하는 것은 쉽지만), 지금까지 구현하는 모든 쉬운 방법은 상대적으로 느립니다 (실패 기준 3).

인터프리터 와 JIT 가 올바른 JIT 를 구현하고 전체 언어를 구현하며 일부 코드가 더 빠를지라도 더 빠르지 만 (PyPy 참조) JIT 컴파일러 생성을 자동화하는 경우에만 가능합니다 (Psyco는없이 수행했습니다) 그러나 속도를 높일 수있는 코드는 매우 제한적입니다). 그러나 우리가 정적 에 대해 이야기하고 있기 때문에 이것은 명시 적으로 범위를 벗어났습니다.(일명 사전 컴파일러) 컴파일러. 정적 컴파일러에서 왜 그 접근 방식이 작동하지 않는지 설명하기 위해 이것을 언급합니다 (또는 적어도 기존의 반대 사례가 없습니다). 먼저 프로그램을 해석하고 관찰 한 다음 루프의 특정 반복을위한 코드 (또는 다른 선형 코드)를 생성해야합니다 그런 다음 특정 반복에 대해서만 (또는 적어도 가능한 모든 반복에 대해서는) 가정하지 않음으로 지옥을 최적화하십시오. 그 코드의 이후의 많은 실행이 기대와 일치하여 최적화의 이점을 기대합니다. 정확성을 보장하기 위해 일부 (상대적으로 저렴한) 검사가 추가됩니다. 이 모든 작업을 수행하려면 전문화 할 대상에 대한 아이디어와 느리지 만 일반적인 구현으로 넘어 가야합니다. AOT 컴파일러에는 없습니다. 그들은 전문 수 없습니다 전혀볼 수없는 코드 (예 : 동적으로로드 된 코드)를 기반으로하고 부주의하게 전문화한다는 것은 더 많은 코드를 생성하는 것을 의미합니다.

전체 언어 를 올바르게 구현하는 AOT 컴파일러를 구현하는 것도 비교적 쉽습니다. 런타임에 호출하여 인터프리터가이 코드를 제공 할 때 수행 할 작업을 수행하는 코드를 생성하십시오. Nuitka (주로)이 작업을 수행합니다. 그러나 인터프리터만큼 불필요한 작업을 수행해야하므로 바이트 코드를 C 코드 블록으로 디스패치하기 위해 저장해야하므로 성능이 향상되지 않습니다 (실패 기준 3). 이는 기존의 인터프리터에서 최적화 할 가치가 있지만 비용이 적게 들지만 자체적 인 문제로 완전히 새로운 구현을 정당화 할만큼 중요하지는 않습니다.

세 가지 기준을 모두 충족하려면 무엇이 필요합니까? 우리는 모른다. Python 프로그램에서 콘크리트 유형, 제어 흐름 등에 대한 일부 정보를 추출 할 수있는 정적 분석 체계가 있습니다. 단일 기본 블록의 범위를 벗어난 정확한 데이터를 생성하는 것은 매우 느리고 전체 프로그램 또는 적어도 대부분을 볼 필요가 있습니다. 그러나 기본 제공 유형에 대한 몇 가지 작업을 최적화하는 것 외에는 해당 정보로 많은 것을 할 수 없습니다.

왜 그래? 솔직히 말하면 컴파일러는 런타임에로드 된 Python 코드를 실행하는 기능을 제거하거나 (실패 기준 1), 또는 Python 코드에서 전혀 무효화 할 수있는 가정을하지 않습니다. 불행하게도, 그 프로그램을 최적화하기위한 거의 모든 유용한 포함 등을 하나의 문자열로 전달, 여러 가지 방법으로 납치 할 수 가져 오기 회복 할 수있는 기능, 클래스가 변이 될 수있다 또는 완전히 교체, 모듈 임의로도 수정할 수 있습니다 포함한 전역을 eval, exec, __import__또는 기타 여러 기능이이 기능을 수행 할 수 있습니다. 실제로, 큰 최적화를 거의 적용 할 수 없으므로 성능상의 이점이 거의 없습니다 (실패 기준 3). 위 단락으로 돌아갑니다.


4

가장 어려운 문제는 주어진 시간에 모든 유형의 유형을 파악하는 것입니다.

C 또는 Java와 같은 정적 언어에서 유형 선언을 본 후에는 해당 객체가 무엇이며 무엇을 할 수 있는지 알 수 있습니다. 변수가 선언 int되면 정수입니다. 예를 들어 호출 가능한 함수 참조가 아닙니다.

파이썬에서는 가능합니다. 이것은 끔찍한 파이썬이지만 합법적입니다.

i = 2
x = 3 + i

def prn(s):
    print(s)

i = prn
i(x)

자,이 예제는 꽤 어리석지 만 일반적인 아이디어를 보여줍니다.

보다 현실적으로는 내장 함수를 호출 할 때 인수를 기록하는 버전과 같이 약간 다른 기능을 수행하는 사용자 정의 함수로 바꿀 수 있습니다.

PyPy는 코드가 실제로하는 일을보고 JIT (Just-In-Time) 컴파일을 사용하므로 PyPy는 작업 속도를 크게 높일 수 있습니다. PyPy는 루프를 감시하고 루프가 실행될 때마다 variable foo이 항상 정수인지 확인합니다. 그러면 PyPy는 foo루프를 통과 할 때마다 유형을 찾는 코드를 최적화 할 수 있으며 종종 정수를 나타내는 Python 객체를 제거 foo할 수 있으며 CPU의 레지스터에있는 숫자가 될 수 있습니다. 이것이 PyPy가 CPython보다 빠를 수있는 방법입니다. CPython은 가능한 빨리 유형 조회를 수행하지만 조회를 수행하지 않아도 훨씬 빠릅니다.

세부 사항을 모르지만 Unladen Swallow라는 프로젝트가 정적 컴파일러 기술을 적용하여 Python 속도를 높이기 위해 노력했습니다 (LLVM 사용). Unladen Swallow를 Google에서 검색하여 원하는대로 작동하지 않는 이유에 대한 토론을 찾을 수 있는지 확인하십시오.


Unladen Swallow는 정적 컴파일이나 정적 유형에 관한 것이 아닙니다. 결과적으로 CPython 인터프리터를 모든 동적 기능을 갖춘 LLVM으로 효과적으로 포팅하는 것이 었습니다. 실제로 끝나는 것이지만 멋진 JIT (Parrot 또는 .NET의 DLR 또는 PyPy와 같은 종류)와 함께 CPython 내에서 많은 로컬 최적화가 이루어졌습니다 (일부는 메인 라인 3.x로 만들었습니다). Shedskin은 아마도 정적 유형 유추를 사용하여 Python을 정적으로 컴파일하는 것으로 생각한 프로젝트 일 것입니다 (C ++은 아니지만 기본 코드는 아니지만).
abarnert

Unladen Swallow의 저자 중 하나 인 Reid Kleckner는 Unladen Swallow Retrospective를 게시했습니다. Unladen Swallow Retrospective 는이 맥락에서 읽을 가치가 있지만 실제로 기술적 인 것보다 관리 및 후원 문제에 관한 것입니다.
abarnert

0

다른 답변에서 알 수 있듯이 주요 문제는 유형 정보를 파악하는 것입니다. 정적으로 할 수있는 한, 좋은 코드를 직접 생성 할 수 있습니다.

그러나 정적으로 할 수는 없지만 실제 유형 정보 를 얻을 때 런타임에 합리적인 코드를 생성 할 수 있습니다 . 이 정보는 종종 안정적인 것으로 밝혀 지거나 특정 코드 포인트에서 특정 엔티티에 대해 최대 몇 개의 다른 값을 갖습니다. 셀프 프로그래밍 언어는 공격적인 런타임 타입 수집 및 런타임 코드 생성의 아이디어의 많은 개척했다. 이 아이디어는 Java 및 C #과 같은 최신 JIT 기반 컴파일러에서 널리 사용됩니다.

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