2009-03-23 7 views
37

스택 정렬이란 무엇입니까? 왜 사용됩니까? 컴파일러 설정으로 제어 할 수 있습니까?"스택 정렬"이란 무엇입니까?

이 질문의 세부 사항은 msvc에서 ffmpeg 라이브러리를 사용하려고 할 때 직면하게되는 문제에서 취해졌지만 실제로 관심이있는 것은 "스택 정렬"에 대한 설명입니다.

세부 정보 :

  • 내 MSVC 내가 다음과 같은 오류 얻을 avcodec에 대한 링크 프로그램을 준수 runnig : 충돌에 이어, "컴파일러는 스택 변수를 정렬되지 않은를 위해 libavcodec은 가 잘못 컴파일되었습니다." avcodec.dll.
  • avcodec.dll이 msvc로 컴파일되지 않아 내부에서 무슨 일이 벌어지고 있는지 볼 수 없습니다.
  • ffmpeg.exe를 실행하고 동일한 avcodec.dll을 사용할 때 모든 것이 잘 작동합니다.
  • ffmpeg.exe MSVC이 컴파일되지 않았가, GCC /와 Mingw (avcodec.dll 동일)

덕분

을 준수 하였다 메모리 변수
+1

다른 사람들은 스택 정렬이 무엇이고 왜 사용되는지 설명 했으므로 _ "컴파일러 설정으로 제어 할 수 있습니까?"라는 두 개의 센트를 추가하려고합니다. [이 질문] 참조 (http://stackoverflow.com/questions/5496045/why-is-my-stack-pointer-only-incrementing-in-multiples-of-16?lq=1) – andreee

답변

91

정렬 (a 약사).

과거 컴퓨터에는 8 비트 데이터 버스가있었습니다. 이것은 각 클럭 사이클에서 8 비트의 정보가 처리 될 수 있음을 의미합니다. 그 때 괜찮 았어.

그런 다음 16 비트 컴퓨터가 나타납니다. 하위 호환성 및 기타 문제로 인해 8 비트 바이트가 유지되고 16 비트 워드가 도입되었습니다. 각 단어는 2 바이트였습니다. 그리고 각 클록 사이클의 16 비트 정보가 처리 될 수 있습니다. 그러나 이것은 작은 문제를 야기했다. 메모리 맵에서

살펴 보자 :

+----+ 
|0000| 
|0001| 
+----+ 
|0002| 
|0003| 
+----+ 
|0004| 
|0005| 
+----+ 
| .. | 

각 주소에서 개별적으로 액세스 할 수있는 바이트가있다. 그러나 단어는 짝수 주소에서만 가져올 수 있습니다. 따라서 0000에서 한 단어를 읽으면 0000과 0001의 바이트를 읽습니다. 그러나 0001 위치의 워드를 읽으려면 두 개의 읽기 액세스가 필요합니다. 처음 0000,0001 다음 00020003 그리고 우리는 단지 0001,0002 만 유지합니다.

물론 이것은 약간의 시간이 걸렸습니다. 그래서 그것이 그들이 정렬을 발명 한 이유입니다. 따라서 우리는 단어 경계에서 단어 변수를 저장하고 바이트 경계에서 바이트 변수를 저장합니다. 재미없는

+----+ 
|0000| B 
|0001| W 
+----+ 
|0002| W 
|0003| 
+----+ 

: 우리가 바이트 필드 (B) 및 워드 필드 (W) (매우 순진 컴파일러)와 구조가있는 경우

예를 들어, 우리는 다음과 같은 수 . 그러나 단어 맞춤을 사용할 때 다음을 찾습니다.

+----+ 
|0000| B 
|0001| - 
+----+ 
|0002| W 
|0003| W 
+----+ 

여기서 액세스 속도는 메모리를 희생합니다.

더블 워드 (4 바이트) 또는 쿼드 워드 (8 바이트)를 사용할 때 이것이 훨씬 더 중요하다고 생각할 수 있습니다. 그렇기 때문에 대부분의 최신 컴파일러에서 프로그램을 컴파일하는 동안 어떤 정렬을 사용할지 선택할 수 있습니다.

+3

스택 정렬에 대한 훌륭한 설명 ! –

+0

나는 어셈블리를 배우려고 노력하고 있으며, 나는 정렬을 이해하는 데 어려움을 겪고있다. 이 질문은 전적으로 내 질문에 대한 답변입니다. – joek1975

+0

항상 누군가를 도울 수있어서 기뻐합니다 :-). –

