메모리 오더링
"오늘의AI위키"의 AI를 통해 더욱 풍부하고 폭넓은 지식 경험을 누리세요.
1. 개요
메모리 오더링은 컴퓨터 시스템에서 메모리 접근의 순서를 정의하는 개념으로, 컴파일 시점과 실행 시점 모두에서 발생한다. 컴파일 시점의 메모리 오더링은 컴파일러가 코드의 성능 향상을 위해 명령어 순서를 재배치할 때 발생하며, 멀티스레드 환경이나 메모리 맵 I/O를 사용하는 시스템에서는 예기치 않은 결과를 초래할 수 있다. 실행 시점의 메모리 오더링은 대칭형 멀티프로세싱(SMP) 시스템에서 여러 프로세서가 메모리에 접근할 때 메모리 일관성 모델에 따라 정의되며, 순차 일관성, 느슨한 일관성, 약한 일관성 등의 모델이 존재한다. 각 CPU 아키텍처는 고유한 메모리 순서 모델을 가지며, 하드웨어 메모리 장벽을 통해 메모리 접근 순서를 제어할 수 있다.
더 읽어볼만한 페이지
- 프로그래밍 언어 설계 - 신택틱 슈거
신택틱 슈거는 프로그래밍 언어에서 가독성과 사용성을 높이기 위해 도입된 문법적 편의 기능으로, 언어의 핵심 기능에 영향을 주지 않으면서 간결하고 직관적인 코드 작성을 돕는다. - 런타임 시스템 - 런타임 라이브러리
런타임 라이브러리는 프로그래밍 언어의 기능을 지원하고 컴파일러에 의해 자동으로 링크되며, 예외 처리, 메모리 할당 등의 기능을 제공하고 디버깅 및 릴리스 용도에 따라 여러 버전이 존재한다. - 런타임 시스템 - 디노 (소프트웨어)
Deno는 Node.js의 대안으로 라이언 달이 개발한 JavaScript 및 TypeScript 런타임 환경으로, 보안 강화, 의존성 관리 단순화, ES 모듈 기본 사용 등 Node.js와 차별화된 특징을 가지며 관련 프로젝트 개발을 통해 생태계를 확장하고 있다. - 일관성 모델 - 원자성
원자성은 분자를 구성하는 원자 수로, 분자는 원자성에 따라 단원자, 이원자, 삼원자 분자 등으로 나뉘며, 금속이나 탄소는 원자성이 2로 간주되고 단원자 분자의 원자성은 분자량을 원자량으로 나누어 계산한다. - 일관성 모델 - 캐시 일관성
캐시 일관성은 다중 프로세서 시스템에서 공유 메모리 데이터의 일관성을 유지하기 위해 읽기 및 쓰기 동작을 정의하며, 스누핑, 디렉터리 기반 방식 등의 메커니즘과 다양한 모델 및 프로토콜이 사용된다.
메모리 오더링 | |
---|---|
개요 | |
접근 순서 | 컴퓨터 메모리에 대한 CPU의 접근 순서 |
상세 정보 | |
관련 주제 | 컴파일러 명령어 파이프라인 메모리 장벽 메모리 모델 (프로그래밍) |
2. 컴파일 시점의 메모리 순서
대부분의 프로그래밍 언어는 문(statements)들이 정의된 순서대로 실행되는 실행 스레드(thread) 개념을 가지고 있다. 기존 컴파일러는 고수준 표현식을 저수준 머신 명령어(instruction) 열로 변환하며, 이는 프로그램 카운터를 기준으로 한다.
컴파일러는 컴파일 시 명령어 실행 순서를 자유롭게 변경할 수 있다. 그러나 메모리 접근 순서가 중요한 경우, 이러한 재배치는 문제를 일으킬 수 있다.[1] 이러한 문제는 주로 멀티스레드 환경이나 메모리 맵 I/O를 사용하는 임베디드 시스템에서 발생한다.
실행 효과는 프로그램 코드 내의 고수준과, 동시 프로그래밍에서 다른 스레드 또는 처리 요소가 보거나 하드웨어 디버깅 보조 도구를 사용하여 머신 상태에 접근할 때의 머신 수준, 두 가지 수준에서 나타난다. 컴파일 시 메모리 순서는 전자에 중점을 두며, 다른 관점에는 관여하지 않는다.[1]
2. 1. 프로그램 순서의 일반적인 문제
대부분의 프로그래밍 언어는 정의된 순서대로 문을 실행하는 실행 스레드 개념을 가지고 있다. 기존 컴파일러는 고급 표현식을 하위 수준 머신의 프로그램 카운터를 기준으로 하는 일련의 저수준 명령어로 변환한다.[1]실행 효과는 두 가지 수준에서 나타난다. 높은 수준의 프로그램 코드 내, 그리고 동시 프로그래밍에서 다른 스레드 또는 처리 요소가 보거나 하드웨어 디버깅 보조 도구를 사용하여 머신 상태에 접근할 때(이러한 지원의 일부는 종종 실행 코어와 별도로 기능적으로 독립적인 회로로 CPU 또는 마이크로컨트롤러에 직접 내장되어 있으며, 코어 자체가 실행 상태의 정적 검사를 위해 중단된 경우에도 계속 작동) 머신 수준에서 나타난다.[1] 컴파일 시간 메모리 순서는 전자에 중점을 두며, 이러한 다른 관점에는 관여하지 않는다.[1]
컴파일러는 컴파일 시 명령어 실행 순서를 자유롭게 변경할 수 있지만, 메모리 접근 순서가 중요한 경우 문제가 발생한다.[1]
요약에 따르면, 컴파일러는 표현식 평가나 함수 호출 순서를 최적화할 수 있다. 이러한 내용은 하위 섹션에서 자세히 다루어지므로, 여기서는 간략하게 언급만 하고 넘어간다.
2. 1. 1. 표현식 평가로 인한 프로그램 순서의 영향
절차적 프로그래밍 언어에서, 컴파일하는 동안 하드웨어 명령어는 종종 고급 코드에 지정된 것보다 더 세분화된 방식으로 생성된다. 주요 관찰 가능한 효과는 명명된 변수에 새 값을 할당하는 것이다.예를 들어, 다음과 같은 코드를 보자.
```
sum = a + b + c;
print(sum);
```
`print` 문은 변수 `sum`에 값을 할당하는 문 다음에 오므로, `print` 문이 계산된 변수 `sum`을 참조할 때 이전 실행 시퀀스의 관찰 가능한 결과로 이 결과를 참조한다. 프로그램 시퀀스의 규칙에 따라, `print` 함수 호출이 `sum`을 참조할 때, `sum`의 값은 변수 `sum`에 가장 최근에 실행된 할당의 값이어야 한다(이 경우 바로 앞의 문).
머신 레벨에서, 세 개의 숫자를 하나의 명령어로 더할 수 있는 머신은 거의 없으므로, 컴파일러는 이 표현식을 두 개의 덧셈 연산으로 변환해야 한다. 프로그래밍 언어의 의미론이 컴파일러가 표현식을 왼쪽에서 오른쪽 순서로 변환하도록 제한하는 경우 생성되는 코드는 프로그래머가 원래 프로그램에 다음과 같은 문을 작성한 것처럼 보일 것이다.
```
sum = a + b;
sum = sum + c;
```
컴파일러가 덧셈의 결합 법칙을 활용하도록 허용되면 대신 다음과 같이 생성될 수 있다.
```
sum = b + c;
sum = a + sum;
```
컴파일러가 덧셈의 교환 법칙을 활용하도록 허용되면 대신 다음과 같이 생성될 수 있다.
```
sum = a + c;
sum = sum + b;
```
대부분의 프로그래밍 언어에서 정수 데이터 유형은 정수 오버플로가 없는 경우에만 수학적 정수에 대한 대수를 따르며, 대부분의 프로그래밍 언어에서 사용 가능한 부동 소수점 데이터 유형에 대한 부동 소수점 연산은 반올림 효과에서 교환적이지 않으므로, 계산된 결과의 작은 차이에서 표현식 순서의 효과를 볼 수 있다(그러나 작은 초기 차이는 더 긴 계산에서 임의로 큰 차이로 이어질 수 있다).
만약 프로그래머가 정수 오버플로 또는 부동 소수점의 반올림 효과에 대해 걱정한다면, 다음과 같이 코드를 작성하여 연산 순서를 명시적으로 지정할 수 있다.
```
sum = a + b;
sum = sum + c;
2. 1. 2. 함수 호출로 인한 프로그램 순서의 영향
많은 프로그래밍 언어에서는 문(statement)의 경계를 순서점으로 처리하여, 한 문장의 모든 효과가 다음 문장이 실행되기 전에 완료되도록 한다. 이렇게 하면 컴파일러가 작성된 문장 순서대로 코드를 생성하게 된다.[1] 하지만 문장은 내부 함수 호출을 포함하는 등 더 복잡해질 수 있다.[1]예를 들어, 다음과 같은 코드를 생각해 보자.
```
sum = f(a) + g(b) + h(c);
```
기계 수준에서 함수를 호출하는 것은 일반적으로 함수 호출을 위한 스택 프레임을 설정하는 것을 포함하며, 이는 기계 메모리에 대한 많은 읽기 및 쓰기를 포함한다.[1] 대부분의 컴파일 언어에서 컴파일러는 `f`, `g`, `h` 함수 호출을 임의의 순서대로 정렬할 수 있으며, 이는 프로그램 메모리 순서에 큰 변화를 가져올 수 있다.[1]
그러나 순수 함수형 언어에서는 함수 호출이 프로그램 상태에 부작용을 일으키지 않으므로 (반환 값 제외) 함수 호출 순서에 따른 기계 메모리 순서의 차이는 프로그램 의미에 영향을 주지 않는다.[1] 반면, 절차적 언어에서는 호출된 함수가 입출력 작업을 수행하거나 전역 변수를 변경하는 등의 부작용을 가질 수 있으며, 이는 프로그램 모델에서 관찰 가능한 효과를 생성한다.[1]
만약 프로그래머가 이러한 부작용에 신경 쓴다면, 원래 소스 코드를 다음과 같이 더 명확하게 표현할 수 있다.[1]
```
sum = f(a);
sum = sum + g(b);
sum = sum + h(c);
```
문 경계가 순서점으로 정의된 프로그래밍 언어에서는 함수 호출 `f`, `g`, `h`가 작성된 순서대로 실행되어야 한다.[1]
2. 2. 메모리 순서의 구체적인 문제
대부분의 프로그래밍 언어는 정의된 순서대로 문을 실행하는 실행 스레드 개념을 가지고 있다. 기존 컴파일러는 고급 표현식을 하위 수준 머신의 프로그램 카운터를 기준으로 하는 일련의 저수준 명령어로 변환한다.실행 효과는 두 가지 수준에서 나타난다. 하나는 높은 수준의 프로그램 코드 내이고, 다른 하나는 동시 프로그래밍에서 다른 스레드 또는 처리 요소가 보거나, 하드웨어 디버깅 보조 도구를 사용하여 머신 상태에 접근할 때(이러한 지원의 일부는 종종 실행 코어와 별도로 기능적으로 독립적인 회로로 CPU 또는 마이크로컨트롤러에 직접 내장되어 있으며, 코어 자체가 실행 상태의 정적 검사를 위해 중단된 경우에도 계속 작동)의 머신 수준이다. 컴파일 시간 메모리 순서는 전자에 중점을 두며, 이러한 다른 관점에는 관여하지 않는다.
컴파일러는 컴파일 시 명령 실행 순서를 자유롭게 변경할 수 있지만, 메모리 접근 순서가 중요한 경우 문제가 발생한다.
2. 2. 1. 포인터 표현으로 인한 프로그램 순서의 영향
csum = *a + *b + *c;
```
`*x`라는 표현식을 평가하는 것은 포인터를 "역참조"하는 것이라고 하며, `x`의 현재 값으로 지정된 위치의 메모리에서 읽는 것을 포함한다.[1] 포인터에서 읽는 효과는 아키텍처의 메모리 모델에 의해 결정된다.[1] 표준 프로그램 저장소에서 읽을 때 메모리 읽기 작업의 순서로 인한 부작용은 없다.[1] 임베디드 시스템 프로그래밍에서는 메모리에 대한 읽기 및 쓰기가 I/O 작업을 트리거하거나 프로세서의 작동 모드를 변경하여 매우 눈에 띄는 부작용을 일으키는 메모리 맵 I/O를 갖는 것이 매우 일반적이다.[1] 위의 예에서 포인터가 이러한 부작용 없이 일반 프로그램 메모리를 가리킨다고 가정한다.[1] 컴파일러는 프로그램 순서대로 이러한 읽기를 자유롭게 재정렬할 수 있으며 프로그램에서 보이는 부작용은 없다.[1]
할당된 값이 포인터 간접 참조도 하는 경우는 다음과 같다.[2]
```c
- sum = *a + *b + *c;
```
여기서 언어 정의는 컴파일러가 다음과 같이 분리하는 것을 허용하지 않을 가능성이 크다.[2]
```c
// 컴파일러에 의해 재작성됨
// 일반적으로 금지됨
- sum = *a + *b;
- sum = *sum + *c;
```
이것은 대부분의 경우 효율적이지 않은 것으로 간주되며, 포인터 쓰기는 보이는 머신 상태에 잠재적인 부작용을 가질 수 있다.[2] 컴파일러는 이 특정 분할 변환을 허용하지 않으므로 `sum`의 메모리 위치에 대한 유일한 쓰기는 값 표현식의 세 포인터 읽기를 논리적으로 따라야 한다.[2]
그러나 프로그래머가 정수 오버플로의 보이는 의미에 대해 걱정하고 다음과 같이 프로그램 수준에서 문을 분리한다고 가정한다.[3]
```c
// 프로그래머가 직접 작성함
// 별칭 관련 문제
- sum = *a + *b;
- sum = *sum + *c;
```
첫 번째 문은 두 개의 메모리 읽기를 인코딩하며, 이는 `*sum`에 대한 첫 번째 쓰기(어떤 순서이든)보다 먼저 발생해야 한다.[3] 두 번째 문은 두 개의 메모리 읽기(어떤 순서든)를 인코딩하며, 이는 `*sum`의 두 번째 업데이트보다 먼저 발생해야 한다.[3] 이렇게 하면 두 개의 덧셈 연산의 순서가 보장되지만, 잠재적으로 주소 별칭이라는 새로운 문제가 발생한다.[3] 이러한 포인터 중 어느 것이든 잠재적으로 동일한 메모리 위치를 참조할 수 있다.[3]
예를 들어, `*c`와 `*sum`이 동일한 메모리 위치로 별칭 지정되고 두 버전의 프로그램을 `*sum`을 둘 다 대신 사용하여 다시 작성해 보면 다음과 같다.[4]
```c
- sum = *a + *b + *sum;
```
여기에는 문제가 없다.[4] 처음에 `*c`로 작성한 원래 값은 `*sum`에 할당될 때 손실되고, 원래 `*sum`의 값도 손실되지만, 이는 처음에 덮어 쓰여졌으며 특별한 문제가 되지 않는다.[4]
```c
// *c와 *sum이 별칭 지정된 경우 프로그램의 모습
- sum = *a + *b;
- sum = *sum + *sum;
```
여기서 `*sum`의 원래 값은 첫 번째 액세스 전에 덮어 쓰여지고, 대신 다음의 대수적 등가물을 얻는다.[4]
```c
// 위의 별칭 지정된 경우의 대수학적 등가물
- sum = (*a + *b) + (*a + *b);
```
이로 인해 문 재정렬로 인해 완전히 다른 값이 `*sum`에 할당된다.[4]
가능한 별칭 효과로 인해 포인터 표현식은 보이는 프로그램 효과를 위험에 빠뜨리지 않고 재정렬하기 어렵다.[5] 일반적인 경우 별칭 효과가 없을 수 있으므로 코드가 이전과 같이 정상적으로 실행되는 것처럼 보인다.[5] 그러나 별칭이 있는 경우에는 심각한 프로그램 오류가 발생할 수 있다.[5] 이러한 경우가 정상적인 실행에서는 전혀 나타나지 않더라도, 악의적인 상대방이 별칭이 존재하는 입력을 조작하여 잠재적으로 컴퓨터 보안 익스플로잇으로 이어질 수 있는 문을 열어 둔다.[5]
이전 프로그램의 안전한 재정렬은 다음과 같다.[6]
```c
// 적절한 유형의 임시 지역 변수 'temp'를 선언합니다.
temp = *a + *b;
- sum = temp + *c;
```
마지막으로 추가된 함수 호출이 있는 간접적인 경우를 고려해 보면 다음과 같다.[7]
```c
- sum = f(*a) + g(*b);
```
컴파일러는 두 함수 호출 전에 `*a`와 `*b`를 평가하도록 선택할 수 있으며, 함수 호출 `f` 이후에 `*b`의 평가를 연기하거나 함수 호출 `g` 이후에 `*a`의 평가를 연기할 수 있다.[7] 함수 `f`와 `g`에 프로그램에서 보이는 부작용이 없는 경우 세 가지 선택 모두 동일한 보이는 프로그램 효과를 가진 프로그램을 생성한다.[7] `f` 또는 `g`의 구현에 별칭 지정 대상이 되는 포인터 `a` 또는 `b`에 대한 모든 포인터 쓰기의 부작용이 포함된 경우 세 가지 선택은 다른 보이는 프로그램 효과를 생성하기 쉽다.[7]
2. 2. 2. 언어 사양에서의 메모리 순서
C/C++와 같은 많은 고급 컴파일 언어들은 최고 성능을 추구하기 위해 컴파일러가 코드 재정렬에서 낙관적인 가정을 할 수 있는 위치와 의미론적 위험을 피하기 위해 비관적인 가정을 해야 하는 위치에 대한 복잡하고 정교한 의미론적 사양을 갖도록 발전했다.[1]현대 절차적 언어에서 부작용의 가장 큰 종류는 메모리 쓰기 작업과 관련이 있으므로 메모리 정렬에 대한 규칙은 프로그램 순서 의미론의 지배적인 구성 요소이다.[2] 함수 호출의 재정렬은 다른 고려 사항처럼 보일 수 있지만, 이는 일반적으로 함수 호출을 생성하는 표현식의 메모리 연산과 상호 작용하는 호출된 함수 내부의 메모리 효과에 대한 관심사로 귀결된다.[2]
컴파일러는 컴파일 시 명령 실행 순서를 자유롭게 변경할 수 있지만, 메모리 접근 순서가 중요한 경우 문제가 발생한다.[3] 대부분의 프로그래밍 언어는 정의된 순서대로 문을 실행하는 실행 스레드 개념을 가지고 있으며,[4] 기존 컴파일러는 고수준의 식을 저수준의 명령 열로 변환하고, 기계 수준의 프로그램 카운터와 연결했다.[4]
2. 3. 추가적인 어려움과 복잡성
대부분의 프로그래밍 언어는 정의된 순서로 문장을 실행하는 스레드 개념을 가지고 있다. 기존 컴파일러는 고급 표현식을 하위 수준 기계의 프로그램 카운터를 기준으로 하는 일련의 저수준 명령어로 변환한다.실행 효과는 두 가지 수준에서 나타난다. 첫 번째는 높은 수준의 프로그램 코드 내에서이고, 두 번째는 동시 프로그래밍에서 다른 스레드나 처리 요소가 보거나, 하드웨어 디버깅 보조 도구를 사용하여 머신 상태에 접근할 때이다. 이러한 지원의 일부는 종종 실행 코어와 별도로 기능적으로 독립적인 회로로 CPU 또는 마이크로컨트롤러에 직접 내장되어 있으며, 코어 자체가 실행 상태의 정적 검사를 위해 중단된 경우에도 계속 작동한다. 컴파일 시간 메모리 순서는 전자에 중점을 두며, 이러한 다른 관점에는 관여하지 않는다.
현대 컴파일러는 때때로 as-if 규칙을 통해 한 단계 더 나아간다. 자세한 내용은 하위 섹션을 참고하라.
메모리 순서 의미론에 대한 완전한 이해는 일반적으로 이 분야에 가장 정통한 전문 시스템 프로그래머 하위 집단 사이에서도 난해한 전문 지식으로 여겨진다. 대부분의 프로그래머는 자신의 프로그래밍 전문 지식 범위 내에서 이러한 문제에 대한 적절한 실무적 이해로 만족한다. 메모리 순서 의미론 전문화의 극단적인 끝에는 동시 컴퓨팅 모델을 지원하는 소프트웨어 프레임워크를 제작하는 프로그래머가 있다.
2. 3. 1. "as-if" 하에서의 최적화
현대 컴파일러는 때때로 as-if 규칙을 사용하여 최적화를 수행하는데, 이 규칙은 프로그램의 보이는 의미에 영향을 주지 않는다면 문장 간의 재정렬을 허용한다. 이 규칙에 따라 번역된 코드의 연산 순서는 원래 프로그램 순서와 크게 다를 수 있다.컴파일러가 서로 다른 포인터 표현식이 별칭 중첩이 없다고 가정할 수 있다면 (이는 일반적으로 정의되지 않은 동작을 보이는 잘못된 형식의 프로그램으로 분류될 수 있다), 코드 실행 전이나 직접적인 코드 검사 전에는 공격적인 코드 최적화 변환의 부정적인 결과를 예측하기 어렵다. 정의되지 않은 동작은 예측 불가능한 결과를 초래할 수 있다.
프로그래머는 언어 사양을 참조하여 컴파일러 최적화로 인해 의미가 변경될 수 있는 잘못된 형식의 프로그램을 작성하지 않도록 주의해야 한다. 포트란은 이러한 문제에 대한 높은 책임을 프로그래머에게 부여하며, 시스템 프로그래밍 언어인 C와 C++도 마찬가지이다.
2. 3. 2. 지역 변수의 별칭
지역 변수에 대한 포인터가 외부로 유출되면 별칭 문제가 발생할 수 있다.[1]```c
sum = f(&a) + g(a);
```
함수 `f`가 주어진 `a`에 대한 포인터로 무엇을 했는지 알 수 없다.[1] `g` 함수가 나중에 접근하는 전역 상태에 복사본을 남겼을 수도 있다.[1] 가장 간단한 경우, `f`는 변수 `a`에 새 값을 쓰며, 이 표현식의 실행 순서는 정의되지 않는다.[1] `f`는 포인터 인수에 const 한정자를 적용하여 이러한 작업을 명백히 방지할 수 있으며, 이로써 표현식이 잘 정의된다.[1] 따라서 C/C++의 현대 문화는 가능한 모든 경우에 함수 인수 선언에 const 한정자를 제공하는 것에 다소 집착하게 되었다.[1]
C와 C++는 `f`의 내부에서 const 속성을 타입 캐스팅하여 위험한 편법으로 제거하는 것을 허용한다.[1] 만약 `f`가 위 표현식을 깨뜨릴 수 있는 방식으로 이 작업을 수행한다면, 처음부터 포인터 인수 타입을 const로 선언해서는 안 된다.[1]
다른 고급 언어들은 이러한 선언 속성을 언어 자체 내에서 이 보장을 위반하는 허점이 없는 강력한 보장으로 간주하는 경향이 있다.[1] 애플리케이션이 다른 프로그래밍 언어로 작성된 라이브러리를 링크하는 경우, 이 언어 보장은 모두 무효가 된다 (하지만 이것은 극도로 나쁜 설계로 간주된다).[1]
2. 4. 컴파일 시점의 메모리 장벽 구현
대부분의 프로그래밍 언어는 정의된 순서로 문을 실행하는 실행 스레드의 개념을 가지고 있다. 기존 컴파일러는 고급 표현식을 하위 수준 머신의 프로그램 카운터를 기준으로 하는 일련의 저수준 명령어로 변환한다. 컴파일 시점 메모리 순서는 프로그램 코드 내의 높은 수준에 중점을 두며, 다른 관점에는 관여하지 않는다.[9]이러한 컴파일 시점의 메모리 장벽은 컴파일러가 명령어의 순서를 재배열하는 것을 막아주며, 런타임 시 CPU에 의한 재배열은 막지 않는다.
- GNU 인라인 어셈블러 구문은 GCC 컴파일러가 앞뒤의 읽기 및 쓰기 명령을 재배열하는 것을 금지한다:[9][27]
```
asm volatile("" ::: "memory");
__asm__ __volatile__ ("" ::: "memory");
```
```
atomic_signal_fence(memory_order_acq_rel);
```
- 인텔 C++ 컴파일러(ICC/ICL)는 "전체 컴파일러 펜스" 내장 함수를 사용한다:[11][12][28][29]
```
__memory_barrier()
```
- 마이크로소프트 비주얼 C++(MSVC) 컴파일러는 x86/x64에 대해서만 일부 내장 함수를 지원한다(이들 모두는 사용 중단됨):[13][30]
```
_ReadBarrier()
_WriteBarrier()
_ReadWriteBarrier()
2. 5. 결합된 배리어
다수의 프로그래밍 언어에서는 다양한 유형의 메모리 배리어를 다른 연산(예: 읽기, 쓰기, 원자적 증가, 원자적 비교 및 교환)과 결합할 수 있어, 전후에 추가적인 메모리 배리어(또는 둘 다)가 필요하지 않다. 대상 CPU 아키텍처에 따라, 이러한 언어 구조는 하드웨어 메모리 정렬 보장에 따라 특수 명령어, 여러 명령어(예: 배리어 및 읽기), 또는 일반 명령어로 변환된다.3. 실행 시점의 메모리 순서
멀티프로세서 시스템에서는 CPU 코어 간의 메모리 접근 순서가 프로그램의 정확성에 영향을 미칠 수 있다.
3. 1. 대칭형 멀티프로세싱 (SMP) 마이크로프로세서 시스템에서의 실행 시점의 메모리 순서
SMP 시스템에는 여러 가지 메모리 일관성 모델이 존재한다.- 순차 일관성: 모든 읽기 및 쓰기가 순서대로 실행된다.
- 완화된 일관성: 일부 재정렬이 허용된다.
- 로드 후 로드 재정렬 가능 (\[\[캐시 일관성]] 및 확장성 향상)
- 저장 후 로드 재정렬 가능
- 저장 후 저장 재정렬 가능
- 로드 후 저장 재정렬 가능
- 약한 일관성: 명시적 메모리 장벽으로만 제한되며, 읽기 및 쓰기가 임의로 재정렬될 수 있다.
일부 CPU에서는 다음과 같은 특징이 나타날 수 있다.
- 원자적 연산이 로드 및 저장과 재정렬될 수 있다.[14]
- 자체 수정 코드가 특수 명령어 캐시 플러시/다시 로드 명령어 없이는 실행되지 못하도록 하는, 일관성이 없는 명령어 캐시 파이프라인이 있을 수 있다.
- 종속 로드를 재정렬할 수 있다(이는 Alpha에 고유함). 프로세서가 먼저 일부 데이터에 대한 포인터를 가져온 다음 데이터를 가져오는 경우, 데이터를 직접 가져오지 않고 이미 캐시되었지만 아직 무효화되지 않은 오래된 데이터를 사용할 수 있다. 이러한 완화를 허용하면 캐시 하드웨어가 더 간단하고 빨라지지만, 리더 및 라이터에 대한 메모리 장벽이 필요하다.[15]
3. 1. 1. 여러 아키텍처의 메모리 순서 비교
; RISC-V 메모리 순서 모델:
:; WMO: 약한 메모리 순서(기본값)
:; TSO: 전체 저장 순서(Ztso 확장 기능으로만 지원)
; SPARC 메모리 순서 모드:
:; TSO: 전체 저장 순서(기본값)
:; RMO: 완화된 메모리 순서(최신 CPU에서 지원되지 않음)
:; PSO: 부분 저장 순서(최신 CPU에서 지원되지 않음)
3. 2. 하드웨어 메모리 장벽 구현
x86, x86-64 아키텍처에서는 `lfence`, `sfence`, `mfence` 명령어를 사용하여 메모리 접근 순서를 제어한다.[18][19] PowerPC는 `sync`, MIPS는 `sync`[20][21], Itanium은 `mf`, POWER는 `dcs`, ARMv7은 `dmb`, `dsb`, `isb` 명령어를 사용한다.[22]3. 2. 1. 하드웨어 메모리 장벽에 대한 컴파일러 지원
컴파일러 내장 함수를 통해 하드웨어 메모리 장벽을 사용할 수 있다. 예를 들어 GCC의 `__sync_synchronize`와 C11/C++11의 `atomic_thread_fence()` 등이 있다. 다음은 여러 아키텍처에서 지원하는 하드웨어 명령어들이다.
참조
[1]
웹사이트
Weak vs. Strong Memory Models
https://preshing.com[...]
2012-09-30
[2]
웹사이트
Linux Kernel Memory Barriers
https://www.kernel.o[...]
2024-08-03
[3]
웹사이트
Memory Ordering at Compile Time
https://preshing.com[...]
2012-06-25
[4]
웹사이트
Relaxed-Memory Concurrency
https://www.cl.cam.a[...]
University of Cambridge
2024-08-03
[5]
웹사이트
Memory Ordering in Modern Microprocessors
http://www.rdrop.com[...]
2007-09-19
[6]
웹사이트
Re: branch prediction, renaming, joins
https://yarchive.net[...]
2003-12-08
[7]
웹사이트
JSR 133 (Java Memory Model) FAQ
http://www.cs.umd.ed[...]
University of Maryland
2004-02
[8]
웹사이트
Intel 64 Architecture Memory Ordering White Paper
https://www.cs.cmu.e[...]
Intel
2007-08
[9]
문서
GCC compiler-gcc.h
http://lxr.linux.no/[...]
[10]
웹사이트
std::atomic_signal_fence
http://en.cppreferen[...]
[11]
문서
ECC compiler-intel.h
http://lxr.linux.no/[...]
[12]
문서
Intel(R) C++ Compiler Intrinsics Reference
http://software.inte[...]
[13]
웹사이트
_ReadWriteBarrier
https://learn.micros[...]
2021-08-03
[14]
서적
Shared Memory Application Programming: Concepts and Strategies in Multicore Application Programming
Elsevier Science
2015
[15]
문서
Reordering on an Alpha processor by Kourosh Gharachorloo
http://www.cs.umd.ed[...]
[16]
웹사이트
Memory Barriers: a Hardware View for Software Hackers
http://www.rdrop.com[...]
2010-06-07
[17]
문서
Table 1. Summary of Memory Ordering
http://www.linuxjour[...]
[18]
문서
SFENCE — Store Fence
http://www.softeng.r[...]
[19]
문서
MFENCE — Memory Fence
http://www.softeng.r[...]
[20]
웹사이트
MIPS® Coherence Protocol Specification, Revision 01.01
https://s3-eu-west-1[...]
2023-12-15
[21]
웹사이트
MIPS instruction set R5
https://training.mip[...]
2023-12-15
[22]
문서
Data Memory Barrier, Data Synchronization Barrier, and Instruction Synchronization Barrier.
http://infocenter.ar[...]
[23]
문서
Atomic Builtins
https://gcc.gnu.org/[...]
[24]
웹사이트
36793 – x86-64 does not get __sync_synchronize right
https://gcc.gnu.org/[...]
[25]
웹사이트
MemoryBarrier function (winnt.h) - Win32 apps
https://learn.micros[...]
2021-10-06
[26]
문서
Handling Memory Ordering in Multithreaded Applications with Oracle Solaris Studio 12 Update 2: Part 2, Memory Barriers and Memory Fence
http://www.oracle.co[...]
[27]
문서
GCC compiler-gcc.h
http://lxr.linux.no/[...]
[28]
문서
ECC compiler-intel.h
http://lxr.linux.no/[...]
[29]
문서
Intel(R) C++ Compiler Intrinsics Reference
http://software.inte[...]
[30]
문서
Visual C++ Language Reference
http://msdn.microsof[...]
[31]
문서
Reordering on an Alpha processor by Kourosh Gharachorloo
http://www.cs.umd.ed[...]
[32]
문서
Memory Ordering in Modern Microprocessors by Paul McKenney
http://www.rdrop.com[...]
[33]
문서
Memory Barriers: a Hardware View for Software Hackers
http://www.rdrop.com[...]
[34]
문서
Table 1. Summary of Memory Ordering
http://www.linuxjour[...]
본 사이트는 AI가 위키백과와 뉴스 기사,정부 간행물,학술 논문등을 바탕으로 정보를 가공하여 제공하는 백과사전형 서비스입니다.
모든 문서는 AI에 의해 자동 생성되며, CC BY-SA 4.0 라이선스에 따라 이용할 수 있습니다.
하지만, 위키백과나 뉴스 기사 자체에 오류, 부정확한 정보, 또는 가짜 뉴스가 포함될 수 있으며, AI는 이러한 내용을 완벽하게 걸러내지 못할 수 있습니다.
따라서 제공되는 정보에 일부 오류나 편향이 있을 수 있으므로, 중요한 정보는 반드시 다른 출처를 통해 교차 검증하시기 바랍니다.
문의하기 : help@durumis.com