다이내믹 디스패치
Dynamic dispatch| 다형성 |
|---|
| 애드혹 다형 |
| 파라메트릭 다형 |
| 서브타이핑 |
컴퓨터 과학에서 동적 디스패치는 실행 시 호출할 다형 연산(방법 또는 함수)의 구현을 선택하는 프로세스입니다.일반적으로 객체 지향 프로그래밍(OOP) 언어 및 [1]시스템에 사용되며, 이러한 언어의 주요 특징으로 간주됩니다.
객체 지향 시스템은 문제를 이름으로 참조되는 작업을 실행하는 상호 작용 객체 집합으로 모델링합니다.다형성은 어느 정도 상호 교환이 가능한 물체가 각각 같은 이름의 동작을 드러내지만 동작이 다를 수 있는 현상이다.예를 들어,파일 개체와 데이터베이스 개체는 둘 다 스토리지에 개인 레코드를 쓰는 데 사용할 수 있는 StoreRecord 메서드를 가집니다.실장은 다릅니다.프로그램은 파일 개체 또는 데이터베이스 개체일 수 있는 개체에 대한 참조를 보유합니다.어느 것이 런타임 설정에 의해 결정될 수 있으며, 이 단계에서는 프로그램이 어떤 것을 알 수 없거나 신경 쓰지 않을 수 있습니다.프로그램이 개체에서 StoreRecord를 호출할 때 어떤 동작을 실행할지 선택해야 합니다.OOP가 객체에 메시지를 보내는 것으로 생각되는 경우 이 예에서는 프로그램이 알 수 없는 유형의 객체에 StoreRecord 메시지를 발송하고 메시지를 올바른 객체에 디스패치하도록 런타임 지원 시스템에 맡깁니다.오브젝트는 실행하는 [2]동작을 결정합니다.
다이내믹 디스패치는 컴파일 시에 다형성 동작의 실장이 선택되는 스태틱디스패치와 대조됩니다.동적 디스패치의 목적은 파라미터(또는 여러 파라미터)의 실행시간 유형을 알 때까지 적절한 구현 선택을 연기하는 것입니다.
다이내믹 디스패치는 레이트바인딩(다이나믹바인딩이라고도 불립니다)과는 다릅니다.이름 바인딩은 이름과 작업을 연관짓습니다.다형성 작업에는 여러 가지 구현이 있으며 모두 동일한 이름과 관련되어 있습니다.바인딩은 컴파일 시 또는 런타임에 (늦은 바인딩으로) 만들 수 있습니다.다이내믹 디스패치에서는 실행 시 동작의 특정 구현이 선택됩니다.다이내믹 디스패치는 레이트바인딩을 의미하는 것은 아니지만 레이트바인딩 동작의 실장은 [citation needed]런타임까지 알 수 없기 때문에 레이트바인딩은 다이내믹디스패치를 의미합니다.
싱글 디스패치 및 멀티 디스패치
호출하는 메서드의 버전은 단일 오브젝트 또는 오브젝트의 조합에 따라 선택할 수 있습니다.전자는 싱글 디스패치라고 불리며 Smalltalk, C++, Java, C#, Objective-C, Swift, JavaScript, Python 등의 공통 객체 지향 언어에 의해 직접 지원됩니다.이러한 언어 및 유사한 언어에서는 다음과 같은 구문을 사용하여 나눗셈 방법을 호출할 수 있습니다.
배당금.나누다(제수) 배당수 / 제수 여기서 파라미터는 옵션입니다.이는 매개 변수 제수를 사용하여 divide라는 이름의 메시지를 배당에 보내는 것으로 간주됩니다.구현은 배당 유형(아마도 합리적, 부동소수점, 매트릭스)만을 기준으로 선택되며, 제수의 유형이나 값은 무시됩니다.
이와는 대조적으로 일부 언어에서는 오퍼랜드의 조합에 기초한 메서드 또는 함수를 디스패치합니다.나눗셈의 경우 배당 및 제수의 유형에 따라 어떤 분할 연산이 수행될지가 결정됩니다.이것은 멀티 디스패치라고 불립니다.다중 디스패치를 지원하는 언어로는 Common Lisp, Dylan 및 Julia가 있습니다.
동적 디스패치 메커니즘
언어는 다른 동적 디스패치 메커니즘으로 구현될 수 있습니다.언어에 의해 제공되는 동적 디스패치 메커니즘의 선택은 주어진 언어 내에서 사용 가능하거나 가장 자연스럽게 사용되는 프로그래밍 패러다임을 크게 변화시킵니다.
통상, 입력된 언어에서는, 디스패치메커니즘은, 인수의 타입(통상은 메시지의 수신자의 타입에 근거해)에 근거해 실행됩니다.타이핑 시스템이 약하거나 입력 시스템이 없는 언어에서는 종종 각 객체의 객체 데이터의 일부로 디스패치테이블을 전송합니다.이를 통해 각 인스턴스가 특정 메시지를 다른 메서드에 매핑할 수 있으므로 인스턴스 동작이 허용됩니다.
일부 언어는 하이브리드 방식을 제공합니다.
동적 디스패치에서는 항상 오버헤드가 발생하기 때문에 일부 언어에서는 특정 방법에 대해 정적 디스패치를 제공합니다.
C++의 실장
C++는 얼리바인딩을 사용하여 동적 디스패치와 정적 디스패치를 모두 제공합니다.디스패치의 기본 형식은 static입니다.동적 디스패치를 얻으려면 프로그래머는 메서드를 가상으로 선언해야 합니다.
C++ 컴파일러는 일반적으로 가상 함수 테이블(vtable)이라고 불리는 데이터 구조를 사용하여 동적 디스패치를 구현합니다.이러한 데이터 구조는 특정 클래스의 이름과 구현에 대한 매핑을 멤버 함수 포인터 세트로 정의합니다(이는 순전히 구현 세부 사항입니다.C++ 사양에서는 vtables를 언급하지 않습니다).그런 다음 해당 유형의 인스턴스는 인스턴스 데이터의 일부로 이 테이블에 대한 포인터를 저장합니다.다중 상속을 사용하는 경우 이 작업은 복잡합니다.C++는 레이트바인딩을 지원하지 않기 때문에 C++ 오브젝트의 가상 테이블은 런타임에 변경할 수 없습니다.따라서 디스패치타깃의 잠재적인 세트는 컴파일 시에 선택된 유한 세트로 제한됩니다.
언어에서는 메시지파라미터의 유형을 정식 메시지명의 일부로 간주하기 때문에 타입 오버로딩에서는 C++에서는 다이내믹디스패치가 생성되지 않습니다.이는 프로그래머가 보는 메시지 이름이 바인딩에 사용되는 정식 이름이 아님을 의미합니다.
Go and Rust 구현
Go, Rust 및 Nim에서는 보다 다용도적인 얼리 바인딩이 사용됩니다.Vtable 포인터는 객체 참조와 함께 '팻 포인터'(Go의 경우 인터페이스,[citation needed] Rust의 경우 '트래빗 객체')로 전송됩니다.
이것에 의해, 서포트되고 있는 인터페이스가, 기반이 되는 데이터 구조로부터 분리됩니다.컴파일된 각 라이브러리는 유형을 올바르게 사용하기 위해 지원되는 인터페이스의 전체 범위를 알 필요는 없으며 필요한 특정 vtable 레이아웃만 알 수 있습니다.코드는, 같은 데이터에 대해서 다른 인터페이스를 다른 함수에 건네줄 수 있습니다.이러한 범용성은 각 객체 참조에 대한 추가 데이터를 희생하며, 이러한 참조가 많이 지속적으로 저장되면 문제가 됩니다.
fat pointer라는 용어는 단순히 추가 관련 정보가 있는 포인터를 가리킵니다.추가 정보는 위에서 설명한 동적 디스패치용 vtable 포인터일 수 있지만 일반적으로 슬라이스 [citation needed]등 관련 개체의 크기를 나타냅니다.
SmallTalk 구현
Smalltalk는 유형 기반 메시지 디스패처를 사용합니다.각 인스턴스에는 메서드를 포함하는 정의의 단일 유형이 있습니다.인스턴스가 메시지를 수신하면 디스패처는 메시지와 메서드 맵에서 해당 유형의 메서드를 검색하여 메서드를 호출합니다.
유형은 기본 유형의 체인을 가질 수 있으므로 이 검색에는 비용이 많이 들 수 있습니다.Smalltalk 메커니즘의 순진한 구현은 C++보다 훨씬 더 높은 오버헤드를 가지고 있는 것처럼 보이며, 이 오버헤드는 객체가 수신하는 각각의 메시지와 모든 메시지에 대해 발생합니다.
실제 Smalltalk 구현에서는 많은 경우 인라인[3] 캐싱이라고 불리는 기술을 사용하여 메서드 디스패치를 매우 빠르게 할 수 있습니다.인라인 캐싱은 기본적으로 콜사이트(또는 멀티웨이 캐싱의 경우 여러 쌍)의 이전 행선지 메서드주소와 오브젝트클래스를 저장합니다.캐시된 방식은 메서드실렉터에 따라 가장 일반적인 타깃 방식(또는 캐시 미스 핸들러만)으로 초기화됩니다.메서드 콜사이트가 실행 중에 도달하면 캐시 내의 주소를 호출합니다(다이나믹코드 제너레이터에서는 다이렉트주소가 캐시 미스 로직에 의해 백패치 되어 있기 때문에 이 콜은 다이렉트콜입니다).호출된 메서드의 프롤로그 코드는 캐시된 클래스와 실제 오브젝트 클래스를 비교합니다.이 클래스가 일치하지 않을 경우 캐시 미스핸들러로 실행 브랜치를 실행하여 클래스 내에서 올바른 메서드를 찾습니다.고속 구현에는 여러 캐시 엔트리가 있을 수 있으며, 초기 캐시 누락 시 올바른 방법으로 실행하는데 몇 가지 명령만 있으면 되는 경우가 많습니다.일반적인 경우는 캐시된 클래스 일치이며 메서드로 실행이 계속됩니다.
오브젝트 클래스 및 메서드실렉터를 사용하여 메서드 호출 로직에서도 아웃오브라인 캐싱을 사용할 수 있습니다.하나의 설계에서는 클래스 및 메서드 셀렉터가 해시되어 메서드 디스패치 캐시 테이블의 인덱스로 사용된다.
Smalltalk는 반사적인 언어이기 때문에 많은 구현에서 개개의 객체를 동적으로 생성된 메서드 룩업테이블을 사용하여 객체로 변환할 수 있습니다.이를 통해 개체별로 개체 동작을 변경할 수 있습니다.프로토타입 기반 언어라고 알려진 모든 범주의 언어들은 이것으로부터 발전해 왔으며, 그 중 가장 유명한 것은 Self와 JavaScript이다.디스패치 캐싱 방식을 신중하게 설계하여 프로토타입 기반의 언어에서도 고성능 디스패치를 사용할 수 있습니다.
Python, Ruby, Objective-C 및 Groovy를 포함한 많은 동적 유형의 언어들이 유사한 접근 방식을 사용합니다.
Python의 예
학급 고양이: 방어하다 말하다(자신): 인쇄물('야옹') 학급 개: 방어하다 말하다(자신): 인쇄물('우프') 방어하다 말하다(애완동물): # Speak 메서드를 동적으로 디스패치합니다. # 애완동물은 고양이와 개의 인스턴스 중 하나일 수 있습니다. 애완동물.말하다() 고양이 = 고양이() 말하다(고양이) 개 = 개() 말하다(개) C++의 예
#실패하다 <iostream> // Pet을 추상 가상 기반 클래스로 만듭니다. 학급 애완동물 { 일반의: 가상 무효 말하다() = 0; }; 학급 개 : 일반의 애완동물 { 일반의: 무효 말하다() { 표준::외치다 << > "우우우!\n"; } }; 학급 고양이 : 일반의 애완동물 { 일반의: 무효 말하다() { 표준::외치다 << > 야옹!\n"; } }; // Speak()는 Pet에서 파생된 모든 것을 받아들일 수 있습니다. 무효 말하다(애완동물& 애완동물) { 애완동물.말하다(); } 인트 주된() { 개 열리다; 고양이 심바; 말하다(열리다); 말하다(심바); 돌아가다 0; } 「 」를 참조해 주세요.
레퍼런스
- ^ Milton, Scott; Schmidt, Heinz W. (1994). Dynamic Dispatch in Object-Oriented Languages (Technical report). Vol. TR-CS-94-02. Australian National University. CiteSeerX 10.1.1.33.4292.
- ^ Driesen, Karel; Hölzle, Urs; Vitek, Jan (1995). "Message Dispatch on Pipelined Processors". ECOOP’95 — Object-Oriented Programming, 9th European Conference, Åarhus, Denmark, August 7–11, 1995. Lecture Notes in Computer Science. Vol. 952. Springer. CiteSeerX 10.1.1.122.281. doi:10.1007/3-540-49538-X_13. ISBN 3-540-49538-X.
- ^ Müller, Martin (1995). Message Dispatch in Dynamically-Typed Object-Oriented Languages (Master thesis). University of New Mexico. pp. 16–17. CiteSeerX 10.1.1.55.1782.
추가 정보
- Lippman, Stanley B. (1996). Inside the C++ Object Model. Addison-Wesley. ISBN 0-201-83454-5.
- Groeber, Marcus; Di Geronimo, Jr., Edward "Ed"; Paul, Matthias R. (2002-03-02) [2002-02-24]. "GEOS/NDO info for RBIL62?". Newsgroup: comp.os.geos.programmer. Archived from the original on 2019-04-20. Retrieved 2019-04-20.
[…] The reason Geos needs 16 interrupts is because the scheme is used to convert inter-segment ("far") function calls into interrupts, without changing the size of the code. The reason this is done so that "something" (the kernel) can hook itself into every inter-segment call made by a Geos application and make sure that the proper code segments are loaded from virtual memory and locked down. In DOS terms, this would be comparable to an overlay loader, but one that can be added without requiring explicit support from the compiler or the application. What happens is something like this: […] 1. The real mode compiler generates an instruction like this: CALL <segment>:<offset> -> 9A <offlow><offhigh><seglow><seghigh> with <seglow><seghigh> normally being defined as an address that must be fixed up at load time depending on the address where the code has been placed. […] 2. The Geos linker turns this into something else: INT 8xh -> CD 8x […] DB <seghigh>,<offlow>,<offhigh> […] Note that this is again five bytes, so it can be fixed up "in place". Now the problem is that an interrupt requires two bytes, while a CALL FAR instruction only needs one. As a result, the 32-bit vector (<seg><ofs>) must be compressed into 24 bits. […] This is achieved by two things: First, the <seg> address is encoded as a "handle" to the segment, whose lowest nibble is always zero. This saves four bits. In addition […] the remaining four bits go into the low nibble of the interrupt vector, thus creating anything from INT 80h to 8Fh. […] The interrupt handler for all those vectors is the same. It will "unpack" the address from the three-and-a-half byte notation, look up the absolute address of the segment, and forward the call, after having done its virtual memory loading thing... Return from the call will also pass through the corresponding unlocking code. […] The low nibble of the interrupt vector (80h–8Fh) holds bit 4 through 7 of the segment handle. Bit 0 to 3 of a segment handle are (by definition of a Geos handle) always 0. […] all Geos API run through the "overlay" scheme […]: when a Geos application is loaded into memory, the loader will automatically replace calls to functions in the system libraries by the corresponding INT-based calls. Anyway, these are not constant, but depend on the handle assigned to the library's code segment. […] Geos was originally intended to be converted to protected mode very early on […], with real mode only being a "legacy option" […] almost every single line of assembly code is ready for it […]
- 폴, 마티아스 R.(2002-04-11)."Re:[fd-dev]ANNOUNCE:CuteMouse 2.0알파 1". freedos-dev.그 2020-02-21에 원래에서 Archived.2020-02-21 Retrieved.그런 망가진 포인터의 경우[…][…] 많은년 전에 액셀과 방법을 어떻게 마부로 여러 인터럽트 벡터(로 이것은 우리에게 전체 복식 개갱고 더 많거나 더 적은 동일한startup/exit 틀 코드에 대해서는 모든 그들 중에 많은 공간을 절약할 것이다)는데 그 후 다른 interru로 전환*one* 진입 지점을 사용하는 것에 대해 생각하고 있었다.로 처리기 내부적으로.예를 들어:1234h:0000h[…]1233h:0010h[…]1232h:0020h[…]1231h:0030h[…]1230h:정확히 같은 진입점에 0040h[…]모든 점.만약 당신이 INT 21h 1234h에:1233h 0000h과 INT2Fh에:0010h 등 연결하면 그들이 모두 같은"허점"해결했는데, 당신은 여전히 그들과 사이의 다른 처리기 내부적으로로 구별할 수 있었습니다.한"압축"진입점의 해병 공격 헬기 대대 하중에 대한 A20 스텁을 생각해 보시오.이것은 받아들여지지 않는 한 프로그램 세그먼트:오프셋 magics 추기 시작하고 일한다.[…]이 당신이 많은 인터럽트 연결하는 훨씬 더 많은 메모리를 낭비하는 정반대의 접근 방식( 어쩌면 지원 IBM의 인터럽트 공유 프로토콜)여러 진입점이 있는 것.[…]우리는 결과에 다른 드라이버나 비정규화하다. 조언도 정상화되기 때문에, 당신이 이 가장 아마도 관행에서, 무슨 이유 영원히 저장되지 않을 것이다 왔다.[…](NB다.뭔가 구체적으로 인텔의 리얼 모드 세그먼트에 대해" 뚱뚱한 포인터"과 비슷하:오프셋 x86프로세서에 있는 공유 코드 진입점은 일부러denormalized 포인터와 몇가지 정보는 여전히 공유 코드 안에서 다른 호출자를 구별하는 것을 포함하는 해소했다.오픈 시스템에서는 (다른 드라이버 또는 응용 프로그램에서) 서드파티 인스턴스를 포인터로 정규화하는 것을 퍼블릭인터페이스에서 완전히 배제할 수 없지만, 이 방식을 내부 인터페이스에서 안전하게 사용하여 엔트리 코드 시퀀스를 장황하게 할 수 있습니다.)
- Bright, Walter (2009-12-22). "C's Biggest Mistake". Digital Mars. Archived from the original on 2022-06-08. Retrieved 2022-07-11. [1]
- Holden, Daniel (2015). "A Fat Pointer Library". Cello: High Level C. Archived from the original on 2022-07-11. Retrieved 2022-07-11.