문제

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

이 질문의 세부 사항은 msvc와 함께 ffmpeg 라이브러리를 사용하려고 할 때 직면한 문제에서 가져온 것입니다. 그러나 제가 정말로 관심을 갖는 것은 "스택 정렬"이 무엇인지에 대한 설명입니다.

세부사항:

  • runnig가 avcodec에 링크하는 MSVC 준수 프로그램을 다음과 같은 오류가 발생합니다."컴파일러가 스택 변수를 정렬하지 않았습니다.Libavcodec은 잘못 컴파일 된 다음 Avcodec.dll에서 충돌이 발생했습니다.
  • avcodec.dll이 msvc로 컴파일되지 않았기 때문에 내부에서 무슨 일이 일어나고 있는지 볼 수 없습니다.
  • ffmpeg.exe를 실행하고 동일한 avcodec.dll을 사용하면 모든 것이 잘 작동합니다.
  • ffmpeg.exe는 msvc로 컴파일되지 않았으며 gcc/mingw와 호환되었습니다(avcodec.dll과 동일).

감사해요,

도움이 되었습니까?

해결책

메모리 내 변수 정렬(짧은 기록)

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

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

메모리 맵을 살펴보겠습니다.

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

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

물론 이것은 약간의 시간이 더 걸렸고 그것은 감사하지 않았습니다.그래서 그들이 정렬을 발명한 것입니다.그래서 우리는 단어 경계에 단어 변수를 저장하고 바이트 경계에 바이트 변수를 저장합니다.

예를 들어, 바이트 필드(B)와 단어 필드(W)(및 매우 순진한 컴파일러)가 있는 구조가 있는 경우 다음을 얻습니다.

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

재미 있지 않습니다.그러나 단어 정렬을 사용하면 다음과 같은 결과를 얻을 수 있습니다.

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

여기서는 액세스 속도를 위해 메모리가 희생됩니다.

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

다른 팁

일부 CPU 아키텍처에는 다양한 데이터 유형의 특정 정렬이 필요 하며이 규칙을 존중하지 않으면 예외를 제외합니다. 표준 모드에서 X86은 기본 데이터 유형에 대해서는이를 요구하지 않지만 성능 처벌을받을 수 있습니다 (저수준 최적화 팁은 www.agner.org를 확인하십시오).

그러나, 그 SSE 명령어 세트 (종종 고성능에 사용) 오디오/비디오 프로세싱에는 엄격한 정렬 요구 사항이 있으며, 부정확하지 않은 데이터에서 사용하려고 시도하면 예외가 발생합니다 (일부 프로세서에서는 훨씬 느린 버전을 사용하지 않는 한).

당신의 문제는입니다 아마 하나의 컴파일러가 기대합니다 방문객 스택을 정렬하는 반면 다른 사람은 칼리 필요한 경우 스택을 정렬합니다.

편집하다: 예외가 발생하는 이유에 대해서는 DLL의 루틴은 아마도 일부 임시 스택 데이터에서 SSE 지침을 사용하고 싶을 것입니다. 두 개의 다른 컴파일러가 컨벤션에 동의하지 않기 때문에 실패합니다.

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

즉, char (1 바이트)와 같은 <2 바이트의 변수를 사용하는 경우 IT와 다음 변수 사이에 사용되지 않은 "패딩"이 8 비트가됩니다. 이를 통해 변수 위치를 기반으로하는 가정으로 특정 최적화가 가능합니다.

기능을 호출 할 때 다음 기능에 인수를 전달하는 한 가지 방법은 스택에 직접 배치하는 것입니다 (레지스터에 직접 배치하는 것과는 대조). 호출 함수가 스택의 변수를 스택에 배치하고 오프셋을 사용하여 호출 함수에 의해 판독되기 때문에 여기서 정렬이 사용되는지 여부는 여기에 중요합니다. 호출 함수가 변수를 정렬하고 호출 된 함수가 정렬되지 않을 것으로 예상하면 호출 된 함수는 찾을 수 없습니다.

MSVC 컴파일 코드는 변수 정렬에 대해 동의하지 않는 것 같습니다. 모든 최적화가 꺼진 상태에서 컴파일을 시도하십시오.

내가 아는 한, 컴파일러는 일반적으로 스택에있는 변수를 정렬하지 않습니다. 라이브러리는 컴파일러에서 지원되지 않는 일부 컴파일러 옵션 세트에 따라 다를 수 있습니다. 정상적인 수정은 정적으로 정렬 해야하는 변수를 선언하는 것이지만, 다른 사람들의 코드 에서이 작업을 수행하면 문제의 변수가 나중에 기능이 아닌 기능에서 초기화되어 있는지 확인해야합니다. 선언.

// 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"정렬 구문은 컴파일러가 사용하는 것이 아닐 수도 있습니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top