Bỏ qua danh sách so với cây tìm kiếm nhị phân


Câu trả lời:


257

Danh sách bỏ qua có thể dễ dàng truy cập / sửa đổi đồng thời hơn. Herb Sutter đã viết một bài viết về cấu trúc dữ liệu trong môi trường đồng thời. Nó có nhiều thông tin sâu sắc hơn.

Việc thực hiện thường xuyên nhất của cây tìm kiếm nhị phân là cây đỏ-đen . Các vấn đề xảy ra đồng thời khi cây được sửa đổi, nó thường cần phải cân bằng lại. Hoạt động tái cân bằng có thể ảnh hưởng đến các phần lớn của cây, điều này sẽ yêu cầu khóa mutex trên nhiều nút cây. Chèn một nút vào danh sách bỏ qua được bản địa hóa hơn nhiều, chỉ các nút được liên kết trực tiếp với nút bị ảnh hưởng cần phải được khóa.


Cập nhật từ bình luận của Jon Harrops

Tôi đọc chương trình đồng thời mới nhất của Fraser và Harris mà không cần khóa . Thứ thực sự tốt nếu bạn quan tâm đến cấu trúc dữ liệu không khóa. Bài viết tập trung vào Bộ nhớ giao dịch và MCAS hoạt động đa từ so sánh và trao đổi lý thuyết. Cả hai đều được mô phỏng trong phần mềm vì chưa có phần cứng nào hỗ trợ chúng. Tôi khá ấn tượng rằng họ có thể xây dựng MCAS trong phần mềm.

Tôi đã không tìm thấy các công cụ bộ nhớ giao dịch đặc biệt hấp dẫn vì nó yêu cầu một trình thu gom rác. Ngoài ra bộ nhớ giao dịch phần mềm bị ảnh hưởng bởi các vấn đề hiệu suất. Tuy nhiên, tôi rất vui mừng nếu bộ nhớ giao dịch phần cứng trở nên phổ biến. Cuối cùng, nó vẫn nghiên cứu và sẽ không được sử dụng cho mã sản xuất trong một thập kỷ nữa.

Trong phần 8.2 họ so sánh hiệu suất của một số triển khai cây đồng thời. Tôi sẽ tóm tắt những phát hiện của họ. Thật đáng để tải xuống bản pdf vì nó có một số biểu đồ rất nhiều thông tin ở các trang 50, 53 và 54.

  • Khóa danh sách bỏ qua là cực kỳ nhanh chóng. Họ quy mô cực kỳ tốt với số lượng truy cập đồng thời. Đây là những gì làm cho danh sách bỏ qua đặc biệt, các cấu trúc dữ liệu dựa trên khóa khác có xu hướng bị uốn cong dưới áp lực.
  • Danh sách bỏ qua không khóa luôn nhanh hơn khóa danh sách bỏ qua nhưng chỉ hầu như không có.
  • danh sách bỏ qua giao dịch luôn chậm hơn 2-3 lần so với các phiên bản khóa và không khóa.
  • khóa cây đỏ-đen uốn cong theo truy cập đồng thời. Hiệu suất của chúng suy giảm tuyến tính với mỗi người dùng đồng thời mới. Trong số hai triển khai cây đỏ đen được biết đến, về cơ bản, có một khóa toàn cầu trong quá trình tái cân bằng cây. Cái khác sử dụng leo thang khóa lạ mắt (và phức tạp) nhưng vẫn không thực hiện đáng kể phiên bản khóa toàn cầu.
  • cây đỏ đen không khóa không tồn tại (không còn đúng nữa, xem Cập nhật).
  • cây đỏ đen giao dịch có thể so sánh với danh sách bỏ qua giao dịch. Điều đó rất đáng ngạc nhiên và rất hứa hẹn. Bộ nhớ giao dịch, mặc dù chậm hơn nếu dễ viết hơn. Nó có thể dễ dàng như tìm kiếm nhanh và thay thế trên phiên bản không đồng thời.

