Một hoạt động so sánh và trao đổi nhiều từ thực tế


10

Trong giấy với tiêu đề giống như của câu hỏi này, các tác giả mô tả làm thế nào để xây dựng một nonblocking linearizable nhiều từ CAS hoạt động chỉ sử dụng một CAS đơn từ. Trước tiên, họ giới thiệu hoạt động trao đổi đơn so sánh kép - RDCSS, như sau:

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);
}

trong đó RDCSSDescriptor_tcấu trúc với các trường sau:

  • a1 - địa chỉ của điều kiện đầu tiên
  • o1 - giá trị dự kiến ​​tại địa chỉ đầu tiên
  • a2 - địa chỉ của điều kiện thứ hai
  • o2 - giá trị dự kiến ​​tại địa chỉ thứ hai
  • n2 - giá trị mới được ghi tại địa chỉ thứ hai

Bộ mô tả này được tạo và khởi tạo một lần trong một luồng bắt đầu hoạt động RDCSS - không có luồng nào khác có tham chiếu đến nó cho đến khi CAS1 đầu tiên trong hàm RDCSSthành công, làm cho bộ mô tả có thể truy cập được (hoặc hoạt động theo thuật ngữ của bài báo).

Ý tưởng đằng sau thuật toán là như sau - thay thế vị trí bộ nhớ thứ hai bằng một mô tả cho biết bạn muốn làm gì. Sau đó, cho rằng bộ mô tả có mặt, kiểm tra vị trí bộ nhớ đầu tiên để xem giá trị của nó có thay đổi không. Nếu không, thay thế bộ mô tả ở vị trí bộ nhớ thứ hai bằng giá trị mới. Nếu không, hãy đặt vị trí bộ nhớ thứ hai trở về giá trị cũ.

Các tác giả không giải thích tại sao dòng với !!nhận xét là cần thiết trong bài báo. Dường như với tôi rằng các CAS1hướng dẫn trong Completechức năng sẽ luôn thất bại sau lần kiểm tra này, miễn là không có sửa đổi đồng thời. Và nếu có một sửa đổi đồng thời giữa kiểm tra và CAS trong Complete, thì luồng thực hiện kiểm tra vẫn không thành công với CAS của nó Complete, vì sửa đổi đồng thời không nên sử dụng cùng một mô tả d.

Câu hỏi của tôi là: Có thể bỏ qua việc kiểm tra chức năng RDCSSS, if (r == d->o2)...với RDCSS vẫn duy trì ngữ nghĩa của một so sánh kép, lệnh hoán đổi đơn có thể tuyến tính hóa và không khóa ? (dòng có !!bình luận)

Nếu không, bạn có thể mô tả kịch bản mà dòng này thực sự cần thiết để đảm bảo tính chính xác?

Cảm ơn bạn.


Trước tiên, để hiểu những gì đang diễn ra, chúng ta cần xem cấu trúc dữ liệu RDCSSDescriptor_t. Thứ hai, điều này có lẽ lạc đề ở đây vì nó không liên quan đến khoa học máy tính lý thuyết; sẽ tốt hơn nếu hỏi điều này trên stackoverflow.com.
Dave Clarke

Các liên kết đến giấy bị hỏng.
Aaron Sterling

1
Tôi xin lỗi vì liên kết - nó sẽ hoạt động. Tôi đã cập nhật câu hỏi để mô tả mô tả là gì. Lý do tôi chưa đăng bài này trên stackoverflow.com là vì Câu hỏi thường gặp nói rằng trang này dành cho các câu hỏi cấp độ nghiên cứu trong khoa học máy tính. Tôi nghĩ rằng các câu hỏi về tự do khóa và tính tuyến tính của một thuật toán đủ điều kiện như vậy. Tôi hy vọng tôi hiểu câu hỏi thường gặp không chính xác.
axel22

Từ khóa bạn bỏ lỡ trong FAQ là "lý thuyết". Khi một số người thấy câu hỏi thú vị, tôi sẽ để nó mở.
Dave Clarke

3
@Dave: Tôi không phải là một chuyên gia trong lĩnh vực phụ này, nhưng với tôi điều này nghe có vẻ như một câu hỏi TCS rất điển hình. Bạn được cung cấp hai mô hình tính toán (A: với CAS một từ, B: với CAS nhiều từ) và thước đo độ phức tạp (số CAS) và bạn được hỏi liệu bạn có thể mô phỏng mô hình B trong mô hình A không, và với những gì tồi tệ nhất trong trường hợp trên không. (Ở đây có thể có một chút sai lầm khi mô phỏng được đưa ra dưới dạng một đoạn mã C thay vì mã giả; điều này có thể gợi ý cho một người lý thuyết rằng điều này có liên quan đến các thách thức lập trình trong thế giới thực.)
Jukka Suomela