11

IIRC, 스택 정렬은 변수가 스택에 배치되어 특정 바이트 수에 "정렬"되는 경우입니다. 따라서 16 비트 스택 정렬을 사용하는 경우 스택의 각 변수는 함수 내의 현재 스택 포인터에서 2 바이트의 배수 인 바이트에서 시작합니다.

즉, 문자 (1 바이트)와 같이 < 2 바이트 변수를 사용하면 변수와 다음 변수 사이에 8 비트의 "패딩"이 사용됩니다. 이를 통해 변수 위치를 기반으로 한 가정을 통한 특정 최적화가 가능합니다.

함수를 호출 할 때 인수를 다음 함수에 전달하는 한 가지 방법은 함수를 레지스터에 직접 배치하는 것이 아니라 스택에 배치하는 것입니다. 호출 함수가 스택에 변수를 배치하고 오프셋을 사용하여 호출 함수에서 읽을 수 있도록 정렬을 사용할지 여부는 중요합니다. 호출 함수가 변수를 정렬하고 호출 된 함수가 변수를 정렬하지 않을 것으로 예상하면 호출 된 함수는 변수를 찾을 수 없습니다.

msvc 컴파일 된 코드가 변수 정렬에 대해 동의하지 않는 것으로 보입니다. 모든 최적화가 해제 된 컴파일을 시도하십시오.

+1

sizeof (char)는 항상 1 바이트이며 항상 8 비트 이상입니다. 바이트가 아닙니다. 정렬은 컴파일러 플랫폼에 따라 다르며 (x86, 여하튼) 32 비트 아키텍처에서는 4 바이트, 64 비트 아치에서는 8 바이트입니다. – snemarch

+1

감사합니다. 실제로 뇌의 크기가 바이트였습니다 : P. 임의의 예제로 16 바이트를 선택했지만 작은 예제를 사용하면 훨씬 명확 해집니다. –

10

일부 CPU 아키텍처에서는 다양한 데이터 유형의 특정 정렬이 필요하며이 규칙을 따르지 않으면 예외가 발생합니다. 표준 모드에서 x86은 기본 데이터 유형에는이 기능이 필요하지 않지만 성능 저하가 발생할 수 있습니다 (저수준 최적화 팁은 www.agner.org에서 확인하십시오).

그러나 오디오/비디오 처리에 필요한명령어 세트는 정렬 요구 사항이 엄격하며 정렬되지 않은 데이터에서 사용하려고하면 예외가 발생합니다 (일부 프로세서에서는 사용하지 않는 경우) 훨씬 느린 정렬되지 않은 버전). 다른 하나는 필요한 경우 스택을 정렬 수신자 기대하면서

문제는, 하나의 컴파일러는 정렬 된 스택을 유지하기 위해 발신자을 기대 아마 것입니다.

편집는 : 예외가 발생하는 이유에 관해서는, DLL의 루틴은 아마 일시적으로 스택 데이터에 SSE 명령어를 사용하기를 원하고, 두 개의 서로 다른 컴파일러 호출 규칙에 동의하지 않기 때문에 실패합니다.

2

필자가 아는 한 컴파일러는 일반적으로 스택에있는 변수를 정렬하지 않습니다. 라이브러리는 컴파일러에서 지원되지 않는 일부 컴파일러 옵션에 따라 달라질 수 있습니다. 일반적인 수정은 정적으로 정렬해야하는 변수를 선언하는 것이지만 다른 사람의 코드에서이 작업을 수행하는 경우 해당 변수가 나중에 함수에서 초기화되는 것을 확인하는 것이 좋습니다. 선언.

// Some compilers won't align this as it's on the stack... 
int __declspec(align(32)) needsToBe32Aligned = 0; 
// Change to 
static int __declspec(align(32)) needsToBe32Aligned; 
needsToBe32Aligned = 0; 

또는 스택의 변수를 정렬하는 컴파일러 스위치를 찾으십시오. 분명히 여기에 사용 된 "__declspec"정렬 구문은 컴파일러에서 사용하지 않을 수도 있습니다.