스레드 코드
Threaded code컴퓨터 과학에서 스레드 코드는 기본적으로 서브루틴 호출로 구성된 형식을 가진 프로그래밍 기법입니다.이는 컴파일러에서 자주 사용되며, 컴파일러는 해당 형식으로 코드를 생성하거나 해당 형식으로 구현될 수 있습니다.코드는 인터프리터에 의해 처리될 수도 있고 기계 코드 호출 명령의 시퀀스일 수도 있습니다.
스레드 코드는 대체 생성 기법 및 대체 호출 규칙에 의해 생성된 코드보다 밀도가 높습니다.캐시된 아키텍처에서는 실행 속도가 약간 [citation needed]느려질 수 있습니다.그러나 컴퓨터 프로세서의 캐시에 들어갈 정도로 작은 프로그램은 캐시 누락이 많은 [1]큰 프로그램보다 더 빨리 실행될 수 있습니다.다른 프로그램이 캐시를 가득 채운 경우 작은 프로그램일수록 스레드 전환 속도가 더 빠를 수 있습니다.
스레드 코드는 많은 프로그래밍 언어 컴파일러에서 사용되는 것으로 가장 잘 알려져 있습니다. 예를 들어 Fourth, BASIC의 많은 구현, COBOL의 일부 구현, [2]B의 초기 버전 및 소형 미니컴퓨터와 아마추어 무선 [citation needed]위성용 기타 언어입니다.
역사
![]() |
컴퓨터 프로그램을 만드는 일반적인 방법은 컴파일러를 사용하여 소스 코드를 기계 코드로 변환하는 것입니다.일반적으로 실행 파일은 빠르지만 하드웨어 플랫폼 전용이므로 이식할 수 없습니다.다른 방법은 가상 시스템에 대한 명령을 생성하고 각 하드웨어 플랫폼에서 인터프리터를 사용하는 것입니다.인터프리터는 가상 시스템 환경을 인스턴스화하고 명령을 실행합니다.따라서 인터프리터만 컴파일해야 합니다.
초기 컴퓨터는 상대적으로 메모리가 적었다.예를 들어, 대부분의 Data General Nova, IBM 1130 및 최초의 마이크로컴퓨터 대부분은 4kB의 RAM만 설치되었습니다.그 결과, 사용 가능한 메모리에 맞추기 위해 프로그램의 크기를 줄이는 방법을 찾기 위해 많은 시간을 소비했습니다.
한 가지 해결책은 인터프리터를 사용하여 심볼 언어를 한 번에 조금씩 읽고 함수를 호출하여 액션을 실행하는 것입니다.일반적으로 소스 코드는 결과 머신 코드보다 훨씬 밀도가 높기 때문에 전체 메모리 사용량을 줄일 수 있습니다.이것이 Microsoft BASIC이 [a]인터프리터인 이유였습니다. 자체 코드는 Altair 8800과 같은 컴퓨터의 4kB 메모리를 사용자의 소스 코드와 공유해야 했기 때문입니다.컴파일러는 소스 언어에서 머신 코드로 변환되므로 컴파일러, 소스 및 출력이 동시에 메모리에 있어야 합니다.인터프리터에서는 출력이 없습니다.코드는 한 번에 한 줄씩 만들어 실행한 후 폐기됩니다.
스레드 코드는 메모리 사용을 최소화하는 컴파일된 코드의 포맷 스타일입니다.예를 들어 매크로 어셈블러에서 흔히 볼 수 있는 것처럼 프로그램의 모든 단계에서 작업의 모든 단계를 쓰는 대신, 컴파일러는 코드의 각 공통 비트를 서브루틴에 씁니다.따라서 각 비트는 메모리의 한 곳에만 존재합니다("반복하지 마십시오." 참조).이러한 프로그램의 최상위 애플리케이션은 서브루틴 콜만으로 구성될 수 있습니다.이러한 서브루틴의 대부분은 하위 레벨의 서브루틴 콜만으로 구성됩니다.
메인프레임과 RCA 1802와 같은 초기 마이크로프로세서는 서브루틴을 호출하기 위해 몇 가지 명령을 필요로 했습니다.최상위 어플리케이션 및 많은 서브루틴에서 이 시퀀스는 항상 반복되며 서브루틴 주소만 콜에서 다음 콜로 변경됩니다.즉, 다수의 함수 호출로 구성된 프로그램에도 상당한 양의 반복 코드가 있을 수 있습니다.
이를 해결하기 위해 스레드 코드 시스템은 의사 코드를 사용하여 단일 연산자의 함수 호출을 나타냅니다.실행 시 작은 "인터프리터"가 최상위 코드를 스캔하여 메모리에 있는 서브루틴의 주소를 추출하여 호출합니다.다른 시스템에서는 이 기본 개념이 브랜치테이블, 디스패치테이블 또는 가상 메서드테이블로서 실장되어 서브루틴 주소 테이블로 구성되어 있습니다.
1970년대에 하드웨어 설계자들은 서브루틴 콜을 보다 빠르고 간단하게 하기 위해 상당한 노력을 기울였습니다.개선된 설계에서는 서브루틴을 호출하기 위해 하나의 명령어만 사용되므로 의사 명령어를 사용해도 공간이 [citation needed]절약되지 않습니다.게다가 이러한 콜의 퍼포먼스에는, 추가의 오버헤드가 거의 없습니다.오늘날 거의 모든 프로그래밍 언어는 서브루틴으로 코드를 분리하는 데 초점을 맞추고 있지만 공간을 절약하기 위해서가 아니라 코드의 명확성과 유지보수를 위해서입니다.
스레드 코드 시스템에서는 서브루틴 주소만 콜에서 다음 콜로 변경되는 함수 호출 목록을 콜 [3][4][5][6][7]opcode가 제거되고 주소 목록만 남겨진 함수 호출인 실행 토큰 목록으로 대체하여 공간을 절약합니다.
오랜 세월에 걸쳐 프로그래머들은 그 "인터프리터" 또는 "스몰 셀렉터"에 대해 많은 변형을 만들어 왔습니다.주소 목록의 특정 주소는 색인, 범용 레지스터 또는 포인터를 사용하여 추출할 수 있습니다.주소는 직접 또는 간접, 연속 또는 비연속(포인터로 링크), 상대 또는 절대, 컴파일 시 해결되거나 동적으로 구축될 수 있습니다.모든 상황에서 단일 변형이 "최적"인 것은 아닙니다.
발전
공간을 절약하기 위해 프로그래머는 서브루틴 호출 목록을 서브루틴 주소의 단순한 목록으로 압축하고 작은 루프를 사용하여 각 서브루틴을 차례로 호출했습니다.예를 들어 다음 의사 코드에서는 이 기술을 사용하여 A와 B의 2개의 번호를 추가합니다.이 예에서 목록에는 스레드라는 라벨이 붙어 있으며 변수 IP(Instruction Pointer)는 목록 내의 플레이스를 추적합니다.다른 변수 sp(Stack Pointer)에는 일시적으로 값을 유지할 수 있는 메모리 내의 다른 위치에 주소가 포함되어 있습니다.
개시하다: 아이피 = &실 // 텍스트 레이블 '스레드'가 아닌 주소 '&pushA'를 가리킵니다. 정상: 뛰어내리다 *아이피++ // 스레드 내 주소로 ip를 따르고 해당 주소를 따라 서브루틴으로 이동하고 ip를 확장합니다. 실: &푸시 A &푸시 B &더하다 ... 푸시 A: *sp++ = A // sp에 따라 사용 가능한 메모리에 A를 저장하고 sp를 다음으로 진행합니다. 뛰어내리다 정상 푸시 B: *sp++ = B 뛰어내리다 정상 더하다: 추가 1 = *--sp // 스택에서 상위 값을 팝합니다. 추가 2 = *--sp // 스택에서 두 번째 값을 팝합니다. *sp++ = 추가 1 + 추가 2 // 두 값을 합산하여 결과를 스택 상단에 저장합니다. 뛰어내리다 정상
콜루프top
는 매우 단순하기 때문에 각 서브루틴의 끝에서 인라인으로 반복할 수 있습니다.이제 컨트롤은 서브루틴의 끝에서 다른 루트의 시작까지 한 번 점프합니다.top
. 예:
개시하다: 아이피 = &실 // ip는 &pushA를 가리킵니다(pushA의 첫 번째 명령을 가리킵니다). 뛰어내리다 *아이피++ // 제어를 푸시 A의 첫 번째 명령으로 전송하고 IP를 &push B로 확장합니다. 실: &푸시 A &푸시 B &더하다 ... 푸시 A: *sp++ = A // sp에 따라 사용 가능한 메모리에 A를 저장하고 sp를 다음으로 진행합니다. 뛰어내리다 *아이피++ // ip가 지시하는 곳(즉, pushB)에 컨트롤을 전송하고 ip를 확장합니다. 푸시 B: *sp++ = B 뛰어내리다 *아이피++ 더하다: 추가 1 = *--sp // 스택에서 상위 값을 팝합니다. 추가 2 = *--sp // 스택에서 두 번째 값을 팝합니다. *sp++ = 추가 1 + 추가 2 // 두 값을 합산하여 결과를 스택 상단에 저장합니다. 뛰어내리다 *아이피++
이를 직접 나사산 코드(DTC)라고 합니다.이 기술은 더 오래되었지만, "스레드 코드"라는 용어가 처음으로 널리 퍼진 것은 아마도 제임스 R일 것입니다.벨의 1973년 기사 "스레드 코드"[8]
1970년에 Charles H. Moore는 자신의 4번째 가상 머신에 대해 보다 콤팩트한 배치인 간접 스레드 코드(ITC)를 개발했습니다.Nova 미니컴퓨터가 모든 주소에 간접 비트를 가지고 있어 ITC가 쉽고 빠르게 이루어졌기 때문에 Moore가 이 계약을 체결하게 되었습니다.나중에, 그는 그것이 매우 편리하다는 것을 알게 되어 나중에 모든 [9]Fourth 디자인에 그것을 전파시켰다고 말했다.
현재 일부 Fourth 컴파일러는 직접 스레드 코드를 생성하는 반면 다른 컴파일러는 간접 스레드 코드를 생성합니다.어느 쪽이든 실행 파일은 동일하게 동작합니다.
스레드화 모델
실질적으로 모든 실행 가능한 스레드 코드는 서브루틴을 호출하기 위해 이러한 방법 중 하나 이상을 사용합니다(각 방법을 "스레딩 모델"이라고 부릅니다).
직접 스레드화
스레드의 주소는 기계어의 주소입니다.이 형식은 단순하지만 스레드가 머신주소로만 구성되어 있기 때문에 오버헤드가 발생할 수 있습니다.따라서 이후의 파라미터는 모두 메모리에서 간접적으로 로드해야 합니다.일부 포스 시스템은 직접 스레드 코드를 생성합니다.많은 기계에서 직접 스레드는 서브루틴 스레드보다 빠릅니다(아래 참조).
스택 머신의 예에서는 "push A, push B, add" 시퀀스를 실행할 수 있습니다.이는 다음 스레드 및 루틴으로 변환될 수 있습니다.ip
라벨이 붙은 주소로 초기화됩니다.thread
(즉, 주소:&pushA
저장됩니다).
#420 PUSH(x) (*sp++ = (x) #define POP() (*--sp) 개시하다: 아이피 = &실 // ip는 &pushA를 가리킵니다(pushA의 첫 번째 명령을 가리킵니다). 뛰어내리다 *아이피++ // 제어를 푸시 A의 첫 번째 명령으로 전송하고 IP를 &push B로 확장합니다. 실: &푸시 A &푸시 B &더하다 ... 푸시 A: 밀어넣다(A) 뛰어내리다 *아이피++ // ip가 지시하는 곳(즉, pushB)에 컨트롤을 전송하고 ip를 확장합니다. 푸시 B: 밀어넣다(B) 뛰어내리다 *아이피++ 더하다: 결과 = 팝() + 팝() 밀어넣다(결과) 뛰어내리다 *아이피++
또, 스레드에 오퍼랜드를 포함할 수도 있다.이렇게 하면 위에서 필요로 하는 일부 간접은 제거할 수 있지만 스레드는 더 커집니다.
#420 PUSH(x) (*sp++ = (x) #define POP() (*--sp) 개시하다: 아이피 = &실 뛰어내리다 *아이피++ 실: &밀다 &A // 리터럴 A가 아닌 A가 저장되는 주소 &밀다 &B &더하다 ... 밀다: variable_address = *아이피++ // 서브루틴 주소가 아니므로 ip past 피연산자 주소를 이동해야 합니다. 밀어넣다(*variable_address) // 변수에서 값을 읽고 스택을 푸시합니다. 뛰어내리다 *아이피++ 더하다: 결과 = 팝() + 팝() 밀어넣다(결과) 뛰어내리다 *아이피++
간접 스레드화
간접 스레드화는 기계 코드를 가리키는 위치에 대한 포인터를 사용합니다.간접 포인터는 스레드에 반복적으로 저장하는 것이 아니라 간접 "블록"에 저장되는 피연산자가 뒤따를 수 있습니다.따라서 간접 코드는 종종 직접 스레드화된 코드보다 더 컴팩트합니다.일반적으로 인터렉션은 바이트 코드 인터프리터보다 빠르지만 속도가 느려집니다.핸들러 오퍼랜드에 값과 타입이 모두 포함되어 있는 경우 직접 스레드된 코드에 비해 공간을 대폭 절약할 수 있습니다.오래된 FORT 시스템은 일반적으로 간접 스레드 코드를 생성합니다.
예를 들어 "push A, push B, add"를 실행하는 것이 목표인 경우 다음을 사용할 수 있습니다.여기서,ip
에 대응하도록 초기화되어 있습니다.&thread
, 각 코드 프래그먼트(push
,add
)는 이중으로 검색하여 확인할 수 있습니다.ip
및 간접 블록, 및 fragment에 대한 모든 오퍼랜드는 fragment의 주소 뒤에 있는 간접 블록에 있습니다.이를 위해서는 현재 서브루틴을ip
이 예에서는 호출되는 다음 서브루틴이 포함되어 있었습니다.
개시하다: 아이피 = &실 // '&i_displays'를 가리킵니다.A' 뛰어내리다 *(*아이피) // 'push'의 첫 번째 명령에 대한 포인터를 따릅니다. 아직 ip를 진행하지 마십시오. 실: &i_pushA &i_pushB &추가 ... i_pushA: &밀다 &A i_pushB: &밀다 &B 추가: &더하다 밀다: *sp++ = *(*아이피 + 1) // 피연산자 주소에 대한 간접 블록 시작 후 1회 검색 뛰어내리다 *(*++아이피) // 스레드에서 ip를 진행하고 다음 간접 블록을 통과하여 다음 서브루틴으로 이동합니다. 더하다: 추가 1 = *--sp 추가 2 = *--sp *sp++ = 추가 1 + 추가 2 뛰어내리다 *(*++아이피)
서브루틴 스레드화
이른바 "서브루틴 스레드 코드" ("콜 스레드 코드")는 일련의 기계어 "콜" 명령(또는 "점프"의 직접적인 스레드 사용과는 대조적으로 "콜"에 대한 함수의 주소)으로 구성됩니다.ALGOL, Fortran, Cobol 및 일부 Forth 시스템용 초기 컴파일러는 종종 서브루틴 스레드 코드를 생성했다.이러한 시스템의 많은 코드는 컴파일러 이론이 잘 개발된 피연산자의 LIFO(Last-In-First-Out) 스택에서 작동했습니다.대부분의 최신 프로세서는 서브루틴의 "콜" 및 "리턴" 명령에 대해 특별한 하드웨어 지원을 제공하므로 디스패치당 1개의 추가 머신 명령의 오버헤드가 다소 줄어듭니다.
Gforth 컴파일러의 공동창작자인 Anton Ertl은 "일반적인 신화와는 달리 서브루틴 스레드는 보통 직접 스레드보다 느리다"[10]고 말했다.단, Ertl의 최신[1] 테스트에서는 서브루틴 스레드가 25개의 테스트 케이스 중 15개의 테스트 케이스에서 직접 스레드보다 고속임을 알 수 있습니다.특히 직접 스레드는 Xeon, Opteron 및 Athlon 프로세서에서 가장 빠른 스레드 모델이며 간접 스레드는 Pentium M 프로세서에서 가장 빠른 스레드 모델이며 서브루틴 스레드는 Pentium 4, Pentium III 및 PPC 프로세서에서 가장 빠른 스레드 모델임을 발견했습니다.
「Push A, push B, add」의 콜스레딩의 예로서 다음과 같이 합니다.
실: 불러 푸시 A 불러 푸시 B 불러 더하다 리트 푸시 A: *sp++ = A 리트 푸시 B: *sp++ = B 리트 더하다: 추가 1 = *--sp 추가 2 = *--sp *sp++ = 추가 1 + 추가 2 리트
토큰 스레드화
토큰 스레드 코드는 작업 테이블에 인덱스 목록으로 스레드를 구현합니다. 인덱스 폭은 밀도와 효율성을 위해 가능한 한 작게 선택됩니다.1바이트/8비트는 프로그래밍의 용이성을 위해 기본적으로 선택되지만 지원되는 동작의 수에 따라 4비트와 같은 작은 크기 또는 12비트 또는 16비트 같은 큰 크기를 사용할 수 있습니다.인덱스 폭은 기계 포인터보다 좁도록 선택되는 한 프로그래머에 의한 특별한 노력 없이 자연스럽게 다른 스레드 타입보다 작아질 것이다.보통 다른 스레드의 1/2 ~3/4 크기이며, 스레드화되지 않은 코드의 1/4 ~8 크기입니다.테이블의 포인터는 간접 또는 직접일 수 있습니다.일부 포스 컴파일러는 토큰 스레드 코드를 생성합니다.일부 프로그래머는 일부 파스칼 컴파일러에 의해 생성된 "p-code"와 에서 사용되는 바이트코드를 고려합니다.NET, Java, BASIC 및 일부 C 컴파일러는 토큰 스레딩이 됩니다.
지금까지 일반적인 접근 방식은 바이트 코드이며, 일반적으로 8비트 운영 코드를 스택 기반 가상 머신과 함께 사용합니다.원형 바이트 코드 인터프리터는 "디코딩 및 디스패치 인터프리터"로 알려져 있으며 다음 형식을 따릅니다.
개시하다: vpc = &실 디스패치: 주소 = 해독하다(&vpc) // 다음 바이트 코드 작업을 구현하는 기계 코드에 대한 포인터로 변환합니다. // 여기에서 명령 간 작업을 수행합니다(예: 글로벌 상태 업데이트, 이벤트 처리 등). 뛰어내리다 주소 코드_PTR 해독하다(바이트 코드 **p) { // 더 복잡한 인코딩에서는 또는 제어/모드 플래그를 선택할 수 있는 테이블이 여러 개 있을 수 있습니다. 돌아가다 테이블[*(*p)++]; } 실: /* 머신 주소가 아닌 바이트 코드가 포함되어 있습니다.그래서 더 콤팩트해요.*/ 1 /*pushA*/ 2 /*푸시B*/ 0 /*추가*/ 테이블: &더하다 /* table[0] = 바이트 코드 0을 구현하는 기계 코드의 주소 */ &푸시 A /* 테이블 [1]...*/ &푸시 B /* 테이블 [2]...*/ 푸시 A: *sp++ = A 뛰어내리다 디스패치 푸시 B: *sp++ = B 뛰어내리다 디스패치 더하다: 추가 1 = *--sp 추가 2 = *--sp *sp++ = 추가 1 + 추가 2 뛰어내리다 디스패치
가상 시스템이 바이트 크기 명령만 사용하는 경우decode()
단순히 에서 가져온 것이다.thread
단, 일반적으로 사용되는1 바이트 명령과 몇 가지 덜 일반적인 멀티바이트 명령(복잡한 명령 집합 컴퓨터 참조)이 있습니다.decode()
더 복잡합니다.opcode를 인덱스로 직접 사용하여 분기 테이블을 통해 단일 바이트 opcode의 디코딩을 매우 간단하고 효율적으로 처리할 수 있습니다.
"push" 및 "add"와 같이 개별 작업이 간단한 명령의 경우, 실행할 항목을 결정하는 데 드는 오버헤드는 실제 실행하는 비용보다 크기 때문에 이러한 인터프리터는 종종 기계 코드보다 훨씬 느립니다.그러나 더 복잡한("복합") 명령의 경우 오버헤드 비율은 상대적으로 덜 유의합니다.
토큰 스레드 코드가 너무 커서 물리적 CPU의 L1 명령 캐시에 들어가지 못할 경우 토큰 스레드 코드가 동등한 시스템 코드보다 더 빠르게 실행될 수 있습니다.스레드 코드(특히 토큰 스레드 코드)의 높은 코드 밀도를 통해 L1 캐시에 완전히 들어갈 수 있으므로 캐시 스레싱을 피할 수 있습니다.단, 스레드코드는 명령캐시(각 동작의 실장용)와 데이터캐시(바이트코드 및 테이블용)를 모두 소비합니다.이는 스레드코드가 CPU가 처리용으로 유지할 수 있는 데이터량의 예산을 소비한다는 것을 의미합니다.어느 경우든 계산 중인 문제가 적은 양의 데이터에 많은 작업을 적용하는 경우 스레드 코드를 사용하는 것이 이상적인 최적화일 수 있습니다.[4]
허프만 스레드화
Huffman 스레드 코드는 Huffman 코드로 저장된 토큰 목록으로 구성됩니다.허프만 코드는 하나의 토큰을 식별하는 가변 길이의 비트 문자열입니다.Huffman 스레드 인터프리터는 Huffman 코드에 의해 네비게이트 가능한 인덱스 테이블 또는 포인터 트리를 사용하여 서브루틴을 찾습니다.허프만 스레드 코드는 컴퓨터 프로그램으로 알려진 가장 콤팩트한 표현 중 하나입니다.인덱스와 코드는 코드 내의 각 서브루틴에 대한 콜 빈도를 측정하여 선택합니다.빈번한 콜에는 최단 코드가 부여됩니다.주파수가 거의 동일한 동작에는 비트 길이가 거의 동일한 코드가 부여됩니다.대부분의 Huffman-threaded 시스템은 다이렉트 스레드 포스 시스템으로 구현되어 있으며, 작고 저렴한 마이크로 컨트롤러에 많은 양의 느린 실행 코드를 포장하는 데 사용됩니다.대부분의[11] 공개된 용도는 스마트 카드, 장난감, 계산기 및 시계에서 사용되었습니다.PBASIC에서 사용되는 비트 지향 토큰화 코드는 Huffman-threaded 코드의 일종이라고 볼 수 있습니다.
덜 사용되는 스레드화
예를 들어 문자열 스레딩이 있습니다.이 경우 연산은 문자열로 식별되며 보통 해시 테이블로 식별됩니다.이것은 Charles H. Moore의 초기 Fourth 구현과 일리노이 대학의 실험적인 하드웨어 인터프리터 컴퓨터 언어에 사용되었습니다.그것은 또한 Bashforth에서도 사용된다.
RPL
HP-18C Calculator에서 1986년에 처음 도입된 HP의 RPL은 다른 TIL과 달리 RPL "객체"를 "런스트림" 즉, "런스트림"에 삽입할 수 있는 자체 하이브리드 직접 스레드 및 간접 스레드 스레드 인터프리터 언어입니다.인터프리터 포인터가 전진하는 주소의 스트림.RPL "객체"는 메모리 내 구조가 객체의 시작 부분에서 "객체 프롤로그"에 대한 주소를 포함하고 데이터 또는 실행 가능 코드가 이어지는 특별한 데이터 유형이라고 생각할 수 있다.오브젝트 프롤로그는 오브젝트의 본문을 실행 또는 처리하는 방법을 결정합니다.William C에 의해 발명되어 발행된 (및 특허 취득된) "RPL 내부 루프"[12]를 사용합니다.1986년 Wickes가 Applied Fourth Research, Inc., 1988년 "Programming Environments"(프로그래밍 환경)에 발표되었을 때 실행 내용은 다음과 같습니다.
- IP(명령 포인터)를 참조 해제하고 O(현재 개체 포인터)에 저장합니다.
- IP를 1개의 주소 포인터 길이만큼 늘립니다.
- O를 참조 해제하고 주소를 O_1에 저장합니다(이것은 간접의 두 번째 수준입니다).
- PC(프로그램 카운터)를 O_1에 1개의 주소 포인터를 추가하여 다음 포인터 또는 임베디드 객체로 제어 전환
- 순서 1로 돌아가다
이는 다음과 같이 보다 정확하게 나타낼 수 있습니다.
O = [I] I = I + δ PC = [ O ] + δ
위에서 O는 현재 객체 포인터, I는 인터프리터 포인터, δ는 하나의 주소 워드의 길이, "[]" 연산자는 "참조 해제"를 나타냅니다.
컨트롤이 오브젝트 포인터 또는 임베디드 오브젝트로 전송되면 다음과 같이 실행이 계속됩니다.
프롤로그 ->. 프롤로그(그prolog 코드의 시작의 프롤로그 주소 자체를 가리키)IFO+Δ=/= PC그 GOTO INDIRECT(직접 실행을 위한 시험)O)나는-Δ(정답 O포함 개체의 시작을 지목하기)나는 정도 나는 + α(개체의 α은 길이 나는 포함된 목적을 가리키는 옳게 고치시오.)INDIRECT( 있다.세인트의 프롤로그)
RPL을 사용하는 HP의 새턴 마이크로프로세서에서는 아키텍처/프로그래밍 트릭에 의해 가능한 세 번째 수준의 인터다이렉션이 있어 보다 빠른 [12]실행을 가능하게 합니다.
나뭇가지
모든 인터프리터에서 브랜치는 단순히 스레드 포인터를 변경합니다.ip
)를 스레드내의 다른 주소로 송신합니다.top-of-stack 값이 0인 경우에만 점프를 하는 조건부 jump-if-zero 브랜치를 다음과 같이 구현할 수 있습니다.이 예에서는 다이렉트스레딩의 내장 파라미터 버전을 사용하고 있기 때문에&thread[123]
line은 조건이 true일 때 점프하는 수신처이므로 건너뛸 필요가 있습니다( ).ip++
over (브런치를 취득하지 않은 경우는, 덮어씁니다.
실: ... &브리즈 &실[123] ... 브리즈: when_true_ip = *아이피++ // 지점의 대상 주소 가져오기 한다면 (*--sp == 0) // 탑 오브 스택을 팝업/소비하여 0인지 확인합니다. 아이피 = when_true_ip 뛰어내리다 *아이피++
공통 어메니티
머신 내에서 데이터와 리턴 스택을 분리하면 스택 관리 코드가 대폭 삭제되어 스레드 코드의 크기가 대폭 줄어듭니다.듀얼 스택 원칙은 Burroughs 대형 시스템, Fourth 및 PostScript에서 독립적으로 세 번 시작되었습니다.일부 Java 가상 시스템에서 사용됩니다.
스레드 가상 머신에는 보통 3개의 레지스터가 있습니다.서브루틴('워드') 간에 데이터를 전달하기 위한 또 다른 것이 존재한다.다음과 같습니다.
- 가상 머신의 ip 또는 i(명령 포인터) (VM을 구현하는 기본 하드웨어의 프로그램 카운터와 혼동하지 말 것)
- w(작업 포인터)
- rp 또는 r(리턴스택 포인터)
- sp 또는 s(단어 간에 매개 변수를 전달하기 위한 매개 변수 스택 포인터)
대부분의 경우 Forth 구현과 같은 스레드 가상 머신에는 3개의 기본 요소로 구성된 단순한 가상 머신이 있습니다.다음과 같은 것이 있습니다.
- 도콜이라고도 하는 둥지
- unnest 또는 semi_s(s;s)
- 다음 분.
여기에 제시된 간접 스레드 가상 시스템의 작업은 다음과 같습니다.
다음 분.: *아이피++ -> w 뛰어내리다 **w++ 둥지: 아이피 -> *rp++ w -> 아이피 다음 분. 둥지를 틀다: *--rp -> 아이피 다음 분.
이것은 아마도 가장 간단하고 빠른 인터프리터 또는 가상 머신일 것입니다[citation needed].
「 」를 참조해 주세요.
- 글로벌 변수를 대체하는 Continuation-passing 스타일
ip
함수 매개 변수를 사용하여 - 적시 컴파일
- 반환 지향 프로그래밍: 원격 취약 시스템을 이용하기 위한 스레드 코드 재검출.
- 꼬리 재귀
메모들
레퍼런스
- ^ a b "Speed of various interpreter dispatch techniques V2".
- ^ 데니스 M.Ritchie, "C 언어의 발전", 1993.견적: "PDP-7의 B 컴파일러는 기계 명령을 생성하지 않고 "스레드 코드"를 생성했습니다."
- ^ 데이비드 프레치."muforth readme"섹션 "심플하고 반복적인 네이티브 컴파일러"
- ^ a b 스티브 헬러."효율적인 C/C++ 프로그래밍: 더 작고, 더 빠르고, 더 나은." 2014.5장: "통역이 필요합니까?" 페이지 195.
- ^ 장 폴 트램블레이, P. G. 소렌슨"컴파일러 글쓰기의 이론과 실천" 1985. 페이지 527
- ^ "무선 세계: 전자, 라디오, 텔레비전, 제89권." 페이지 73.
- ^ "바이트, 볼륨 5", 1980. 페이지 212
- ^ Bell, James R. (1973). "Threaded code". Communications of the ACM. 16 (6): 370–372. doi:10.1145/362248.362270.
- ^ Moore, Charles H.는 Byte Magazine Fourth Issue에 코멘트를 게재했습니다.
- ^ Ertl, Anton. "What is Threaded Code?".
- ^ Latendresse, Mario; Feeley, Marc. Generation of Fast Interpreters for Huffman-Compressed Bytecode. Elsevier. CiteSeerX 10.1.1.156.2546.
- ^ a b 버스비, 조나단."RPL 내부 루프 설명", "HP 계산기 박물관", 2018년 9월 7일, 2019년 12월 27일 취득
- ^ Wickes, William C. (May 30, 1986). "Data processing system and method for the direct and indirect execution of uniformly structured object types". uspto.gov. Retrieved December 27, 2019.
외부 링크
- Anton Ertl의 설명 페이지 「스레드 코드란」에서는, 다양한 스레드화 기술에 대해 설명하고, 한층 더 참고 자료를 제공합니다.
- Dennis M의 C언어 개발. Ritchie는 "스레드 코드"를 사용하여 구현된 B(C의 전구체)를 설명합니다.
- Thinking Fourse Project에는 1984년에 출판된 레오 브로디의 정석(단, 절판) 책이 포함되어 있습니다.
- 레오 브로디가 1981년에 출판한 책 Starting FORTH의 온라인 버전을 시작으로.
- Brad Rodriguez의 Moving FOUND: Part 1: Fourth Kernel의 설계 결정에서는 스레드화 기술에 대해 자세히 설명합니다.
- 범용 CPU 이력
- GCC 내선번호값으로서의 라벨
- Horn, Joseph K. "What is RPL?". Archived from the original on 2017-09-17. Retrieved 2017-09-17. (NB. HP 48 등의 HP 계산기에서 사용되는 스레드 언어 시스템 및 사용자 RPL의 개요).