Cập nhật
Dưới đây là bài viết về cây không khóa : Cây đỏ đen không khóa sử dụng CAS .
Tôi chưa nhìn sâu vào nó, nhưng bề ngoài thì có vẻ chắc chắn.


3
Chưa kể rằng trong một skiplist không suy biến, khoảng 50% các nút chỉ nên có một liên kết duy nhất giúp chèn và xóa hiệu quả rõ rệt.
Adisak

2
Cân bằng lại không yêu cầu khóa mutex. Xem cl.cam.ac.uk/research/srg/netos/lock-free
Jon Harrop

3
@Jon, có và không. Không có triển khai cây đỏ-đen không khóa được biết đến. Fraser và Harris chỉ ra cách thực hiện một cây đỏ đen dựa trên bộ nhớ giao dịch và hiệu suất của nó. Bộ nhớ giao dịch vẫn còn rất nhiều trong lĩnh vực nghiên cứu, vì vậy trong mã sản xuất, một cây đỏ đen vẫn sẽ cần phải khóa các phần lớn của cây.
deft_code

4
@deft_code: Intel gần đây đã công bố triển khai Bộ nhớ giao dịch thông qua TSX trên Haswell. Điều này có thể chứng minh sự thú vị khi khóa những cấu trúc dữ liệu miễn phí mà bạn đã đề cập.
Mike Bailey

2
Tôi nghĩ rằng câu trả lời của Fizz cập nhật hơn (từ năm 2015) hơn là câu trả lời này (2012) và do đó có lẽ nên là câu trả lời ưa thích nhất bây giờ.
fnl

81

Đầu tiên, bạn không thể so sánh một cấu trúc dữ liệu ngẫu nhiên với cấu trúc dữ liệu mang lại cho bạn sự đảm bảo trong trường hợp xấu nhất.

Danh sách bỏ qua tương đương với cây tìm kiếm nhị phân cân bằng ngẫu nhiên (RBST) theo cách được giải thích chi tiết hơn trong "Khám phá tính đối ngẫu giữa danh sách bỏ qua và cây tìm kiếm nhị phân" của Dean và Jones .

Theo cách khác, bạn cũng có thể có các danh sách bỏ qua xác định đảm bảo hiệu suất trường hợp xấu nhất, xem Munro và cộng sự.

Trái với những gì một số yêu cầu ở trên, bạn có thể có các triển khai cây tìm kiếm nhị phân (BST) hoạt động tốt trong lập trình đồng thời. Một vấn đề tiềm ẩn với các BST tập trung vào đồng thời là bạn không thể dễ dàng nhận được sự bảo đảm tương tự về việc cân bằng như bạn làm từ cây đỏ-đen (RB). (Nhưng "tiêu chuẩn", tức là ngẫu nhiên, danh sách bỏ qua cũng không cung cấp cho bạn những đảm bảo này.) Có sự đánh đổi giữa việc duy trì cân bằng mọi lúc và truy cập đồng thời tốt (và dễ lập trình), vì vậy cây RB thoải mái thường được sử dụng khi đồng thời tốt là mong muốn. Thư giãn bao gồm không cân bằng lại cây ngay lập tức. Đối với một cuộc khảo sát có niên đại (1998), hãy xem Hanke '' Hiệu suất của các thuật toán cây đen-đen đồng thời '' [ps.gz] .

Một trong những cải tiến gần đây về điều này là cái cây được gọi là màu sắc (về cơ bản bạn có một số trọng lượng sao cho màu đen sẽ là 1 và màu đỏ sẽ bằng 0, nhưng bạn cũng cho phép các giá trị ở giữa). Và làm thế nào để một cây màu sắc chống lại danh sách bỏ qua? Chúng ta hãy xem những gì Brown et al. "Một kỹ thuật chung cho cây không chặn" (2014) phải nói:

với 128 luồng, thuật toán của chúng tôi vượt trội hơn so với skiplist của Java từ 13% đến 156%, cây AVL dựa trên khóa của Bronson et al. từ 63% đến 224% và RBT sử dụng bộ nhớ giao dịch phần mềm (STM) từ 13 đến 134 lần

EDIT để thêm: Danh sách bỏ qua dựa trên khóa của Pugh, đã được điểm chuẩn trong Fraser và Harris (2007) "Lập trình đồng thời không khóa" khi gần với phiên bản không khóa của riêng họ (một điểm được nhấn mạnh trong câu trả lời hàng đầu ở đây), cũng được điều chỉnh để hoạt động đồng thời tốt, xem "Bảo trì đồng thời danh sách bỏ qua" của Pugh , mặc dù theo một cách khá nhẹ nhàng. Tuy nhiên, một bài báo mới hơn / 2009 "Thuật toán danh sách bỏ qua lạc quan đơn giản"bởi Herlihy và cộng sự, người đề xuất việc thực hiện các danh sách bỏ qua đồng thời đơn giản hơn (so với Pugh), đã chỉ trích Pugh vì không cung cấp bằng chứng về tính đúng đắn đủ thuyết phục cho họ. Bỏ qua điều này (có thể là quá khoa trương), Herlihy et al. cho thấy việc triển khai danh sách bỏ qua dựa trên khóa đơn giản hơn của họ thực sự không mở rộng được cũng như triển khai không khóa của JDK, nhưng chỉ để tranh chấp cao (chèn 50%, xóa 50% và tra cứu 0%) ... mà Fraser và Harris đã không kiểm tra gì cả; Fraser và Harris chỉ kiểm tra 75% tra cứu, chèn 12,5% và xóa 12,5% (trong danh sách bỏ qua với ~ 500K phần tử). Việc thực hiện đơn giản hơn của Herlihy et al. cũng đến gần với giải pháp không khóa từ JDK trong trường hợp tranh chấp thấp mà họ đã thử nghiệm (70% tra cứu, chèn 20%, xóa 10%); họ thực sự đã đánh bại giải pháp không khóa cho kịch bản này khi họ đưa ra danh sách bỏ qua của mình đủ lớn, tức là đi từ 200K đến 2 triệu phần tử, do đó xác suất xảy ra tranh chấp trên bất kỳ khóa nào là không đáng kể. Sẽ thật tuyệt nếu Herlihy et al. đã vượt qua sự treo cổ của họ về bằng chứng của Pugh và cũng đã thử nghiệm việc thực hiện của anh ta, nhưng than ôi họ đã không làm điều đó.

EDIT2: Tôi đã tìm thấy một bản gốc (xuất bản năm 2015) của tất cả các điểm chuẩn: "Nhiều hơn bạn muốn biết về đồng bộ hóa" của Synoli .

nhập mô tả hình ảnh ở đây

"Algo.4" là tiền thân (phiên bản cũ hơn, 2011) của Brown và cộng sự đã đề cập ở trên. (Tôi không biết phiên bản 2014 tốt hơn hay tệ hơn thế nào). "Algo.26" là Herlihy đã đề cập ở trên; như bạn có thể thấy nó bị hỏng trong các bản cập nhật và tệ hơn nhiều đối với CPU Intel được sử dụng ở đây so với CPU Sun từ giấy gốc. "Algo.28" là ConcurrencySkipListMap từ JDK; nó không làm tốt như người ta có thể hy vọng so với các triển khai danh sách bỏ qua dựa trên CAS khác. Những người chiến thắng trong cuộc tranh cãi cao là "Algo.2" một thuật toán dựa trên khóa (!!) được mô tả bởi Crain et al. trong "Cây tìm kiếm nhị phân thân thiện với sự tham gia" và "Algo.30" là "skiplist xoay" từ "Cấu trúc dữ liệu logarit cho đa lõi" . ". Xin lưu ý rằng Gramoli là đồng tác giả của cả ba bài viết về thuật toán chiến thắng này. "Algo.27" là bản triển khai C ++ của danh sách bỏ qua của Fraser.