Câu trả lời:


9

Trong một môi trường thời gian chạy đồng thời, những điều đơn giản có vẻ kỳ lạ ... hy vọng điều này có thể giúp ích.

Chúng tôi có một CAS1 BUILT-IN ATOMIC có ngữ nghĩa này:

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

Chúng ta cần xác định hàm ATOMIC RDCSS bằng CAS1 và có ngữ nghĩa sau:

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

Theo trực giác: chúng ta cần thay đổi giá trị tại addr2 chỉ khi * addr1 == oldval1 ... nếu một luồng khác đang thay đổi, chúng ta có thể giúp luồng khác hoàn thành thao tác, sau đó chúng ta có thể thử lại.

Hàm RDCSS sẽ được sử dụng (xem bài viết) để xác định CASN. Bây giờ, chúng tôi xác định Bộ mô tả RDCSS theo cách sau:

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

Sau đó, chúng tôi thực hiện RDCSS theo cách sau:

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);  
}
  • BƯỚC 1: đầu tiên chúng tôi cố gắng thay đổi giá trị của * addr2 thành mô tả (riêng) của chúng tôi d, nếu CAS1 thành công thì res == d-> oldval2 (nghĩa là res KHÔNG phải là mô tả)
  • BƯỚC 2: kiểm tra xem res có phải là mô tả hay không, ví dụ như STEP1 không thành công (một luồng khác đã thay đổi addr2) ... giúp một luồng khác hoàn thành thao tác
  • BƯỚC 3: thử lại BƯỚC1 nếu chúng tôi không thành công trong việc lưu trữ mô tả của chúng tôi d
  • BƯỚC 4: nếu chúng tôi đã lấy giá trị mong đợi của mình từ addr2 thì chúng tôi đã thành công trong việc lưu trữ mô tả (con trỏ) của chúng tôi trong addr2 và chúng tôi có thể hoàn thành nhiệm vụ lưu trữ newval2 thành * addr2 iif * addr1 == oldval1

TRẢ LỜI CÂU HỎI CỦA BẠN

Nếu chúng ta bỏ qua STEP4 thì if (... && * addr1 == oldval1) * addr2 = newval2 của ngữ nghĩa RDCSS sẽ không bao giờ được thực thi (... hoặc tốt hơn: nó có thể được thực thi theo cách có thể đoán được bởi các luồng khác cái hiện tại).

Như bạn đã chỉ ra trong nhận xét của mình, điều kiện nếu (res == d1-> oldval2) tại STEP4 là không cần thiết: ngay cả khi chúng tôi bỏ qua nó, cả CAS1 trong Complete () sẽ thất bại vì * (d-> addr2)! = D . Purpouse duy nhất của nó là tránh một cuộc gọi chức năng.

Ví dụ 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)

Cảm ơn bạn, giải thích tốt. Tôi hoàn toàn bỏ lỡ điểm CAS1 trả về giá trị cũ, không phải giá trị mới.
axel22

Nhưng, trong kịch bản, 2 dòng cuối cùng nói rằng: không có điều kiện tại STEP4, T1 có thể lưu trữ giá trị, vì addr2chứa d2->newval2. Nhưng, đối với tôi, CAS1 trong ý Completechí sẽ thất bại, bởi vì nó hy vọng giá trị cũ sẽ là mô tả d1- không có gì sẽ được viết bởi T1. Đúng?
axel22

@ axel22: Tôi đã bỏ lỡ CAS1 trong Complete () :-D. Có bạn đúng ... ví dụ của tôi là sai, điều kiện if chỉ được sử dụng để tránh lệnh gọi hàm, nếu chúng ta vứt bỏ if () không có gì thay đổi. Rõ ràng là Toàn bộ (d) tại STEP4 là cần thiết. Bây giờ tôi sửa đổi ví dụ.
Marzio De Biasi

Tránh CAS mà chúng tôi hy vọng sẽ thất bại là một kỹ thuật tối ưu hóa bộ đệm theo như tôi biết, vì trên phần cứng thực, nó thường có các hiệu ứng tiêu cực như xóa các dòng bộ đệm và có được quyền truy cập độc quyền vào dòng bộ đệm. Tôi đoán tác giả của bài báo muốn thuật toán phải thực tế nhất có thể ngoài việc chính xác.
Tim Seguine
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.