스택의 목적은 무엇입니까? 왜 필요한가요?
그래서 지금 C # .NET 응용 프로그램을 디버깅하는 방법을 배우기 위해 MSIL을 배우고 있습니다.
나는 항상 궁금해했다 : 스택의 목적은 무엇입니까?
내 질문을 맥락에서 설명하자면
왜 메모리에서 스택 또는 "로드"로 전송됩니까? 반면에 왜 스택에서 메모리 또는 "스토리지"로의 전송이 이루어 집니까? 왜 그것들을 모두 메모리에 넣지 않았습니까?
- 더 빠르기 때문입니까?
- RAM 기반이기 때문입니까?
- 효율성을 위해?
CIL 코드를 훨씬 더 깊이 이해할 수 있도록 이것을 이해하려고합니다 .
업데이트 : 나는이 질문을 너무 좋아하여 2011 년 11 월 18 일 내 블로그의 주제 로 만들었습니다 . 좋은 질문 감사합니다!
나는 항상 궁금해했다 : 스택의 목적은 무엇입니까?
런타임에 실제 스레드 당 스택 이 아니라 MSIL 언어 의 평가 스택 을 의미한다고 가정합니다 .
메모리에서 스택 또는 "로드"로 전송되는 이유는 무엇입니까? 반면에 왜 스택에서 메모리 또는 "스토리지"로의 전송이 이루어 집니까? 왜 그것들을 모두 메모리에 넣지 않았습니까?
MSIL은 "가상 머신"언어입니다. C # 컴파일러와 같은 컴파일러는 CIL을 생성 한 다음 런타임에 JIT (Just In Time) 컴파일러라고하는 다른 컴파일러가 IL을 실행할 수있는 실제 기계 코드로 변환합니다.
먼저 "왜 MSIL이 있습니까?"라는 질문에 대답하겠습니다. C # 컴파일러가 머신 코드를 작성하는 이유는 무엇입니까?
이런 식으로 하는 것이 더 저렴 하기 때문입니다 . 그렇게하지 않았다고 가정하자. 각 언어에 자체 기계 코드 생성기가 있어야한다고 가정하십시오. C #, JScript .NET , Visual Basic, IronPython , F # 등 20 개의 언어 가 있으며 10 개의 서로 다른 프로세서가 있다고 가정합니다. 몇 개의 코드 생성기를 작성해야합니까? 20 x 10 = 200 코드 생성기. 많은 작업입니다. 이제 새 프로세서를 추가하려고한다고 가정하십시오. 각 언어마다 하나씩 코드 생성기를 20 번 작성해야합니다.
또한 어렵고 위험한 작업입니다. 전문가가 아닌 칩을위한 효율적인 코드 생성기를 작성하는 것은 어려운 일입니다! 컴파일러 설계자는 새로운 칩 세트의 효율적인 레지스터 할당이 아니라 언어의 의미 론적 분석에 대한 전문가입니다.
이제 CIL 방식으로 수행한다고 가정하겠습니다. 몇 개의 CIL 생성기를 작성해야합니까? 언어 당 하나. 몇 개의 JIT 컴파일러를 작성해야합니까? 프로세서 당 1 개 합계 : 20 + 10 = 30 개의 코드 생성기. 또한 CIL은 간단한 언어이기 때문에 CIL 생성기는 쓰기가 쉽고 CIL은 간단한 언어이기 때문에 CIL- 기계 코드 생성기는 쓰기도 쉽습니다. 우리는 C #과 VB의 모든 복잡성을 제거하고 지터를 작성하기 쉬운 간단한 언어로 모든 것을 "낮게"줄입니다.
중간 언어를 사용하면 새로운 언어 컴파일러를 생산하는 비용이 크게 줄어 듭니다 . 또한 새로운 칩 지원 비용을 대폭 절감합니다. 새로운 칩을 지원하고 싶을 때, 그 칩에 대한 전문가를 찾아 CIL 지터를 작성하게하시면됩니다. 그런 다음 칩에서 모든 언어를 지원합니다.
자, 우리는 왜 MSIL을 가지고 있는지를 설정했습니다. 중간 언어를 사용하면 비용이 낮아지기 때문입니다. 그렇다면 왜 언어가 "스택 머신"입니까?
스택 머신은 개념적으로 언어 컴파일러 작성자가 다루기 매우 단순하기 때문입니다. 스택은 계산을 설명하기위한 간단하고 이해하기 쉬운 메커니즘입니다. 스택 머신은 개념적으로 JIT 컴파일러 작성자가 다루기 매우 쉽습니다. 스택 사용은 단순화 된 추상화이므로 비용이 절감 됩니다.
당신은 "왜 스택을 가지고 있습니까?" 모든 것을 메모리에서 직접 수행하지 않는 이유는 무엇입니까? 글쎄, 그것에 대해 생각해 보자. 다음에 대한 CIL 코드를 생성한다고 가정하십시오.
int x = A() + B() + C() + 10;
"add", "call", "store"등이 항상 인수를 스택에서 가져 와서 스택에 결과 (있는 경우)를 넣는 규칙이 있다고 가정합니다. 이 C #에 대한 CIL 코드를 생성하기 위해 다음과 같이 말합니다.
load the address of x // The stack now contains address of x
call A() // The stack contains address of x and result of A()
call B() // Address of x, result of A(), result of B()
add // Address of x, result of A() + B()
call C() // Address of x, result of A() + B(), result of C()
add // Address of x, result of A() + B() + C()
load 10 // Address of x, result of A() + B() + C(), 10
add // Address of x, result of A() + B() + C() + 10
store in address // The result is now stored in x, and the stack is empty.
이제 스택없이 수행했다고 가정 해보십시오. 모든 opcode가 피연산자의 주소와 결과를 저장하는 주소를 취하는 방식으로 수행합니다 .
Allocate temporary store T1 for result of A()
Call A() with the address of T1
Allocate temporary store T2 for result of B()
Call B() with the address of T2
Allocate temporary store T3 for the result of the first addition
Add contents of T1 to T2, then store the result into the address of T3
Allocate temporary store T4 for the result of C()
Call C() with the address of T4
Allocate temporary store T5 for result of the second addition
...
어떻게되는지 봤어? 일반적으로 규칙에 따라 모든 임시 저장소를 명시 적으로 할당해야하기 때문에 코드가 엄청나게 커지고 있습니다. 더 나쁜 것은, 우리의 opcode 자체는 모두 결과가 쓰여질 주소와 각 피연산자의 주소를 인수로 취해야하기 때문에 모두 엄청나게 커지고 있다는 것입니다. 스택에서 두 가지 작업을 수행하고 한 가지 작업을 수행한다는 것을 알고있는 "추가"명령은 단일 바이트 일 수 있습니다. 두 개의 피연산자 주소와 결과 주소를 취하는 add 명령어는 엄청납니다.
스택은 일반적인 문제를 해결하기 때문에 스택 기반 opcode를 사용 합니다. 즉 : 임시 저장 공간을 할당하고 곧 사용하고 완료되면 빨리 제거하려고합니다 . 처리에 스택이 있다고 가정함으로써 opcode를 매우 작게 만들고 코드를 간결하게 만들 수 있습니다.
업데이트 : 몇 가지 추가 생각
덧붙여서, (1) 가상 머신 지정, (2) VM 언어를 대상으로하는 컴파일러 작성 및 (3) 다양한 하드웨어에서 VM 구현을 작성함으로써 비용을 대폭 절감한다는이 아이디어는 전혀 새로운 아이디어가 아닙니다. . MSIL, LLVM, Java 바이트 코드 또는 다른 최신 인프라에서 시작되지 않았습니다. 내가 아는이 전략의 초기 구현은 1966 년 의 pcode 시스템입니다 .
내가 개인적으로이 개념에 대해 들어 본 첫 번째는 Infocom 구현자가 Zork을 여러 다른 컴퓨터에서 잘 작동 시키는 방법을 알게되었을 때였습니다 . Z- machine이라는 가상 머신을 지정한 다음 게임을 실행하려는 모든 하드웨어에 대해 Z-machine 에뮬레이터를 만들었습니다. 이는 원시 8 비트 시스템에서 가상 메모리 관리 를 구현할 수 있다는 엄청난 이점이있었습니다 . 게임은 필요할 때 디스크에서 코드를 페이징하고 새로운 코드를로드 할 때 버릴 수 있기 때문에 메모리에 맞는 것보다 더 클 수 있습니다.
MSIL에 대해 이야기 할 때는 가상 컴퓨터에 대한 지침에 대해 이야기하고 있습니다. .NET에서 사용되는 VM은 스택 기반 가상 머신입니다. 레지스터 기반 VM과 달리 Android 운영 체제에서 사용되는 Dalvik VM 이 그 예입니다.
VM의 스택은 가상이며, VM 명령어를 프로세서에서 실행되는 실제 코드로 변환하는 것은 인터프리터 또는 적시 컴파일러에 달려 있습니다. .NET의 경우 거의 항상 지터 인 MSIL 명령 세트는 시작부터 지칠 수 있도록 설계되었습니다. 예를 들어 Java 바이트 코드와 달리 특정 데이터 유형에 대한 작업에 대한 고유 한 명령이 있습니다. 해석하기에 최적화되었습니다. MSIL 인터프리터는 실제로 존재하지만 .NET Micro Framework에서 사용됩니다. 리소스가 매우 제한된 프로세서에서 실행되는 머신 코드를 저장하는 데 필요한 RAM을 감당할 수 없습니다.
실제 기계 코드 모델은 스택과 레지스터가 모두 혼합되어 있습니다. JIT 코드 최적화 프로그램의 큰 작업 중 하나는 스택에 유지되는 변수를 레지스터에 저장하여 실행 속도를 크게 향상시키는 방법을 고안하는 것입니다. Dalvik 지터에는 반대의 문제가 있습니다.
머신 스택은 프로세서 설계에서 오랫동안 사용되어 온 매우 기본적인 스토리지 기능입니다. RAM이 제공 할 수있는 것보다 훨씬 빠른 속도로 데이터를 씹어 재귀를 지원하는 최신 CPU에서 매우 중요한 기능인 참조의 로컬 성이 매우 우수합니다. 언어 디자인은 스택을 가지고 지역 변수 및 메소드 본문에 제한되는 범위를 지원할 수있는 스택을 통해 크게 영향을받습니다. 스택의 중요한 문제는이 사이트의 이름입니다.
이것에 대해 매우 흥미롭고 자세한 Wikipedia 기사, 스택 머신 명령어 세트의 장점 이 있습니다. 나는 그것을 완전히 인용해야하기 때문에 단순히 링크를 넣는 것이 더 쉽습니다. 난 그냥 자막을 인용합니다
- 초소형 객체 코드
- 간단한 컴파일러 / 간단한 인터프리터
- 최소 프로세서 상태
스택 질문에 조금 더 추가하십시오. 스택 개념은 산술 논리 장치 (ALU)의 기계 코드가 스택에있는 피연산자에서 작동하는 CPU 설계에서 파생됩니다. 예를 들어 곱하기 연산은 스택에서 두 개의 최상위 피연산자를 가져 와서 여러 개를 계산 한 다음 결과를 스택에 다시 배치 할 수 있습니다. 기계 언어에는 일반적으로 스택에서 피연산자를 추가하고 제거하는 두 가지 기본 기능이 있습니다. 푸시와 팝. 많은 CPU의 dsp (디지털 신호 프로세서)와 머신 콘트롤러 (세탁기 제어와 같은)에서 스택은 칩 자체에 있습니다. 이를 통해 ALU에보다 빠르게 액세스하고 필요한 기능을 단일 칩으로 통합합니다.
스택 / 힙의 개념을 따르지 않고 데이터가 임의의 메모리 위치에로드되거나 임의의 메모리 위치에서 데이터가 저장된 경우 매우 구조화되지 않고 관리되지 않습니다.
이러한 개념은 성능, 메모리 사용량을 개선하기 위해 데이터 구조를 사전 정의 된 구조에 저장하는 데 사용됩니다.
연속 전달 스타일 의 코딩 을 사용하여 스택없이 시스템을 작동시킬 수 있습니다 . 그런 다음 호출 프레임은 가비지 수집 힙에 할당 된 연속이됩니다 (가비지 수집기는 일부 스택이 필요함).
Andrew Appel의 오래된 저서 : 연속 및 가비지 콜렉션으로 컴파일하는 것이 스택 할당보다 빠를 수 있음을 참조하십시오.
(캐시 문제로 인해 오늘 약간 잘못되었을 수 있습니다)
참고 URL : https://stackoverflow.com/questions/7875253/what-is-the-purpose-of-a-stack-why-do-we-need-it
'Programing' 카테고리의 다른 글
System.gc ()를 호출하는 것이 왜 나쁜 습관입니까? (0) | 2020.03.09 |
---|---|
루비 해시 객체를 JSON으로 변환하는 방법? (0) | 2020.03.09 |
@RequestParam 및 @PathVariable (0) | 2020.03.09 |
모노는 프라임 타임 준비가 되셨습니까? (0) | 2020.03.09 |
별도의 AngularJS 컨트롤러 파일을 만드는 방법은 무엇입니까? (0) | 2020.03.09 |