Kết luận của Gramoli là việc thực hiện một cây đồng thời dựa trên CAS dễ dàng hơn nhiều so với việc đưa ra một danh sách bỏ qua tương tự. Và dựa trên các số liệu, thật khó để không đồng ý. Giải thích của ông cho thực tế này là:

Khó khăn trong việc thiết kế một cây không khóa bắt nguồn từ khó khăn trong việc sửa đổi nhiều tài liệu tham khảo nguyên tử. Danh sách bỏ qua bao gồm các tháp được liên kết với nhau thông qua các con trỏ kế tiếp và trong đó mỗi nút trỏ đến nút ngay bên dưới nó. Chúng thường được coi là tương tự như cây vì mỗi nút có một người kế vị trong tháp kế vị và bên dưới nó, tuy nhiên, một điểm khác biệt lớn là con trỏ hướng xuống thường không thay đổi do đó đơn giản hóa việc sửa đổi nguyên tử của nút. Sự khác biệt này có lẽ là lý do tại sao bỏ qua danh sách vượt trội so với cây dưới sự tranh chấp nặng nề như được quan sát trong Hình [ở trên].

Ghi đè lên khó khăn này là mối quan tâm chính trong công việc gần đây của Brown và cộng sự. Họ có một bài viết hoàn chỉnh (2013) hoàn toàn riêng biệt "Nguyên tắc thực dụng cho các cấu trúc dữ liệu không chặn" về việc xây dựng "nguyên thủy" hợp chất LL / SC, mà họ gọi là LLX / SCX, do chính họ thực hiện bằng cách sử dụng CAS (cấp độ máy). Brown và cộng sự. đã sử dụng khối xây dựng LLX / SCX này trong triển khai cây đồng thời năm 2014 (nhưng không phải trong năm 2011).

Tôi nghĩ có lẽ cũng đáng để tóm tắt ở đây những ý tưởng cơ bản của danh sách bỏ qua "không có điểm nóng" / thân thiện với tranh chấp (CF). Nó bổ sung một ý tưởng thiết yếu từ các cây RB thoải mái (và các cấu trúc dữ liệu xào xáo tương tự): các tòa tháp không còn được xây dựng ngay lập tức khi chèn, nhưng bị trì hoãn cho đến khi có ít sự tranh chấp. Ngược lại, việc xóa một tòa tháp cao có thể tạo ra nhiều sự tranh chấp; điều này đã được quan sát từ tận năm 1990 của Pugh, đó là lý do tại sao Pugh đưa ra sự đảo ngược con trỏ khi xóa (một mẩu tin mà trang Wikipedia về danh sách bỏ qua vẫn không đề cập đến ngày nay, than ôi). Danh sách bỏ qua CF đưa bước này đi xa hơn và trì hoãn việc xóa các tầng trên của một tòa tháp cao. Cả hai loại hoạt động bị trì hoãn trong danh sách bỏ qua CF đều được thực hiện bởi một luồng giống như trình thu gom rác riêng biệt (dựa trên CAS), mà các tác giả của nó gọi là "luồng thích ứng".

Mã Synchrobench (bao gồm tất cả các thuật toán được kiểm tra) có sẵn tại: https://github.com/gramoli/synchrobench . Brown mới nhất et al. triển khai (không bao gồm ở trên) có sẵn tại http://www.cs.toronto.edu/~tabrown/chromatic/ConcienChromaticTreeMap.java Có ai có sẵn máy 32 lõi không? J / K Quan điểm của tôi là bạn có thể tự chạy những thứ này.


12

Ngoài ra, ngoài các câu trả lời được đưa ra (dễ thực hiện kết hợp với hiệu suất tương đương với cây cân bằng). Tôi thấy rằng việc thực hiện truyền tải theo thứ tự (tiến và lùi) đơn giản hơn nhiều vì một danh sách bỏ qua thực sự có một danh sách được liên kết bên trong việc thực hiện.


