위키피디아에 Memory Barrier에 대한 설명이 있더군요. 영어와 일본어로 있어서 일본어쪽을 번역했습니다. 글 중 아래 부분을 잘 모르시는 분들이 계시는데 꼭 기억하시기를 바랍니다.

"

1. volatile는 컴파일러 최적화에 관한 것이라서 다중 프로세서 시스템에서 정렬 문제는 부족

2.  뮤텍스세마포어 등의 프리미티브는 여러 스레드가 자원에 액세스 하기 위한 동기화 기능을 제공

"

 

 

출처 : http://ja.wikipedia.org/wiki/%E3%83%A1%E3%83%A2%E3%83%AA%E3%83%90%E3%83%AA%E3%82%A2

 

메모리 바리어(Memory Barrier) 또는 메모리 펜스(Memory Fence)는 그 전후의 메모리 작업의 순서를 제한하는 CPU 명령(컴퓨터)의 일종이다.

CPU에는 성능 최적화 방법으로서 아웃 오브 오더 실행을 수행할 수도 있어 메모리 로드 명령 및 저장 명령을 포함하여 순서에 상관없이 실행한다.이 명령 정렬은 하나의 스레드 중에서 일반적으로 자동으로 실행되는 다중 스레드 프로그램이나 장치 드라이버에서는 신중하게 통제하지 않는 한 예측 불능의 동작을내는 원인이된다.  순서성 제한 방법은 하드웨어 의존이며 그 구조에 의해 정의된다. 아키텍쳐에서는 어떤 바리어를 제공하여 각각 다른 순서성 제한을 구현하는 경우가 있다.

메모리 바리어는 대부분 낮은 수준의 기계어에서 사용되며 여러 장치가 공유하는 메모리를 사용하는 데 쓰인다. 그런 코드로 동기화  프리미티브, 다중 프로세서 시스템에서 락 프리한 데이터 구조, 어떤 하드웨어를 제어하는 장치 드라이버 등이있다.

 

 

 

간단한 예제

프로그램이 1 개의 CPU에서 동작하는 경우 하드웨어는 전체 메모리 작업이 프로그램된 순서대로 이뤄진 것 같이 보이도록 명령을 수행한다. 그래서 메모리 바리어는 필요 없다. 그러나 메모리를 여러 기기에서 공유되는 경우 (다중 프로세서 시스템의 CPU 제품군과 메모리맵 I/O 등), 아웃 오브 오더 실행에 의해 프로그램의 결과를 변화 시키는데있다. 예를 들면 두 번째 CPU에서 보면 1 번 CPU가 행한 메모리 작업은 프로그램 상의 순서와 다르게 보일지도 모른다.

이하의 2 프로세서 프로그램은 아웃 오브 오더 실행에 의해서 프로그램이 영향을 받은 사례이다.

"
우선 메모리 위치 x와 f 값은 모두 0 이다고 한다.

프로세서 # 1에서 실행되는 프로그램은 f 값이 0 이 아닐 때까지 루프 하여 그 후 x 값을 표시한다. 프로세서 # 2에서 작동하는 프로그램은 x에 42를 저장하고 f에 1을 저장한다. 의사 코드의 일부를 아래에 표시.프로그램의 각 행이 개개의 프로세서 명령에 대응하고있다. 
---------------------------------------------------------------
프로세서 # 1 :
loop :
위치 f의 값을 로드하고 0 이라면 loop에 분기
위치 x의 값을 표시
 
프로세서 # 2 : 
위치 x에 42를 저장
위치 f에 1을 저장
-----------------------------------------------------------------
표시 되는 것은 보통 42라고 예상되지만 프로세서 # 2 저장 명령이 아웃 오브 오더로 실행되면
f가 x 앞에 갱신되는 가능성이 있어 "0" 이 나타날 가능성도 나온다.

많은 프로그램에서는 이런 상황은 받아들일 수 없다.
메모리 바리어를 프로세서 #2의 위치 f에 저장 명령 앞에 삽입하면 다른 프로세서에서 봐도

x가 f 앞에 갱신 되도록 관측되는 것을 보장할 수있다.


저수준 아키텍처에 대한 기본

메모리 바리어는 아키텍처의 메모리 모델의 정의의 일부 낮은 레벨 프리미티브이다. 명령어 집합뿐만 아니라 메모리 모델은 아키텍처에 따라 다양하기 때문에 그 기능을 일반화로 언급하는 것이 적절하지 않다. 일반적으로 메모리 바리어를 제대로 사용하려면 프로그램 대상 하드웨어 아키텍처 문서를 읽어야한다. 라는 것을 아래에는 몇 개인가 실제하는 메모리 바리어를 소개한다.

