|
1, 2
우선, 직접 작성해서 잘 도는 걸 확인한 매크로를 올렸습니다.
애초에 push pop 할 생각이었으면 함수로 작성했겠지요.
(push pop 세 개 정도로야 함수 호출로 인한 분기 보단 부하가 적겠지만
다른 경우들에도 파이프라인이 깨지는 등 퍼포먼스 장애요소이기도 하죠)
사실, 범용레지스터인 eax, ebx, edx (A, B, C, D)는 대피가 필요한 레지스터가 아닙니다.
고의적으로 에러를 유도하는 코드를 넣지 않는다면 말이죠.
어셈블리 코드 사용에서 push, pop은 기본이라 생각할지 모르지만
C/C++ 컴파일러의 어셈코드 전환 매카니즘은 대략 이렇습니다.
- 대부분의 전역 변수는 요휴주소를 로드하는 lea 로 꺼내 쓰게 됩니다.
- 대부분의 지역 변수는 ebp - 주소 로 스택에서 꺼내 쓰게 됩니다.
- 지역 변수를 register 로 강제 선언해서 레지스터로 쓰려고 해도 가용 레지스터가 있는지
살펴보고 뒤의 연산에서 다른 용도로 사용되고, 그 연산이 꼬여 있다면 ebp - 주소로
스택에서 꺼내 씁니다. (mov eax, [ebp-스택시작기준상대주소])
따라서 범용레지스터는 lvalue 형태로 초기화 되서 사용되게 됩니다.
가용 레지스터가 있다고 해도, esi, edi 같은 레지스터를 우선적으로 사용하지
범용레지스터는 잘 건드리지 않습니다. (물론 사용되지 않고 꼬일 염려 없다면 꺼내 쓰죠)
- 즉, 값을 스택에 저장하고 스택 포인터를 이동시키는 연산 보다
사용되지 않는 레지스터에 백업하는게 더 싸고, 컴파일러는 바보가 아닌지라
변경된 레지스터에 대해선 lvalue로 다시 꺼내 씁니다.
- 풀 어셈 코드가 아닌 인라인 어셈코드에서 대부분의 대피 메카니즘에서 사람이 강제화
하는것 보다 컴파일러의 대피 메카니즘에 맡기는게 더 쌉니다.
실제 이런류의 매크로를 게임 루프 같은데서 많이 써 봤으니
코딩 스타일과도 관련이 있겠지요.
주로 세그먼트 레지스터들이나 스택 관련 / 포인터들이 대피가 필요한 정도죠.
3. QueryPerformanceCounter 역시 내부적으론 RDTSC를 쓴다고 알고 있습니다.
어짜피 CPU 종류를 판별하는 주파수는 Ghz 단위 소숫점 첫째 자리까지 유효하죠.
1초를 돌려도 0.1 정도의 정확도는 맞습니다. 다만 반올림이 필요한 정도.
실수로 구했다면 0.05 를 더해서 *10 / 10 하시면 되고 정수상태로 구하면 더 좋겠지요.
하지만 종류를 판별하는 주파수가 아닌 실제 속도란건 별로 의미 없습니다.
CPU의 속도 자체란게 항상 일정하지 않으니까요.
똑같이 2.8Ghz 로 출시된 CPU 도 크게는 몇~몇십 Mhz 씩은 속도 차가 나게 됩니다.
몇 ~ hz 로 일정시간 구동시켜 문제가 없으면 해당 주파수로, 문제가 있다면
한 단계 낮춘 주파수로 출시되는 것 뿐이니까요.
|