1
không theo thứ tự theo thứ tự cho một cây bin đơn giản như: "def func (nút): func (left (nút)); op (nút); func (phải (nút))"?
Claudiu

6
Chắc chắn, điều đó đúng nếu bạn muốn duyệt tất cả trong một lệnh gọi hàm. nhưng sẽ khó chịu hơn nhiều nếu bạn muốn di chuyển theo kiểu lặp như trong std :: map.
Evan Teran

@Evan: Không phải bằng ngôn ngữ chức năng mà bạn chỉ có thể viết bằng CPS.
Jon Harrop

@Evan def iterate(node): for child in iterate(left(node)): yield child; yield node; for child in iterate(right(node)): yield child;:? =). kiểm soát không cục bộ iz awesom .. @Jon: viết bằng CPS là một nỗi đau, nhưng có lẽ bạn có ý nghĩa với việc tiếp tục? máy phát điện về cơ bản là một trường hợp đặc biệt của việc tiếp tục cho trăn.
Claudiu

1
@Evan: có, nó hoạt động miễn là tham số nút bị cắt khỏi cây trong quá trình sửa đổi. Truyền tải C ++ có cùng một ràng buộc.
deft_code

10

Trong thực tế, tôi thấy rằng hiệu suất của B-tree trong các dự án của tôi đã hoạt động tốt hơn so với danh sách bỏ qua. Bỏ qua danh sách dường như dễ hiểu nhưng thực hiện một B-cây là không khó khăn.

Một lợi thế mà tôi biết là một số người thông minh đã tìm ra cách thực hiện danh sách bỏ qua đồng thời không khóa mà chỉ sử dụng các hoạt động nguyên tử. Ví dụ, Java 6 chứa lớp ConcảnSkipListMap và bạn có thể đọc mã nguồn cho nó nếu bạn bị điên.

Nhưng cũng không quá khó để viết một biến thể cây B đồng thời - tôi đã thấy nó được thực hiện bởi người khác - nếu bạn tách ra và hợp nhất các nút "chỉ trong trường hợp" khi bạn đi xuống cây thì bạn sẽ không phải lo lắng về sự bế tắc và chỉ cần giữ một khóa trên hai cấp độ của cây tại một thời điểm. Chi phí đồng bộ hóa sẽ cao hơn một chút nhưng cây B có thể nhanh hơn.


4
Tôi nghĩ bạn không nên gọi Cây nhị phân là Cây B, có một DS hoàn toàn khác với tên đó
Shihab Shahriar Khan

8

Từ bài viết Wikipedia bạn trích dẫn:

Các hoạt động Θ (n), buộc chúng ta phải truy cập vào mọi nút theo thứ tự tăng dần (chẳng hạn như in toàn bộ danh sách) cung cấp cơ hội để thực hiện việc khử nhiễu hậu trường của cấu trúc cấp độ của danh sách bỏ qua một cách tối ưu, đưa danh sách bỏ qua đến thời gian tìm kiếm O (log n). [...] Một danh sách bỏ qua, mà gần đây chúng tôi chưa thực hiện [bất kỳ hoạt động nào] Θ (n), không cung cấp đảm bảo hiệu suất trong trường hợp xấu nhất tuyệt đối như các cấu trúc dữ liệu cây cân bằng truyền thống , bởi vì luôn có thể (mặc dù với xác suất rất thấp) rằng các đồng xu được sử dụng để xây dựng danh sách bỏ qua sẽ tạo ra một cấu trúc cân bằng kém

EDIT: vì vậy đó là một sự đánh đổi: Skip Lists sử dụng ít bộ nhớ hơn với nguy cơ chúng có thể bị thoái hóa thành một cây không cân bằng.


đây sẽ là một lý do chống lại việc sử dụng danh sách bỏ qua.
Claudiu

7
trích dẫn MSDN, "Cơ hội [cho 100 yếu tố cấp 1] chính xác là 1 trong 1.267.650.600.228.222.401.496,703,205,376".
peterchen