어떤 아키텍처에서는 "풀펜스(전체 울타리)"라고 하는 종류의 메모리 바리어 명령을 제공한다. 풀펜스 명령은 그 이전의 모 든로드 / 스토어 명령이 펜스 후의 로드 / 저장소 명령 앞에 완료하는 것을 보장한다. 다른 아키텍처에서는 "aquire"명령과 "release"명령에 메모리 바리어를 구성한다. 이것은 읽기 후 쓰기 작업 가시성에 관한 것으로 읽기(aquire)와 쓰기 (release)가 각각의 관점에서 명령을 사용한다. 어떤 아키텍처에서는 주기억과 I/O 메모리 동작의 조합에 따라 여러 종류의 메모리 바리어 명령을 제공한다. 메모리 바리어 명령이 여러 종류 존재하는 아키텍처에서는 각각의 명령에 걸리는 비용에 큰 차이가 있는 점이 중요하다.



"스레드"프로그램과 메모리 시정

스레드화된 프로그램은 일반적으로 Java 등의 고급 언어가 제공하는 동기화 기본 및 POSIX 스레드Win32 등의 API가 제공하는 동기화 프리미티브를 사용한다. 뮤텍스세마포어 등의 프리미티브는 여러 스레드가 자원에 액세스 하기 위한 동기화 기능을 제공한다. 이 프리미티브는 필요한 메모리 가시성(visibility)을 제공하기 위하여 메모리 바리어 기능을 구현하는 경우가 많다. 그런 환경에서는 메모리 바리어를 프로그래머가 명시적으로 사용할 필요가 없다.

각 API 및 프로그래밍 환경은 원칙적으로 자신의 높은 레벨 메모리 모델를 가지고 있서 메모리 가시성을 정의하고있다. 그런 환경에서는 메모리 바리어를 필요로하는 것은 아니지만 메모리 가시성이 어떻게 되어있는지를 가능한 이해하는 것이 중요하다. 그러나 그런 이야기가 반드시 문서화되고 명확화 되어 있는 것은 아니다.

프로그래밍 언어 의미론이 기계어 명령 코드와는 다른 수준으로 추상화된 정의된대로 프로그래밍 환경의 메모리 모델은 하드웨어 메모리 모델과는 추상화 수준이 다르다. 이들을 구분해서 이해하고 낮은 수준의 메모리 바리어 명령과 특정 프로그래밍 환경의 메모리 가시성의 의미가 다르다는 것을 이해하는 것이 중요하다. 예를 들어 플랫폼 pthreads 구현에서는 요구되는 것보다 강한 메모리 바리어를 사용하고 있는지도 모른다. 구현된 메모리 가시성을 전제로 프로그램을 만들 때 설계상 메모리 가시성을 전제로 한 프로그램보다 이식성이 낮아질 가능성이있다.



아웃 오브 오더 수행 컴파일러에 의한 명령 순서 최적화

메모리 바리어 명령은 하드웨어 레벨의 명령 정렬에 대한 것이다. 컴파일러도 최적화 작업의 일환으로 명령의 정렬을 할 수있다. 모든 경우에도 병렬 처리에서 유사한 문제를 일으키는 원인이 되기 때문에 다중 스레드에서 공유하는 데이터에 대한 코드 최적화를 억제하는 수단을 제공할 필요가있다. 게다가 그런 수단이 필요한 것은 동기 프리미티브가 보호되지 않은 데이터뿐이다.

C 언어 의 키워드 volatile 사용하면 해당 변수에 대한 메모리 작업을 정렬하거나 생략 등의 최적화를 억제 할 수있다. 이것은 싱글 프로세스 시스템에서 다중 스레드 및 시그널 핸들러 등의 인터럽트에 대한 일종의 배리어를 제공하는 것이다. 그러나 volatile는 컴파일러 최적화에 관한 것이라서 다중 프로세서 시스템에서 정렬 문제는 부족하다.

어떤 언어는 컴파일러 최적화에도 아웃 오브 오더 실행에 대처하는 기능이 있는 것도 있지만 그 컴파일러가 생성한 코드가 정말 정렬을 하고 있지 않는지 확인 등, 충분히 주의해서 사용해야한다. 개발자에 따라서 컴파일러에 의한 정렬 문제를 방지할 필요가있는 경우에는 어셈블리 언어를 사용하는 사람도있다.

Java 1.5 (5 버전이라고도 함)은 새로운 메모리 모델을 채용하고 있으며 키워드 volatile로 어떤 종류의 하드웨어 명령 정렬 및 컴파일러에 의한 정렬을 방지하는 것을 보장하고있다. C++에도 확장이 제안 되고 있다.

이 글은 스프링노트에서 작성되었습니다.

신고
by 흥배 2009.03.28 14:21
| 1 |

티스토리 툴바