컴파일러는 프로파일 링과 더 나은 코드 실행을 위해 몇 번 반복하지 않는 한 코드를 실행하지 않지만이를 준비해야합니다 - 프로그램에서 정의한 변수를 유지하는 방법, 레지스터로 효율적으로 저장하거나, 느린 (그리고 더 부작용을 일으키기 쉬운) 메모리를 사용하십시오.
처음에는 지역 변수에 스택 프레임의 위치가 지정됩니다 (동적 할당을 명시 적으로 사용하는 메모리는 제외). 함수가 int를 할당하면 컴파일러는 스택에 몇 바이트 씩 증가하도록 지시하고 해당 메모리 주소를 사용하여 해당 변수를 저장하고 코드가 해당 변수에서 수행하는 모든 연산에 피연산자로 전달할 수 있습니다.
그러나 메모리가 (캐시 된 경우에도) 더 느리고 CPU를 더 많이 사용하기 때문에 나중에 컴파일러에서 일부 변수를 레지스터로 이동하려고 할 수 있습니다. 이 할당은 아키텍처에있는 기존 논리 레지스터 수에 들어갈 수있는 가장 재사용되고 지연 시간이 긴 변수를 선택하려고하는 복잡한 알고리즘을 통해 수행됩니다 (피연산자가이 명령에 포함될 필요가있는 몇 가지 지침과 같은 다양한 제한 사항을 확인하는 동안 또는 그 레지스터).
일부 메모리 주소는 컴파일 타임에 알려지지 않은 방식으로 외부 포인터를 사용하여 별칭을 지정할 수 있습니다.이 경우 레지스터로 이동할 수 없습니다. 컴파일러는 대개 매우 조심스럽고 대다수는 위험한 최적화를 피할 것입니다 (그렇지 않으면 불쾌한 일을 피하기 위해 특별한 검사를해야합니다). 모든 그 후
는, 컴파일러는 여전히 조언 할 정도로 정중있는 변수 것이 중요하고 그가 그것을 놓친, 당신은 기본적으로 그에게 요구하고있는 register
키워드로 다음을 표시하여 경우에, 당신에게 중요한 레지스터를 사용할 수 있고 앨리어싱을 사용할 수 없다는 가정하에이 변수에 대해 레지스터를 사용하여이 변수를 최적화하려고 시도하십시오., 다음 코드를 가지고 두 번 같은 일을하지만 약간 다른 상황에 :
는 여기에 약간의 예제 내가 로컬
#include "stdio.h"
int j;
int main() {
int i;
for (i = 0; i < 100; ++i) {
printf ("i'm here to prevent the loop from being optimized\n");
}
for (j = 0; j < 100; ++j) {
printf ("me too\n");
}
}
주, j는 글로벌이다 (따라서 컴파일러는 '아무튼 실행 중에 다른 사람이 액세스 할 수 있는지 알지 못합니다.) -O3와 GCC에서 컴파일
주에 대해 다음 코드를 생성합니다 : 당신이 볼 수 있듯이, 첫 번째 루프 카운터는 EBX에 앉아, 각각의 반복에 증가 및 한계 비교됩니다
0000000000400540 <main>:
400540: 53 push %rbx
400541: bf 88 06 40 00 mov $0x400688,%edi
400546: bb 01 00 00 00 mov $0x1,%ebx
40054b: e8 18 ff ff ff callq 400468 <[email protected]>
400550: bf 88 06 40 00 mov $0x400688,%edi
400555: 83 c3 01 add $0x1,%ebx # <-- i++
400558: e8 0b ff ff ff callq 400468 <[email protected]>
40055d: 83 fb 64 cmp $0x64,%ebx
400560: 75 ee jne 400550 <main+0x10>
400562: c7 05 80 04 10 00 00 movl $0x0,1049728(%rip) # 5009ec <j>
400569: 00 00 00
40056c: bf c0 06 40 00 mov $0x4006c0,%edi
400571: e8 f2 fe ff ff callq 400468 <[email protected]>
400576: 8b 05 70 04 10 00 mov 1049712(%rip),%eax # 5009ec <j> (loads j)
40057c: 83 c0 01 add $0x1,%eax # <-- j++
40057f: 83 f8 63 cmp $0x63,%eax
400582: 89 05 64 04 10 00 mov %eax,1049700(%rip) # 5009ec <j> (stores j back)
400588: 7e e2 jle 40056c <main+0x2c>
40058a: 5b pop %rbx
40058b: c3 retq
.
그러나 두 번째 루프는 위험한 것이었고 gcc는 메모리를 통해 인덱스 카운터를 전달하기로 결정했습니다 (모든 반복에서 rax로로드). 이 예제는 레지스터를 사용할 때 얼마나 좋을지와 때로는 할 수없는 방법을 보여줍니다.
나는 당신이 묻는 것을 보지 못했습니다. 명령을 실행하려면 변수가 레지스터에 있어야합니다. 이것이 바로 CPU가 작동하는 방식입니다. – Kevin
정확히 무엇을 요구하고 있는지 불분명합니다. 읽는 문서는 컴파일러가 생성하는 코드에 대해 이야기합니다. 즉 C 코드에서 컴파일러는 변수를 메모리에 저장하는 위치, 명령이 실행될 때 사용할 CPU 레지스터 등을 포함하여 CPU 작동 방법을 알려주는 코드를 생성해야합니다. 컴파일러가 해당 코드를 생성하면 컴파일 된 코드를 실행할 때 실제 메모리/레지스터 할당 및 사용이 발생합니다. – nos