8
Tại sao bạn lại nói rằng họ sử dụng ít bộ nhớ hơn?
Jonathan

1
@peterchen: Tôi hiểu rồi, cảm ơn. Vì vậy, điều này không xảy ra với danh sách bỏ qua xác định? @Mitch: "Bỏ qua Danh sách sử dụng ít bộ nhớ hơn". Làm thế nào để danh sách bỏ qua sử dụng ít bộ nhớ hơn cây nhị phân cân bằng? Có vẻ như họ đã có 4 con trỏ trong mỗi nút và các nút trùng lặp trong khi cây chỉ có 2 con trỏ và không có trùng lặp.
Jon Harrop

1
@Jon Harrop: Các nút ở cấp một chỉ cần một con trỏ trên mỗi nút. Bất kỳ nút nào ở cấp cao hơn chỉ cần hai con trỏ cho mỗi nút (Một đến nút tiếp theo và một đến cấp dưới nó), mặc dù tất nhiên nút cấp 3 có nghĩa là bạn đang sử dụng tổng cộng 5 con trỏ cho một giá trị đó. Tất nhiên, điều này vẫn sẽ chiếm rất nhiều bộ nhớ (so với tìm kiếm nhị phân nếu bạn muốn một danh sách bỏ qua không vô dụng và có một tập dữ liệu lớn) ... nhưng tôi nghĩ rằng tôi đang thiếu một cái gì đó ...
Brian

2

Bỏ qua danh sách được thực hiện bằng cách sử dụng danh sách.

Các giải pháp khóa miễn phí tồn tại cho các danh sách liên kết đơn và đôi - nhưng không có giải pháp khóa miễn phí nào chỉ sử dụng CAS cho bất kỳ cấu trúc dữ liệu O (logn) nào.

Tuy nhiên, bạn có thể sử dụng danh sách dựa trên CAS để tạo danh sách bỏ qua.

(Lưu ý rằng MCAS, được tạo bằng CAS, cho phép các cấu trúc dữ liệu tùy ý và bằng chứng về khái niệm cây đỏ đen đã được tạo bằng MCAS).

Vì vậy, kỳ lạ như chúng là, chúng hóa ra rất hữu ích :-)


5
"Không có giải pháp khóa miễn phí nào chỉ sử dụng trực tiếp CAS cho bất kỳ cấu trúc dữ liệu O (logn) nào". Không đúng. Để biết ví dụ về bộ đếm, hãy xem cl.cam.ac.uk/research/srg/netos/lock-free
Jon Harrop

-1

Bỏ qua Danh sách có lợi thế của tước khóa. Nhưng, thời gian chạy phụ thuộc vào mức độ của một nút mới được quyết định. Thông thường điều này được thực hiện bằng cách sử dụng Random (). Trên một từ điển 56000 từ, danh sách bỏ qua mất nhiều thời gian hơn cây splay và cây mất nhiều thời gian hơn bảng băm. Hai cái đầu tiên không thể phù hợp với thời gian chạy của bảng băm. Ngoài ra, mảng của bảng băm cũng có thể bị khóa theo cách đồng thời.

Danh sách bỏ qua và danh sách theo thứ tự tương tự được sử dụng khi cần địa phương tham chiếu. Ví dụ: tìm các chuyến bay tiếp theo và trước một ngày trong một ứng dụng.

Một cây splay tìm kiếm nhị phân inmemory là tuyệt vời và thường xuyên được sử dụng.

Bỏ qua danh sách Vs Splay Tree Vs Hash Table Thời gian chạy trên từ điển tìm op


Tôi đã xem nhanh và kết quả của bạn dường như hiển thị SkipList nhanh hơn SplayTree.
Chinasaur

Thật sai lầm khi coi ngẫu nhiên là một phần của danh sách bỏ qua. Làm thế nào các yếu tố được bỏ qua là rất quan trọng. Ngẫu nhiên được thêm vào cho các cấu trúc xác suất.
dùng568109
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.