실용적인 다중 단어 비교 및 ​​스왑 작업


10

에서 종이 이 질문과 동일한 제목, 저자는 구축하는 방법에 대해 설명 블로킹 선형화 여러 단어 CAS의 단 하나의 단어 CAS를 사용하여 작업을. 먼저 다음과 같이 이중 비교 단일 스왑 작업 인 RDCSS를 소개합니다.

word_t RDCSS(RDCSSDescriptor_t *d) {
  do {
    r = CAS1(d->a2, d->o2, d);
    if (IsDescriptor(r)) Complete(r);
  } while (IsDescriptor(r));
  if (r == d->o2) Complete(d); // !!
  return r;
}

void Complete(RDCSSDescriptor_t *d) {
  v = *(d->a1);
  if (v == d->o1) CAS1(d->a2, d, d->n2);
  else CAS1(d->a2, d, d->o2);
}

여기서는 RDCSSDescriptor_t다음 필드가있는 구조입니다.

  • a1 -첫 번째 조건의 주소
  • o1 -첫 번째 주소에서 예상되는 값
  • a2 -두 번째 조건의 주소
  • o2 -두 번째 주소에서 예상되는 값
  • n2 -두 번째 주소에 쓰여질 새로운 값

이 디스크립터는 RDCSS 조작을 시작하는 스레드에서 한 번 작성되고 초기화됩니다. 함수의 첫 번째 CAS1이 RDCSS성공할 때까지 다른 스레드가 해당 스레드에 대한 참조를 갖지 않아 디스크립터에 도달 할 수있게합니다 (또는 논문의 용어에서 활성 상태 ).

알고리즘의 기본 개념은 다음과 같습니다. 두 번째 메모리 위치를 수행하려는 작업을 나타내는 설명 자로 바꿉니다. 그런 다음 설명자가 존재하는 경우 첫 번째 메모리 위치를 확인하여 값이 변경되었는지 확인하십시오. 그렇지 않은 경우 두 번째 메모리 위치의 설명자를 새 값으로 바꾸십시오. 그렇지 않으면 두 번째 메모리 위치를 이전 값으로 다시 설정하십시오.

저자는 !!논문 내에서 주석이 있는 줄이 왜 필요한지 설명하지 않습니다 . 동시 수정이 없으면이 검사 후 함수 의 CAS1명령어 Complete가 항상 실패하는 것 같습니다. 그리고 검사와 CAS 사이에 동시 수정이있는 경우 동시 수정은 동일한 디스크립터를 사용하지 않아야하므로 Complete검사를 수행하는 스레드가 해당 CAS에서 실패 Complete합니다 d.

내 질문이있다 : 함수의 검사 수 RDCSSS, if (r == d->o2)...RDCSS는 여전히 이중 비교, 하나의 스왑 명령의 의미를 유지하면서, 생략 선형화잠금 무료 ? ( !!의견이있는 줄 )

그렇지 않은 경우 정확성을 보장하기 위해이 줄이 실제로 필요한 시나리오를 설명 할 수 있습니까?

감사합니다.


먼저, 진행 상황을 이해하려면 데이터 구조 RDCSSDescriptor_t를 확인해야합니다. 둘째, 이것은 이론적 인 컴퓨터 과학을 다루지 않기 때문에 아마도 여기서 다루지 않을 것입니다. stackoverflow.com에서 이것을 요청하는 것이 좋습니다.
Dave Clarke

용지에 대한 링크가 끊어졌습니다.
Aaron Sterling

1
링크에 대해 사과드립니다. 이제 작동합니다. 설명자가 무엇인지 설명하기 위해 질문을 업데이트했습니다. 내가 stackoverflow.com에 이것을 게시하지 않은 이유는 FAQ 가이 사이트가 컴퓨터 과학에 대한 연구 수준의 질문에 대한 것이라고 말합니다. 잠금 자유 및 알고리즘의 선형성에 대한 질문이 그에 해당한다고 생각했습니다. FAQ를 잘못 이해했으면합니다.
axel22

FAQ에서 놓친 키워드는 "이론적"이었습니다. 어떤 사람들은 그 질문이 흥미로워지면 그것을 열어 두겠습니다.
Dave Clarke

