?um/p1-90`메모리 일관성 모델, 또는 메모리 모델은 SharedArrayBuffer를 백업으로 하는
메모리 모델은 SharedArrayBuffer에 대한 추상 연산이나 Atomics 객체의 메서드에 의해 평가 중 도입되는 Memory 이벤트들에 대한 관계적 제약으로 정의됩니다.
이 절에서는 SharedArrayBuffer에 대한 추상 연산으로 도입된 Memory 이벤트에 대한 공리적(axiomatic) 모델을 제공합니다. 이 모델은 이 명세서의 나머지 부분과 달리 알고리즘으로 표현될 수 없다는 점을 강조해야 합니다. 추상 연산에 의한 이벤트의 비결정적 도입은 ECMAScript 평가의 연산 의미론과 메모리 모델의 공리 의미론 사이의 인터페이스입니다. 이 이벤트들의 의미론은 평가 내의 모든 이벤트 그래프를 고려함으로써 정의됩니다. 이것은 정적 의미론도 아니고
공유 메모리 접근(읽기/쓰기)은 아래에서 정의되는 두 그룹, atomic 접근과 data 접근으로 나뉜다. Atomic 접근은 순차적 일관성을 가지며, 즉 에이전트 클러스터의 모든 에이전트가 동의하는 엄격한 전체 순서가 있다. 비-atomic 접근은 모든 에이전트가 동의하는 엄격한 전체 순서가 없으므로 정렬되지(unordered) 않는다.
순차적 일관성과 unordered 보다 약하거나 강한(예: release-acquire) 순서는 지원되지 않는다.
Shared Data Block 이벤트란 ReadSharedMemory, WriteSharedMemory, ReadModifyWriteSharedMemory 레코드 중 하나를 의미한다. 읽기 이벤트는 ReadSharedMemory 또는 ReadModifyWriteSharedMemory 중 하나이고, 쓰기 이벤트는 WriteSharedMemory 또는 ReadModifyWriteSharedMemory 중 하나이다.
| 필드 이름 | 값 | 의미 |
|---|---|---|
| [[Order]] | 이 이벤트에 대해 |
|
| [[NoTear]] | Boolean 값 | 이 이벤트가 동일한 |
| [[Block]] | 이벤트가 동작하는 블록입니다. | |
| [[ByteIndex]] | 0 이상의 정수 | [[Block]] 안에서 읽기가 발생한 바이트 주소입니다. |
| [[ElementSize]] | 0 이상의 정수 | 읽기 연산의 크기입니다. |
| 필드 이름 | 값 | 의미 |
|---|---|---|
| [[Order]] | 이 이벤트에 대해 |
|
| [[NoTear]] | Boolean 값 | 이 이벤트가 동일한 |
| [[Block]] | 이벤트가 동작하는 블록입니다. | |
| [[ByteIndex]] | 0 이상의 정수 | [[Block]] 내에서 쓰기가 발생한 바이트 주소입니다. |
| [[ElementSize]] | 0 이상의 정수 | 쓰기 연산의 크기입니다. |
| [[Payload]] | 바이트 값들의 리스트 | 다른 이벤트에 의해 읽혀질 바이트 값들의 리스트입니다. |
| Field Name | Value | Meaning |
|---|---|---|
| [[Order]] | Read-modify-write 이벤트는 항상 순차적 일관성. | |
| [[NoTear]] | Read-modify-write 이벤트는 tear 될 수 없다. | |
| [[Block]] | 이벤트가 동작하는 블록. | |
| [[ByteIndex]] | 음이 아닌 정수 | [[Block]] 내 read-modify-write 의 바이트 주소. |
| [[ElementSize]] | 음이 아닌 정수 | read-modify-write 의 크기. |
| [[Payload]] | 바이트 값 |
[[ModifyOp]] 에 전달될 바이트 값 |
| [[ModifyOp]] | 읽은 바이트 |
Shared Data Block 이벤트는 추상 연산이나 Atomics 객체의 메서드를 통해 후보 실행 에이전트 이벤트 레코드에 도입됩니다. 일부 연산은 동기화(Synchronize) 이벤트도 도입하는데, 이 이벤트는 어떠한 필드도 가지지 않으며 오직 다른 이벤트의 허용된 순서를 직접적으로 제한하기 위해 존재합니다. 마지막으로
Shared Data Block 이벤트 e의 메모리 범위는 e.[[ByteIndex]](포함)부터 e.[[ByteIndex]] + e.[[ElementSize]](미포함)까지의 모든 정수의 집합이다. 두 이벤트의 메모리 범위는 [[Block]], [[ByteIndex]], [[ElementSize]]가 모두 동일할 때 같다. 두 이벤트의 메모리 범위는 [[Block]]이 같고, 범위가 같지 않으면서 교집합이 비어있지 않을 때 겹친다고 한다. 두 이벤트의 메모리 범위는 [[Block]]이 다르거나, 범위가 같지도 겹치지도 않을 때 서로 분리되어 있다고 한다.
계정해야 할 postMessage), 에이전트 시작/종료, 공유 메모리 외 채널을 통한 에이전트 클러스터 내 통신. 특정 실행 execution 에 대해 이러한 이벤트는
이벤트는 아래에 정의된 관계에 의해 후보 실행 내에서 순서화된다.
Agent Events Record 는 다음 필드를 가진
| 필드 이름 | 값 | 의미 |
|---|---|---|
| [[AgentSignifier]] | 에이전트 식별자 | 이 순서가 만들어지게 된 평가를 수행한 에이전트입니다. |
| [[EventList]] | 이벤트는 평가 도중 리스트에 추가됩니다. | |
| [[AgentSynchronizesWith]] | 동기화 이벤트 쌍들의 리스트 | 연산 의미론에 의해 도입된 동기화 관계입니다. |
Chosen Value Record 는 다음 필드를 가진
| Field Name | Value | Meaning |
|---|---|---|
| [[Event]] | 이 선택 값에 대해 도입된 |
|
| [[ChosenValue]] | 바이트 값 |
평가 동안 비결정적으로 선택된 바이트. |
candidate execution 은 에이전트 클러스터 평가의 다음 필드를 가진
| 필드 이름 | 값 | 의미 |
|---|---|---|
| [[EventsRecords]] | 에이전트 이벤트 레코드들의 리스트 | 에이전트와 평가 도중 추가된 |
| [[ChosenValues]] | 선택된 값 레코드들의 리스트 |
empty candidate execution 은 필드가 빈
The abstract operation EventSet takes argument execution (후보 실행) and returns
The abstract operation SharedDataBlockEventSet takes argument execution (후보 실행) and returns
The abstract operation HostEventSet takes argument execution (후보 실행) and returns
The abstract operation ComposeWriteEventBytes takes arguments execution (후보 실행), byteIndex (0 이상의 정수), and Ws (
read-modify-write 변형 [[ModifyOp]]는
이 추상 연산은 여러
The abstract operation ValueOfReadEvent takes arguments execution (a
다음
후보 실행 execution에 대하여, is-agent-order-before 관계는 아래 조건을 만족하는
각 에이전트는 평가 과정에서 에이전트별로 엄격한 전체 순서로 이벤트를 도입합니다. 이는 그러한 엄격한 전체 순서들의 합집합이 됩니다.
후보 실행 execution에 대하여, reads-bytes-from 함수는
후보 실행에는 항상 reads-bytes-from 함수가 존재합니다.
후보 실행 execution에 대하여, reads-from 관계는 아래 조건을 만족하는
후보 실행 execution에 대해, host-synchronizes-with 관계는
후보 실행 execution 내 두
이 관계는 postMessage)을 제공할 수 있게 해줍니다.
후보 실행 execution에 대해, synchronizes-with 관계는 다음을 만족하는
후보 실행 execution에서
후보 실행 execution에서
후보 실행 execution의
후보 실행 execution에 대해, happens-before 관계는 다음을 만족하는
이벤트 E, D에 대해, 아래 조건 중 하나라도 만족하면 execution에서 E happens-before D가 된다.
happens-before는
후보 실행 execution이 아래 알고리즘이
후보 실행 execution이 아래 알고리즘이
후보 실행 execution이 아래 알고리즘이
이 요구사항은, 정렬된 방식(aligned)으로 정수형
후보 실행 execution에 대해, is-memory-order-before는
execution에서 R이 W로부터
이 조항은 동일
후보 실행이 is-memory-order-before 관계를 허용하면, 그 실행은 순차적 일관성 원자적 연산을 가진다.
is-memory-order-before는
다음이 모두 참이면 후보 실행 execution은 유효한 실행(또는 간단히 실행)이다.
모든 프로그램은 적어도 하나의 유효한 실행을 가진다.
실행 execution과
실행 execution과
실행 execution에 대해,
모든 실행이 데이터 경합 없어야 프로그램이 데이터 경합 없음으로 간주된다.
아래는 ECMAScript 프로그래머가 shared memory를 사용할 때 참고할 가이드라인이다.
프로그램이 data race free(데이터 경합 없음) 상태를 유지하도록, 즉 동일 메모리 위치에 대해 원자적이지 않은 연산이 동시에 발생할 수 없도록 작성할 것을 권한다. 데이터 경합이 없는 프로그램은 각 에이전트의 평가 의미 단계가 서로 interleave(교차)되는 의미론(interleaving semantics)을 가진다. 데이터 경합 없는 프로그램에서는
보다 일반적으로, 프로그램이 data race free가 아니어도, 원자적 연산이 어떤 데이터 경합에도 참여하지 않고 경합 중인 모든 연산의 접근 크기가 같다면 예측 가능한 행동을 가질 수 있다. 원자적 연산이 경합에 참여하지 않도록 하려면, 원자적 연산과 비원자적 연산이 서로 다른 메모리 셀을 사용하도록 하며, 서로 다른 크기의 원자적 접근이 동시에 같은 메모리 셀에 접근하지 않음이 보장되게 해야 한다. 실제로 프로그래머는 공유 메모리를 최대한 강한 타입(strongly typed)처럼 다루는 것이 좋다. 여전히 경합 중인 비원자적 접근의 순서와 타이밍에는 의존할 수 없으나, 강하게 타입된 것처럼 다루면 중첩 읽기나 쓰기에 값이 "찢기는" 현상은 발생하지 않는다.
아래는 공유 메모리를 사용하는 프로그램에서 컴파일러 변환을 구현하는 ECMAScript 구현체 대상 가이드라인이다.
단일 에이전트 환경에서 유효한 대부분의 프로그램 변환을 다중 에이전트 환경에서도 허용하는 것이 바람직하다. 이는 다중 에이전트 프로그램에서 각 에이전트의 성능이 단일 에이전트 환경에서만큼 좋게 유지될 수 있도록 보장하기 위함이다. 이러한 변환은 종종 판단하기 어렵다. 우리는 프로그램 변환에 대한 몇 가지 규칙을 개략적으로 제시한다. 이 규칙들은 규범적(nomative)으로 받아들여져야 하며(즉,
에이전트 순서 단편(agent-order slice)은
공유 메모리가 없는 상황에서 허용되는 에이전트 순서 단편의 모든 변환은 공유 메모리가 있는 상황에서도 아래 예외를 제외하고 유효하다.
Atomics은 석판에 새겨진 것처럼 불변: 프로그램 변환은 [[Order]]가
(실제로 재배치 금지는 컴파일러가 모든
읽기는 안정적이어야 한다: 동일한 shared memory read는 한 실행에서 하나의 값만을 관측해야 한다.
(예를 들어, 의미상 단일 읽기인 부분이 프로그램 내에서 여러 번 실행되고, 그 각각에서 다른 값을 읽더라도, 프로그램은 결국 그 중 하나의 값만 관측할 수 있다. 이 규칙을 위반하는 변환을 rematerialization이라 한다.)
쓰기도 안정적이어야 한다: shared memory에 대한 모든 관측 가능한 쓰기는 실행의 프로그램 의미에 따라야 한다.
(예를 들어, 변환이 특정 관측 가능한 쓰기를 도입해서는 안 되는데, 더 큰 위치에 대해 read-modify-write 연산을 수행해 더 작은 데이터를 쓸 때, 프로그램이 쓸 수 없는 값을 메모리에 쓰거나, 방금 읽은 값을 그 위치에 다시 쓸 때 해당 위치가 그 사이에 다른 에이전트에 의해 덮어씌워질 수 있었다면 안 된다.)
가능한 읽기 값은 반드시 비어 있지 않아야 한다: 프로그램 변환은 shared memory read의 가능한 읽기 값이 비워지게 해서는 안 된다.
(직관에 반하게도, 이 규칙은 쓰기 변화에 대한 제약을 의미한다. 쓰기는 read 이벤트에 의해 읽힐 수 있기에 의미를 가진다. 예를 들어, 쓰기를 이동하거나 합치는 것은 가능하고, 때로는 두
여전히 유효한 변환의 예: 동일 위치에 대한 여러 비원자적 읽기의 병합, 비원자적 읽기들의 재배치, speculative(추측적) 비원자적 읽기 도입, 동일 위치에 대한 여러 비원자적 쓰기의 병합, 서로 다른 위치로의 비원자적 쓰기 재배열, 반복문 외부로의 비원자적 읽기 hoisting(끌어올리기, 결과적으로 종료 조건에 영향줌). 단, alias 되는
아래는 공유 메모리 접근에 대한 기계어 코드 생성 시 ECMAScript 구현체 대상 가이드라인이다.
ARM이나 Power 이상의 LOCK 프리픽스 명령, ARM에서는 load-exclusive/store-exclusive, Power에서는 load-link/store-conditional)
구체적으로,
가장 단순한 naive 코드 생성 패턴은 다음과 같다:
이는 atomic 연산이 비원자적 쓰기, 혹은 다른 크기의 atomic 연산과 경합하지 않을 때 올바르다.