3
@Dave :이 하위 영역의 전문가는 아니지만 매우 일반적인 TCS 질문처럼 들립니다. 두 가지 계산 모델 (A : 단일 단어 CAS 사용, B : 다중 단어 CAS 사용)과 복잡성 측정 (CAS 수)이 주어지고 모델 A에서 모델 B를 시뮬레이션 할 수 있는지 묻습니다. 최악의 경우 오버 헤드가 발생합니다. (여기서는 시뮬레이션이 의사 코드 대신 C 코드의 일부로 제공된다는 오해의 소지가있을 수 있습니다. 이는
이론가

답변:


9

동시 런타임 환경에서 간단한 일이 이상하게 보일 수 있습니다 ... 이것이 도움이되기를 바랍니다.

우리는 이 의미를 갖는 내장형 원자 CAS1을 가지고 있습니다 :

int CAS1(int *addr, int oldval, int newval) {
  int currval = *addr;
  if (currval == oldval) *addr = newval;
  return currval;
}

우리는 정의 할 필요가 ATOMIC RDCSS 기능을 사용하여 CAS1을 다음과 같은 의미를 갖는

int RDCSS(int *addr1, int oldval1, int *addr2, int oldval2, int newval2) {
  int res = *addr;
  if (res == oldval2 && *addr1 == oldval1) *addr2 = newval2;
  return res;
}

직관적으로 : * addr1 == oldval1 ... addr1 == oldval1 ... 인 경우에만 addr2의 값을 동시에 변경해야합니다. 다른 스레드가 변경하는 경우 다른 스레드가 작업을 완료하는 데 도움을 줄 수 있으며 다시 시도 할 수 있습니다.

CASN을 정의하기 위해 RDCSS 기능이 사용됩니다 (기사 참조). 이제 다음과 같은 방법으로 RDCSS 설명자를 정의합니다 .

RDCSSDESCRI
int *addr1   
int oldval1
int *addr2   
int oldval2
int newval2

그런 다음 다음과 같은 방식으로 RDCSS를 구현합니다.

int RDCSS( RDCSSDESCRI *d ) {
  do {
    res = CAS1(d->addr2, d->oldval2, d);  // STEP1
    if (IsDescriptor(res)) Complete(res); // STEP2
  } while (IsDescriptor(res);             // STEP3
  if (res == d->oldval2) Complete(d);     // STEP4
  return res;
}

void Complete( RDCSSDESCRI *d ) {
  int val = *(d->addr1);
  if (val == d->oldval1) CAS1(d->addr2, d, d->newval2);
    else CAS1(d->addr2, d, d->oldval2);  
}
  • STEP1 : 먼저 * addr2 값을 (자신의) 디스크립터 d로 변경하려고합니다. CAS1이 성공하면 res == d-> oldval2입니다 (즉, res는 디스크립터가 아닙니다)
  • STEP2 : res가 설명자인지 확인하십시오. 즉 STEP1 실패 (다른 스레드 변경 addr2) ... 다른 스레드가 작업을 완료하도록 도와줍니다.
  • STEP3 : 디스크립터를 성공적으로 저장하지 못한 경우 STEP1을 재 시도하십시오 d
  • STEP4 : addr2에서 예상 값을 가져온 경우 addr2에 설명자 (포인터)를 저장하는 데 성공했으며 newval2를 * addr2에 저장하는 작업을 완료 할 수 있습니다. iif * addr1 == oldval1

질문에 대한 답변

STEP4를 생략 하면 RDCSS 시맨틱 의 if (... && * addr1 == oldval1) * addr2 = newval2 부분은 절대 실행되지 않습니다 (... 또는 더 나은 : 다른 스레드가 도움이되는 umpredictable 방식으로 실행될 수 있음) 현재 하나).

의견에서 지적했듯이 STEP4의 if (res == d1-> oldval2) 조건 은 불필요합니다. 생략하더라도 * (d-> addr2) 때문에 Complete ()의 CAS1이 모두 실패합니다! . 그것의 유일한 목적은 함수 호출을 피하는 것입니다.

예 T1 = thread1, T2 = thread2 :

remember that addr1 / addr2 are in a shared data zone !!!

T1 enter RDCSS function
T2 enter RDCSS function
T2 complete STEP1 (and store the pointer to its descriptor d2 in addr2)
T1 at STEP1 the CAS1 fails and res = d2
T2 or T1 completes *(d2->addr2)=d2->newval2 (suppose that *(d2->addr1)==d2->oldval1)
T1 execute STEP1 and now CAS1 can fail because *addr2 == d2->newval2
   and maybe d2->newval2 != d1->oldval2, in every case at the end 
   res == d2->newval2 (fail) or
   res == d1->oldval2 (success)
T1 at STEP2 skips the call to Complete() (because now res is not a descriptor)
T1 at STEP3 exits the loop (because now res is not a descriptor)
T1 at STEP4 T1 is ready to store d1->newval2 to addr2, but only if
   *(d1->addr2)==d (we are working on our descriptor) and *(d1->addr1)==d1->oldval1
   ( Custom() function)

좋은 설명 감사합니다. CAS1이 새로운 값이 아닌 이전 값을 반환한다는 점을 완전히 놓쳤습니다.
axel22 2016 년

그러나 시나리오에서 마지막 두 줄은 다음과 같이 말합니다. STEP4의 조건이 없으면 T1이 addr2포함 하기 때문에 값을 저장할 수 있습니다 d2->newval2. 그러나 Complete이전 값이 디스크립터가 될 것으로 기대하기 때문에 의지 의 CAS1 이 실패 하는 것 같습니다 .T1은 d1아무것도 쓰지 않습니다. 권리?
axel22

@ axel22 : Complete () :-D에서 CAS1을 놓쳤습니다. 네 맞습니다 ... 내 예제가 잘못되었습니다 .if 조건이 함수 호출을 피하기 위해 사용되는 경우에만 if () 아무것도 변경하지 않습니다. 분명히 STEP4 의 Complete (d) 가 필요합니다. 이제 예제를 수정했습니다.
Marzio De Biasi 2016 년

실제 하드웨어에서는 일반적으로 캐시 라인을 비우고 캐시 라인에 독점적으로 액세스하는 것과 같은 부정적인 영향을 미치기 때문에 CAS가 실패 할 것으로 예상하는 것은 캐시 최적화 기술입니다. 논문의 저자는 알고리즘이 정확하고 가능한 한 실용적이기를 원했습니다.
Tim Seguine
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.