BP-Wrapper:無鎖競爭的快取替換演算法系統框架

charlieroro發表於2021-06-13

BP-Wrapper:無鎖競爭的替換演算法系統框架

最近看了一個golang的高效能快取ristretto,該快取可以很好地實現如下功能:

  1. Concurrent
  2. High cache-hit ratio
  3. Memory-bounded (limit to configurable max memory usage)
  4. Scale well as the number of cores and goroutines increases
  5. Scale well under non-random key access distribution (e.g. Zipf).

在官方的introducing-ristretto-high-perf-go-cache一文中提到了一個名為BP-Wrapper的(幾乎)無鎖競爭的框架,用於提升快取的擴充套件性。本文就是該框架的論文。

譯自:bp_wrapper

概要

在高階資料庫系統中,多處理器環境中的併發級別隨著並行事務的增加以及多核處理器的引入而不斷攀升。這給快取管理帶來了新的挑戰,如何在保證擴充套件性的同時滿足高併發資料處理的需求。如果快取管理中的頁替換演算法不支援擴充套件,則可能會嚴重降低系統的效能。大多數替換演算法會使用鎖來保護資料結構,但併發訪問也會引起大量鎖競爭。通常的做法是通過修改替換演算法來降低競爭,如使用時鐘演算法來近似實現LRU替換。不幸的是,這種修改通常會破壞原始演算法的快取命中率。在低併發環境下可能不會存在(或可以容忍)這個問題,因此可能會導致該問題長時間不被重視。本論文不會在替換演算法的高命中率和低鎖競爭之間進行權衡。在這裡,我們提出了一個系統框架,稱為BP-Wrapper,該框架幾乎可以幫助所有替換演算法消除鎖競爭,而無需對演算法作任何改動。在BP-Wrapper中,我們使用批處理和預載入技術降低了鎖競爭,並獲得高命中率。8.2版本的PostgreSQL僅使用300行C程式碼就實現了BP-Wrapper。與使用鎖競爭的替換演算法相比,當執行TPC-C型別和TPC-W型別的負載時,它可以將吞吐量增提升兩倍。

I.簡介

現代資料庫管理系統通常會管理TB級別的資料,並在多處理器系統上並行處理成百上千條事務。這類資料庫系統的快取管理要求能夠高效降低磁碟I/O操作所需要的時間,並能夠隨併發事務數量以及底層處理器數量的增加進行擴充套件。快取管理的核心是資料替換演算法,用來確定哪些資料頁應該被快取到記憶體中,以高效響應上層事務處理執行緒對磁碟資料的請求。替換演算法通常會維護複雜的資料結構來跟蹤執行緒對資料的訪問歷史,這樣就可以依賴資料結構中的原始資訊來執行替換演算法。在高階生產系統中,會有大量執行緒頻繁地並行訪問資料頁,這時會要求替換演算法同時兼具高效性和可擴充套件性。

磁碟I/O操作變得越來越昂貴。為了最小化磁碟訪問,出現了多種替換演算法以及針對資料庫、虛擬記憶體和I/O 快取的實現,主要通過組織和管理深度頁訪問歷史來提升命中率。如LIRS [1]、2Q [2]、和 ARC [3]。這些演算法通常會在每次I/O訪問時採取動作(命中或未命中快取,以及對記錄資料訪問歷史的資料結構的一系列更新)。這些操作非常頻繁,因此通常被設計為簡單而高效的,避免造成訪問開銷。

為了保證資料結構的完整性,替換演算法會以一種序列化的方式採取操作,來響應頁訪問。因此為了在多工系統上實現這些演算法,通常會用到同步鎖,換句話說,在採取動作前必須使用排他鎖進行保護。當一個事務處理執行緒持有鎖時,其他請求該鎖的執行緒必須使用busy-waiting(自旋鎖)或上下文切換的方式達到互斥效果。

在大型系統中,鎖競爭會導致嚴重的效能問題,針對該問題的研究已經持續了很多年(即[4], [5], [6])。在我們的實驗中,在16個處理器系統上,由於替換演算法造成的鎖競爭使得吞吐量下降了2倍。兩種因素導致了效能降級:一個是頻繁地請求鎖,目前通過在所有事務請求執行緒中共享鎖來調節頁訪問,執行緒每次訪問頁時都會請求鎖;另一個因素是獲取鎖的代價,包括改變鎖狀態,busy-waiting,和/或上下文切換。與花費在鎖保護下的操作相比,獲取鎖的代價可能更高。隨著支援DBMS系統的多處理器上多核處理器的增加,上述兩個因素造成的效能降級也愈發嚴重。

由於替換演算法設計者沒有關注鎖競爭造成的問題,因此在實際系統中,演算法的好處(包括高命中率)可能會大打折扣,例如,一些普遍使用的DBMS系統(如postgreSQL)沒有采用高階的替換演算法,相反訴諸於基於時鐘的近似LRU替換演算法。其他DBMS系統(如Oracle Universal Server [7]和ADABAS [8])則使用分散式鎖來解決鎖競爭問題。

相比於對應的原始演算法(分別對應LRU,LIRS或ARC),基於時鐘的近似演算法,如CLOCK [9],CLOCK-PRO [10] 和CAR [11]等通常不會獲得高命中率。它們將快取頁組織為環形列表,並使用引用位或引用計數來記錄每個快取頁的訪問資訊。當命中快取中的一個頁時,基於時鐘的近似演算法會設定引用位或增加計數,而不會修改環形列表本身。由於這些操作並不需要時鐘,因此快取效能是可擴充套件的。然而,基於時鐘的演算法只能記錄有限的歷史訪問資訊,如是否訪問某個頁或對該頁的訪問次數,但無法知道訪問順序。歷史資訊的缺失可能會影響命中率。很多成熟的替換演算法並不會採用基於時鐘的資料結構,原因是無法使用時鐘結構來近似表達它們需要的資訊。如SEQ[12]演算法和DB2[13] 使用的快取替換策略,它們需要知道快取頁的訪問順序,並以此來檢測順序以及訪問順序/隨機訪問模式。

通常,可以使用分散式鎖降低鎖粒度( [4], [5], [6], [14])的方式來減少鎖競爭。但這種方式並不能有效地解決替換演算法中的鎖問題。在分散式鎖中,會將快取分成多塊,每一塊都使用一個本地鎖進行保護。使用輪詢或雜湊方式將資料頁均勻分佈到各塊。由於只有訪問相同塊的快取才會用到相同的鎖,因此可以改善鎖競爭。但由於記錄的歷史資訊位於本地的各個快取塊,因此全域性歷史資訊的缺少可能會對替換演算法的效能造成影響。例如,如果演算法需要檢測訪問順序,但此時相同順序中的頁被分佈到多個快取塊中,此時無法保證效能優勢。

總之,現有的對DBMS系統的研究和開發都聚焦在如何在高命中率和低鎖競爭之間進行權衡。相比於如何在這兩個方面採取妥協,我們的目標是在保持高階替換演算法效能優勢的同時,提供一種有效的框架來消除(大部分)替換演算法中的鎖競爭。通過為每個DBMS事務處理執行緒維護一個小的FIFO佇列,我們的框架提供了兩種主要的、可以普遍應用於所有替換演算法的擴充套件。一種是批量執行,通過批量的頁訪問來改善鎖競爭開銷;另一個是預載入,通過將替換演算法需要的資料預載入到處理器快取中來降低平均所載入時間。我們將這種引入批量(Batching)和預載入(Prefetching)的框架稱為BP-Wrapper。在8.2.3 版本的PostgreSQL 引入該框架後,通過消除TPCW 和TPCC負載中與快取頁替換有關的(幾乎所有)鎖競爭,使吞吐量提升了兩倍。

本論文剩餘的結構如下:在II章節中,我們簡單描述了典型資料庫系統中的替換演算法和鎖扮演的角色。在III章節中,我們描述了BP-Wrapper。第IV章節針對BP-wrapper進行了全面評估。第V和VI章節總結了相關的工作。

II. DBMSs中的快取管理背景

在一個DBMS系統中,它的快取在記憶體空間中儲存了DBMS中所有事務處理執行緒共享的固定大小的快取頁。從磁碟讀取的資料頁會被快取起來,以此避免(不久之後)重複獲取資料頁的I/O操作造成的開銷。快取管理器會使用資料結構(如連結的列表或雜湊表)來組織快取頁的後設資料。後設資料包含快取的資料頁的標識,頁狀態以及構成連結列表或雜湊表的指標。

圖1展示了典型的快取管理器。當一個執行緒請求資料頁時,該執行緒會查詢包含資料頁的快取頁。通常會使用一個雜湊表來加速查詢。如果找到(命中)快取頁,替換演算法會執行一個操作來更新資料結構,以此反映頁訪問。例如,當請求一個快取頁時,LRU替換演算法會從LRU列表中移除快取頁,並將其插入到列表末尾的MRU。然後返回快取頁,結束請求。如果沒有在快取中找到請求的資料頁(快取未命中),替換演算法會選擇一個犧牲頁,然後淘汰該頁中的資料,以此來為需要載入的資料騰出空間。LRU演算法總是選擇LRU列表末尾的快取頁作為犧牲頁。當資料頁讀入記憶體後,快取頁會轉移到LRU列表的首部,並返回給請求。

因為快取管理器是所有事務處理執行緒的請求經常使用的中央元件,因此必須以某種方式控制同時更新資料結構,以保證資料的完整性。DBMS使用排他鎖解決該問題。

image

圖1.典型的DBMS中的快取管理器。虛線矩形中的快取管理器可以同時查詢雜湊表,而陰影塊表示的替換演算法則必須序列化執行

使用鎖並不會影響雜湊表搜尋的系統擴充套件性,因為: (1)在雜湊表中,快取頁的後設資料均勻分佈在雜湊桶中。通過為每個桶提供一個鎖(而不是提供全域性鎖)來控制對桶的訪問。為了保證查詢時間,通常會使用大量桶,這樣就降低了多個執行緒訪問相同桶的概率。(2)如果多個執行緒不修改桶,則多個執行緒可以同時訪問該桶。雜湊桶很少會發生變化,只有當發生發生未命中以及當兩個雜湊桶因為同一個快取未命中(一個桶儲存犧牲頁,另一個儲存新頁)時才會發生變化。在具有大容量記憶體的DBMS中,只有一小部分訪問會發生快取未命中,因此我們不考慮雜湊表查詢中的鎖競爭場景。

相反,替換演算法會因為鎖競爭而嚴重影響效能,因為:(1)一個替換演算法會為它的資料結構使用一個單獨的鎖,這是一個集中式的訪問熱點。(2)大多數替換演算法會在每次頁訪問時更新它們的資料結構,因此一個執行緒必須在每次頁訪問請求時獲取鎖,並執行替換演算法操作。高度的鎖競爭可能會嚴重降低多處理器系統上DNMS系統的效能。

III. 使用BP-WRAPPER最小化鎖競爭

在一個替換演算法中,與鎖使用有關的開銷分為兩部分,稱為獲取鎖開銷和鎖預熱開銷。獲取鎖開銷是指當請求的鎖被另一個執行緒持有時,執行緒本身阻塞所造成的時間開銷。取決於具體實現,這類開銷可能是處於busy-waiting鎖佔用的CPU週期,和/或上下文切換佔用的CPU週期,開銷的多少由鎖競爭的激烈程度而定,如果一個服務上有很多處理器,且併發處理大量事務,則獲取鎖所需要的時間可能會大大超過小型系統所需要的時間。換句話說,開銷直接與替換演算法的擴充套件性關聯,因為每次頁訪問時都需要獲取一個鎖才能在鎖保護的共享資料結構中儲存訪問歷史記錄。

鎖預熱開銷是指在處理器快取中,為準備執行關鍵程式碼段所需的資料造成的開銷,或從非關鍵程式碼段(事務處理的程式碼)進入關鍵程式碼段(用於更新替換演算法的共享資料結構的程式碼)的過程中發生的處理器快取未命中懲罰。當獲取到鎖後,處理器快取中可能沒有描述鎖的資料以及關鍵程式碼段需要訪問的資料集,因此在快取預熱過程中可能會發生一系列快取未命中。重點是在一個執行緒獲取到鎖,而其他執行緒等待該鎖時的未命中懲罰可能會被放大。遵循最小化關鍵程式碼段開銷的原則,我們主要消除預熱開銷。

為了降低兩種潛在的鎖開銷,我們在BP-Wrapper中設計並整合了兩種方法,這兩種方法都由替換演算法本身實現,這樣任何替換演算法都可以直接通過BPWrapper獲得高擴充套件性。

A 使用批量技術降低鎖請求開銷

正如前面提到的,當一個執行緒進入關鍵程式碼段後,在代表替換演算法執行其操作前必須獲取鎖。通常不需要關心請求鎖造成的缺頁,因為與I/O操作造成的開銷相比,鎖請求開銷通常可以忽略不計,此外,其快取未命中數也大大低於資料庫系統。主要的挑戰是大部分替換演算法(特別是最近提出的演算法[1], [2], [3])的命中率都非常低,每次頁請求時都會訪問鎖保護的資料(即使資料已經存在於快取中),當鎖競爭激烈時,鎖請求所造成的開銷也會增加,併成為系統擴充套件的效能瓶頸。

現有資料庫系統中的替換演算法在每次頁訪問時都需要獲取鎖。獲取鎖的總開銷會隨著訪問頻率的增加而增加(通常發生在高併發的資料庫系統中)。我們使用一種稱為批量的技術來分攤並降低多個頁訪問造成的開銷。基本的思路是,當頁訪問累積到一定數目後,週期性地請求鎖,然後在持有鎖的週期內執行相應的替換操作。為了解該技術的效果如何,我們在一個具有16個處理器的系統上(詳細的配置參見Section IV)測量2Q替換演算法中的鎖請求以及一個執行緒處理一定數目的(針對鎖保護的資料結構的)頁訪問所佔用的(鎖持有)時間(包括請求鎖產生的開銷)。批量大小範圍為1-64(即,獲取鎖前累計的頁訪問數目為1到64)。圖2展示了獲取鎖和持有鎖的時間隨批量大小變化的平均值。我們發現,雖然多個訪問涉及的關鍵程式碼段的執行時間與訪問次數成正比,該測量結果展示了批處理技術的有效性,同時得出,當使用小批量時,如64,可以顯著降低獲取鎖的開銷。

image

圖2. 每次頁訪問涉及的鎖獲取和持有鎖時間的平均值隨批量數目的變化(1-64)。圖中的兩個軸都為對數標度,負載為DBT-1,處理器數目為16。

雖然每次頁訪問都需要操作鎖保護的資料,但根據替換演算法的要求,通常不必在頁面訪問之後立即執行操作。替換演算法有兩個特性為我們提供了使用批量技術顯著降低鎖請求頻率的機會。首先,延遲替換演算法(如LRU棧或LIRS棧[1])運算元據結構的操作,這樣就不會影響執行緒從快取中獲取正確的資料,因此也不會影響事務處理的正確性。其次,在具有百萬級別的頁的系統上(如我們實現的服務使用了64GB記憶體或上百萬個8KB的頁),延遲在鎖保護的資料中記錄最近的頁訪問資訊的操作(如64個頁訪問)可能會對分頁演算法的效能造成一些影響,此外,批量操作的執行順序不會因為採納批量技術而發生變化。

在批量技術中,我們為每個事務處理執行緒設定了一個FIFO佇列。當一個訪問與某個執行緒關聯時,會將本次的訪問資訊記錄在該執行緒的佇列中。特別地,當一個執行緒請求的頁位於快取中時,會線上程的FIFO佇列中記錄指向該頁的指標。圖3展示了使用批量技術的快取管理器。當佇列滿或佇列中的訪問記錄達到預設的閾值時(稱為批量閾值),我們會獲取到鎖並以批量的方式為佇列中的所有訪問執行替換演算法定義的操作,該過程稱為提交訪問記錄。在提交之後,佇列會變為空。通過FIFO佇列,一個執行緒可以允許訪問多個頁,而無需為頁替換演算法請求鎖(或不存在獲取鎖開銷)。

在批量技術的設計中,一種替代方案是在多個執行緒間共享FIFO佇列。但最終我們選擇為每個執行緒配置獨立的佇列,原因如下:

  • 私有佇列可以更精確地保證相應執行緒的頁訪問順序。訪問順序對某些頁替換演算法(如SEQ[12])來說至關重要,因為它們需要順序資訊來檢測訪問模式。
  • 在私有FIFO佇列中記錄訪問資訊降低了同步訪問的概率和整體開銷以及一致性成本,而這些開銷正是多執行緒共享FIFO佇列時,填充和清空佇列時所需要的。
image

圖3.使用批量技術的快取管理器

圖4的虛擬碼描述了批量技術,包括頁命中(hit())和未命中(miss())時的相關鎖操作。在虛擬碼中,當出現頁命中時,會首先在佇列中記錄此次訪問(Queue[])。然後,有兩個條件會觸發提交過程,並執行實際的替換演算法。第一個條件是一個佇列中有足夠數量的訪問(不能少於批量閾值),且可以獲取鎖(TryLock()成功)。由於獲取鎖的開銷通常比較高,因此不應該為少量的訪問付出此類開銷。TryLock()會嘗試獲取鎖。如果此時其他執行緒持有該鎖,該操作會失敗,但不會阻塞呼叫執行緒。反之呼叫執行緒會以非常低的開銷獲取鎖。雖然TryLock()的開銷小,但也不能在每個頁訪問時呼叫,否則會造成大量鎖競爭,並降低TryLock()獲取到鎖的機會。第二個條件是佇列滿。這種場景下,必須明確請求鎖(13行的Lock())。當兩種條件都不滿足時,替換演算法會為佇列中的每個訪問記錄涉及的鎖保護的資料執行延遲簿記工作。以LRU演算法為例,它會將每個訪問涉及的頁轉移到佇列末尾的MRU中。注意,虛擬碼實際描述了一個使用批量技術的框架,該框架可以給所有演算法使用,以高效訪問鎖保護的資料(替換演算法本身獨立於批量技術)。現有的替換演算法並沒有在提高其命中率上花費很多精力,必須做出一定修改來適應該技術,降低鎖開銷。與將演算法轉換為其時鐘近似的演算法相比,後者減少了鎖競爭,但在替換效能上做了一定妥協。

    /* a thread’s FIFO queue whose maximum size is S */
1   Page *Queue[S];
    /* the minimal number of pages in Queue[] to trigger a committing 
2   #define batch_threshold T
    /* current queue position to receive next page */
3   int Tail = 0;
    /* called upon a page hit */
4   void replacement_for_page_hit(Page *this_access) {
5       Queue[Tail] <-- this_access;
6       Tail = Tail + 1;
7       if (Tail >= batch_threshold)
            /* a non-blocking attempt to acquire lock */
8           trylock_outcome = TryLock();
9       if(trylock_outcome is a failure) {
10          if( Tail < S)
11              return;
12          else
13              Lock();
14      }
        /* A lock has been secured. Now commit the pages */
15      For each page P in Queue[]{
16          do what is specified by the replacement
                algorithm upon a hit on P;
17      }
18      UnLock();
19      Tail = 0;
20  }
    /* called upon a page miss */
21  void replacement_for_page_miss(Page *this_access) {
22      Lock();
23      for each page P in Queue[] {
24          do what is specified by the replacement
                algorithm upon a hit on P;
25      }
26      do what is specified by the replacement
            algorithm upon a miss on ’this_access’;
27      UnLock();
28      Tail = 0;
29  }

圖4. 獨立於替換演算法的框架虛擬碼,使用批量技術為演算法提供了高效訪問鎖保護的資料的能力

B 使用預載入技術降低鎖預熱開銷

image

圖5. 使用預載入技術將快取未命中懲罰轉移到鎖持有階段

我們使用預載入技術降低鎖預熱開銷(此為鎖持有時間的一部分)。在該技術中,我們會在請求鎖前讀取替換演算法的關鍵程式碼段所需要立即訪問的資料。以LRU演算法為例,我們會讀取在向頁列表末尾的MRU移動時涉及的前/後指標,以及鎖資料結構欄位。讀取的一個副作用是需要將資料載入到處理器快取中,但消除了關鍵程式碼段的快取未命中懲罰。圖5中示了該技術的潛在好處。

在無鎖的共享資料上執行預載入操作並不會對替換演算法中的全域性資料結構的完整性造成影響。預載入(讀)操作只會將資料載入到處理器快取中,不會修改任何資料。同時,如果預載入的資料線上程使用前已經被其他執行緒進行了修改,則處理器中的某些硬體機制會自動讓這些快取失效,或使用最新的值進行更新,以保證資料的一致性。

IV. 效能評估

我們已經在8.2.3版本的postgreSQL資料庫系統上實現了PB-Wrapper框架,包括兩個降低鎖競爭的技術。先前版本的PostgreSQL使用的是LRU和2Q替換演算法,但最終因為擴充套件性問題而被廢棄。從8.1版本開始,為了提升多處理器系統上快取管理的擴充套件性,postgreSQL採用了時鐘替換演算法。時鐘替換演算法在訪問命中時不需要鎖,消除了鎖競爭,並提供了最佳的擴充套件性。

在評估的第一部分,我們只關注擴充套件性問題。評估結果得出,將高階替換演算法(如2Q)和BP-Wrapper配合使用,可以獲得跟時鐘替換演算法相同的擴充套件性。在實驗中,我們將快取配置的足夠大來儲存所有效能測試中的工作集,並對快取進行預熱。這樣無論使用哪種替換演算法都不會發生未命中的情況。使用不同快取管理器的PostgreSQL系統之間的效能差異完全來自其實現的可擴充套件性上的差異,而非命中率。因此可擴充套件性越高,觀察到的效能越好。由於時鐘演算法的擴充套件性最好,因此當系統擴充套件時,其效能最佳。後面我們會對實現的擴充套件性進行測試,衡量其效能與時鐘實現的差異。Section IV-D展示了測試結果。

在實驗的第一部分,我們主要如下問題:(1)在降低替換演算法的鎖競爭上,那種技術更好,批量還是預載入?(2)與時鐘演算法相比,使用這些技術降低了多少鎖競爭?(3)這些技術的效能受多處理器平臺以及多核平臺的影響?

目前存在很多高階替換演算法可以在命中率方面提供(比時鐘演算法更)出色的效能,但除了LRU之外,其他演算法很難轉換為時鐘演算法,如果無法轉換,這類演算法就不適宜執行在高併發環境中。在評估的第二部分中可以看到,如果沒有采取降低鎖競爭的動作,在大型系統中,這類高階演算法的在命中率上的效能優勢將會大打折扣。同時,評估也給出了我們的技術在幫助保持演算法優勢的同時,提升了擴充套件性。

A. 被測系統

我們修改了8.2.3版本的postgreSQL,使用2Q演算法(提供高命中率的高階替換演算法)替換了原來的時鐘演算法。我們將修改後的系統稱為postgreSQL-2Q或簡稱為pg2Q(未針對較低的鎖爭用進行優化),在比較中作為基線系統。然後使用BP-Wrapper框架增強該基線系統。我們分別啟用了批量技術和預載入技術,並將啟動這些技術的系統分別命名為pgBatching和pgPref,在另一個系統上同時啟用批量技術和預載入技術,並將該系統命名為pgBat-Pre。原始的postgreSQL 8.2.3稱為pcClock。表T對這些系統進行了概括。我們還使用LIRS [1] 和 MQ [15]替換演算法分別替代了圖示中最後四個使用2Q演算法的系統。在實驗中,我們不會觀察使用這些演算法的系統與對應的使用2Q演算法的系統之間的效能差異。

image

表I

B. 實驗問題

僅需要對基線系統作有限修改就可以實驗批量和預載入。我們在postgreSQL 8.2.3中新增的程式碼不超過300行。大部分修改都位於一個單獨的檔案中(src/backend/storage/buffer/freelist.c),該檔案包含替換演算法的原始碼。在實現中,FIFO佇列中的每一項都包含兩個欄位:一個指向快取頁的後設資料(BufferDesc structure)的指標,另外一個儲存BufferTag(用於區分資料頁)。在表項提交前,我們會將表項中的BufferTag與快取頁的後設資料中的BufferTag進行比較,保證該資料頁是有效的或未被淘汰。如果快取中存在有效的資料頁,我們會在頁訪問時,執行替換演算法來更新該資料結構。在2Q演算法中,如果頁的結構是Am列表,更新後的頁會轉移到列表末尾的MRU中;在LIRS演算法中,它會在LIR或HIR列表中轉移;在MQ演算法中,它會在多個FIFO佇列中轉移。

C. 實驗配置和負載

我們會在傳統的單核SMP平臺和多核平臺上進行實驗。單核SMP平臺使用的是一個 SGI Altix 350 SMP伺服器,使用16個1.4GHz的Intel Itanium 2處理器,64GB記憶體。儲存為一個位於IBM FAStT600 turbo儲存子系統上的2TB大小的LUN,該LUN包含9個250GB的SATA磁碟,使用8+PRAID5進行配置。作業系統為 Red Hat Enterprise Linux AS release 3(SGI ProPack 3 SP6);多核平臺是一個DELL PowerEdge 1900伺服器,含2個2.66GHz的四核Xeon X5355處理器。為了方便,我們將每個計算核認為是一個處理器。這樣PowerEdge 1900伺服器有8個計算核,或8個處理器。記憶體大小為16GB。儲存為使用5個15K RPM SCSI磁碟的RAID5陣列,作業系統為Red Hat Enterprise Linux AS release 5。

我們使用DBT-1測試套件和來自OSDL資料庫測試套[16]的DBT2測試套件,以及一個構造的基準TableScan對系統進行測試。DBT-1模擬web使用者在一個線上書店瀏覽並下單的行為,它生成具有與(1.7版本的)TPC-W基準規範相同特徵的資料庫工作負載[17]。資料庫會生成100,000個條目,以及290萬個客戶。DBT2衍生自(5.0 [18]版本的)TPC-C規範,提供線上事務處理(OLTP)負載。在實驗中,我們將數倉的數目設定為50,TableScan會模擬順序掃描(資料庫的常用操作之一)。它會執行並行請求,每個請求都會掃描整張表,每張表包含800,000行,每行大小為128個位元組。

在實驗中,我們會通過設定postgreSQL後端程式的CPU親和性掩碼來變更postgreSQL使用的處理器數目,即負責PostgreSQL處理事務的執行緒。由於PostgreSQL的後端程式會在因為競爭而無法獲得鎖時阻塞自身並喚醒其他處理器。因此我們會通過設定postgreSQL的後端程式數大於處理器數來使處理器保持工作。實驗中,我們將FIFO佇列數設定為64,pgBatching 和 pgBat-Pre的批量閾值為32。

D. 擴充套件性實驗

在實驗中,我們評估了5個不同的postgreSQL分別在3種負載下的擴充套件性(在Altix 350伺服器上,我們將處理器數量從1增加到16。在PowerEdge 1900伺服器上,我們將處理器數量從1增加到8).為了消除負載執行過程中的缺頁,我們調整了共享快取的大小,確保記憶體中總是持有整個負載集。我們會採集事務的吞吐量(每秒的事務)以及平均響應時間。對於系統pg2Q、pgBatching、pgPref和pgBat-Pre,我們還會計算平均鎖競爭。當無法即時滿足一個鎖請求,併發生上下文切換時,就產生了一個鎖競爭。在負載執行過程中,平均鎖競爭定義為每毫秒內的訪問頁產生的鎖競爭數。圖6和圖7中展示了不同系統下,三個負載的吞吐量、平均響應時間和平均鎖競爭。

然後增加處理器的數目,與預期相符,pgClock的吞吐量隨處理器的數目線性增加,且DBT-1和TableScan負載下的平均響應時間適度增加。但在DBT2負載下,pcClock的吞吐量亞線性增加,而平均響應時間則大大增加。這是因為產生了鎖競爭(如發生預寫式日誌(Write-Ahead-Logging)活動時),使鎖競爭隨處理器的數目增加而愈發激烈。

當處理器數目小於4時,系統pg2Q可以維持其擴充套件性。在Altix 350 伺服器上,DBT-1和TableScan的處理器的數目大於8,或DBT-2的處理器大於4時,吞吐量達到飽和狀態,並且在進一步增加處理器時,平均響應時間也會大大增加。對於TableScan負載,當處理器的數目從8增加到16後,其吞吐量下降了12.7%。當使用16個處理器時,DBT1, DBT-2 和 TableScan負載下的吞吐量分別為pcClock的61.1%、56.5% 和 66.5%,其平均響應時間分別為pcClock的1.6、1.5 和 1.8倍。通過檢查平均鎖競爭的曲線,我們發現pg2Q每毫秒頁訪問的鎖競爭數目最多,並隨著處理器數目快速上升(注意,數字以對數刻度顯示)。因此這些實驗表明鎖競爭是系統效能降級的元凶。

在PowerEdge 1900伺服器上,我們觀察到類似的現象。TableScan負載下吞吐量更早地達到飽和狀態(或當處理器的數目達到4)。平均鎖競爭表明在多核的PowerEdge 1900上鎖競爭要比Altix 350更加激烈(特別是針對TableScan負載的基準測試)。當使用8個處理器時,PowerEdge 1900上的3個負載的平均鎖競爭分別為Altix 350上相應負載的產生的平均鎖競爭的74.4%、18.5% 和 270.2%。因此,相比 Altix 350,鎖競爭對PowerEdge 1900的效能影響更大。使用8個處理器時,PowerEdge 1900上的系統pg2Q的吞吐量為pgClock吞吐量的37.9%、52.1% 和 57.2%,是Altix 350上的系統pgClock的吞吐量的30.1%、51.5% 和 32.6%。類似地,當使用系統pg2Q時,在PowerEdge 1900上鎖競爭提高的工作負載的平均響應時間百分比要大於在ALTIX 350上提升的工作負載的平均響應時間百分比。

PowerEdge 1900上的鎖競爭更激烈的原因歸咎於系統使用的處理器。PowerEdge 1900上的Xeon X5355處理器具有快取預載入模組,該模組可以通過預測,將資料載入到最後一級快取,從而加速順序記憶體訪問。而Itanium 2處理器則沒有這樣的硬體支援,因此在PowerEdge 1900伺服器上,預載入模組可以加快關鍵段外的計算(通常順序訪問記憶體),但被鎖保護的替換演算法的操作通常會隨機訪問記憶體,幾乎不能被(預載入模組)加速。因此PowerEdge 1900 伺服器上的鎖競爭更激烈,花在關鍵部分上的時間比例要大於 Altix 350。

與pg2Q相比,pgPref在Altix 350系統上的平均鎖競爭減少33.7%至82.6%,在PowerEdge 1900上的平均鎖競爭減少20.8%至87.5%。這是因為預載入降低了鎖持有時間,並相應增加了獲取到鎖的機會。最終,pgPerf的吞吐量比pg2Q高26.1%,而平均響應時間則小 25.2%。我們注意到在Altix 350上執行預載入技術比PowerEdge 1900更有效,這是因為在X5355處理器上執行長流水線且無序的事務,增加了處理快取未命中的負擔。基於此觀察,我們可以預見預載入技術在大型多核處理器系統(如Sun Niagara 2處理器和Azul vega處理器)上可以更有效地降低鎖競爭。這些處理器在一個晶片內有很多順序的用於支援併發執行緒計算核。因此相比於具有少量無序計算核的處理器(如Xeon 5355處理器),快取未命中對此類系統的效能影響更大。

image

圖6. Altix 350上,5種postgreSQL系統(pgClock、pg2Q、pgPref、pgBatching 和 pgBat-Pre)在DBT-1、DBT-2 和 TableScan負載下的吞吐量、平均響應時間和平均鎖競爭(處理器數從1增加到16)。注意吞吐量和平均鎖競爭曲線的Y軸為對數刻度。僅使用一個處理器時,我們沒有展示平均鎖競爭,這是因為這種情況下,鎖競爭非常少,無法在圖中展示出來。

在平均鎖競爭的曲線中可以看到,系統pgPref的擴充套件性要弱於pg2Q,這是因為批量技術無法顯著降低鎖競爭,特別 是在使用大於4個處理器時。例如,當在 Altix 350伺服器上使用兩個處理器時,在3個負載下,平均降低了pg2Q上82.4%的平均鎖競爭,當處理器數目為4時,降低的鎖競爭的百分比減少到60.2%;當處理器數目為8時,降低的鎖競爭的百分比減少到46.3%;當處理器數目為16時,降低的鎖競爭的百分比減少到38.6%。pgPref中的批量技術降低了鎖競爭,並提升了系統的吞吐量。但隨著吞吐量的增加,請求的鎖也愈發頻繁,抵消了降低鎖競爭帶來的好處。在處理器數目較多的系統上,該現象更加明顯。

在實驗中,系統pgBatching展示了與pgClock幾乎相同的擴充套件性,為擴充套件性最佳的演算法。當處理器數目增加時,它的吞吐量曲線和平均響應時間曲線與pgclock非常契合。在平均鎖競爭曲線圖中可以看到,系統pgBatching將鎖競爭數從9000多降低了127倍,提升了擴充套件性。我們還注意到,使用16個處理器的pgBatching的平均鎖競爭要遠低於使用2個處理器的pgPref和pg2Q。

image

圖7. DELL PowerEdge 1900上,5種postgreSQL系統(pgClock、pg2Q、pgPref、pgBatching 和 pgBat-Pre)在DBT-1、DBT-2 和 TableScan負載下的吞吐量、平均響應時間和平均鎖競爭(處理器數從1增加到8)。注意吞吐量和平均鎖競爭曲線的Y軸為對數刻度。僅使用一個處理器時,我們沒有展示平均鎖競爭,這是因為這種情況下,鎖競爭非常少,無法在圖中展示出來。

同時使用批量和預載入技術時,相比pgBatching,系統pgBat-Pre可以進一步降低鎖競爭。然而,如圖所示,降低鎖競爭並不會提高吞吐量或降低平均響應時間,這是因為pgBatching的平均鎖競爭非常少,因此對效能的影響範圍也不大。當使用16個處理器時,在每100萬個頁訪問中,pgBatching和pgBatPre的鎖競爭在400個(或少於400個)左右。當繼續增加處理器的數目時,在常見的多核處理器中,鎖競爭會變得更嚴重,這種情況下,pgBat-Pre可以幫助進一步降低鎖競爭,提升系統效能。

E. 引數敏感性研究

系統pgBatching使用一個小的FIFO佇列來記錄每個postgreSQL後端程式的訪問歷史來分攤鎖請求開銷。批量技術中有兩個主要引數,一個是FIFO佇列的大小,另一個是批量閾值,或觸發提交時佇列中的最少元素個數。在本節中,我們將會在包含16個處理器的 Altix 350系統上對兩個引數的敏感性進行研究。
首先在保證批量閾值為佇列大小一半的前提下,逐步將佇列大小從2增加到64,並使用3個負載執行paBatching,表II中展示了對應的吞吐量和平均鎖競爭。然後將佇列大小固定為64,並使用3個負載執行paBatching,表III中展示了對應的吞吐量和平均鎖競爭。

表 II .當佇列長度從2增加到64,且批量閾值為1/2的佇列長度時,在系統pgBatching在負載DBT-1, DBT-2 和 TABLESCAN下的吞吐量和平均鎖競爭

image

表 III. 批量閾值從1增加到64時,DBT-1,DBT-2和TABLESCAN負載下系統pgBatching的吞吐量和平均鎖競爭

image

增加佇列大小可以降低平均鎖競爭並提升吞吐量,因為此時可以一次性提交更大批的訪問歷史。當佇列大小從2增加大8時,DBT-1的平均鎖競爭降低了477倍,DBT-2降低了32倍,TableScan降低了130倍,對應的吞吐量則大幅上升。當佇列大小超過8時,增加佇列大小仍然可以降低平均鎖競爭,但很難將這部分提升轉換為吞吐量(鎖競爭已經被大幅降低了)。同時,我們發現,即使佇列非常小(2),Pgbatching也優於PGPref。
直觀上,選擇一個小的批量閾值可以讓postgreSQL後端程式更容易獲取鎖(無需在FIFO佇列滿之前多次阻塞呼叫TryLock()獲取鎖),因此可以降低鎖競爭的概率。但表III的資料顯示出,只有當批量閾值的數目小於32時才會出現這種趨勢。當將批量閾值從1增加到32,我們發現其平均競爭降低了,且吞吐量增加。這是因為較低的閾值可以提前觸發提交動作(或可以以小批量提交訪問歷史),並增加執行緒通過TryLock()獲取鎖的概率。

image

圖8. PowerEdge 1900伺服器上,在3個postgreSQL系統(pgClock, pg2Q 和pgBat-Pre)中使用DBT-1和DBT-2負載時的命中率和歸一化吞吐量(處理器數目為8)。吞吐量為與pgClock系統的歸一化值。

當批量閾值超過32後,後端程式批量提交的訪問歷史會大於32(個頁訪問),通過這樣方式有效分攤獲取鎖開銷。上面展示了非阻塞獲取鎖(TryLock())的優勢。使用相對較小的閾值,在FIFO佇列滿之前會多次呼叫TryLock(),且在不阻塞自身的前提下獲取到鎖的概率也非常高。因此,我們可以看到,當批量閾值從32增加到64時,平均鎖競爭增加,吞吐量下降。
當批量閾值設定為佇列大小(64)時,後端程式獲取TryLock()的概率將非常小。最終,可以看到平均鎖競爭的大幅上升以及吞吐量的下降(儘管使用大批量分攤了獲取鎖的成本)。實驗表明為了獲得TryLock()的優勢,有必要讓批量閾值足夠小於佇列的大小。

F. 總體效能

本節中,我們將評估當PowerEdge 1900上的處理器數為8時,3個系統pgClock, pg2Q 和 pgBat-Pre的整體效能。我們已經評估了在快取大小等於負載資料大小情況下各種系統的擴充套件性。實驗結果表明,結合批量和預載入技術,可以有效降低鎖競爭。然而,真實系統中,快取大小通常遠小於資料大小。因此,通過提升命中率來降低I/O操作的開銷對整體效能來說非常重要。在本次實驗中,我們將快取大小從32MB增加到1024MB,並讓系統發起直接I/O來繞過作業系統的快取。DBT-1 和 DBT2的資料量分別為6.8GB 和 5.6GB,因此快取無法滿足所有的訪問。
圖8展示了3個系統中的命中率吞吐量。當記憶體比較小時(小於256MB),系統是I/O密集型的。通過維持更好的高命中率,系統pg2Q和pgBat-Pre產生了比系統pgClock更高的吞吐量。但隨著記憶體大小的增加,系統的整體效能主要取決於其擴充套件性,系統pg2Q的命中率優勢則不那麼明顯。當快取大小增加到1GB時,整體效能將低於系統pgClock。同時,系統pgBat-Pref在提升擴充套件性的同時,維持了其效能優勢。同時也可以注意到,pg2Q個pgBatPref的命中率曲線重疊率非常高。表明,我們的技術並沒有影響命中率。

V. 相關工作

在系統和資料庫社群中,已經積極開展了應對鎖競爭引起的效能降級的研究。通常可以通過如下方式降低鎖競爭:

A. 使用分散式鎖降低鎖的粒度

降低鎖粒度是降低鎖競爭的常用方法。使用多個細粒度鎖來替換全域性鎖可以移除單點競爭。Oracle Universal Server [7], ADABAS [8] 和 Mr.LRU 的替換演算法[19]使用多個列表來管理快取,每個列表由獨立的鎖保護。當一個新頁進入快取後,Oracle Universal Server會將該頁插入無鎖列表的首部,而ADABAS會以輪詢的方式選擇一個列表。它們都允許從一個列表中淘汰頁,並將其插入另一個列表。因此很多替換演算法,如2Q[2]和LIRS[1]將無法工作。為了解決該問題,Mr.LRU會通過雜湊選擇列表,保證每次從磁碟載入一個頁時,該頁都能進入相同的列表。
(包括Mr.LRU使用的)分散式鎖方式有如下嚴重缺陷:(1)無法用於實現檢測訪問順序的替換演算法,如SEQ[12],因為相同順序的頁可能被分佈到多個列表中。(2)雖然可以將頁均勻地分佈到多個列表中,但對快取頁的訪問則可能並不均勻。具有熱點頁的列表,如頂級索引或用於並行連線(parallel join)的小型表中的頁,仍然會受制於鎖競爭。(3)為了降低競爭,需要將快取切分為成百上千個列表。這樣每個列表的大小要遠小於快取的大小。使用小型列表時,需要對這些頁進行特殊處理,防止被淘汰,如髒頁和索引頁就有可能被永久從快取中淘汰。相比之下,我們的框架能夠實現所有的替換演算法,而無需切分快取。

B. 降低鎖持有時間

從前面可知,鎖的持有時間越長,競爭越嚴重。降低鎖持有時間是另一種有效降低鎖競爭的方式。
Charm中使用的TSTE(二階段事務執行)策略將磁碟I/O和鎖獲取分成兩個互斥的階段,從而保證一個事務所需要的所有資料頁在鎖定前就已經存在於記憶體中。通過這種方式TSTE將磁碟事務處理系統中的鎖競爭延遲降低到與記憶體事務系統相同的水平。
在2.4版本的Linux核心中,排程器會遍歷使用自旋鎖保護的全域性佇列中的task結構體,並從中選擇一個任務執行。論文[21]表明,在遍歷過程中,硬體快取中的不必要的衝突未命中可能會增加遍歷時間,進而惡化自旋鎖上的競爭。通過在記憶體中仔細布局task結構,減少遍歷所需的時間,可以避免大部分衝突未命中,並且可以大大減少鎖競爭。相比之下,我們的框架可以通過預載入技術減少在執行替換演算法時,用在降低硬體快取未命中中的鎖持有時間,以及在批量模式下,為多個訪問執行替換演算法時的鎖持有時間。

C. Wait-Free 同步和事務記憶體

由於鎖同步會導致效能降級和優先順序翻轉等問題。wait-free同步[22]通過確保一個事務能夠在有限的步驟內完成訪問共享資源的操作(無需關心其他事務的執行進度)來解決這些問題。然而,很難設計採納這種技術的程式,同時有可能找不到實現了wait-free的演算法。再者,wait-free同步要求硬體支援原子語義。例如,雙邊佇列的wait-free實現要求實現Double CompareAnd-Swap (DCAS),而大多數處理器不支援這類實現。

事務記憶體是另一種解決這類鎖競爭的方式。在事務記憶體中,一個事務被認為是基於共享資源的一系列操作。由實現事務記憶體的硬體[23]或軟體[24]保證執行的原子性。它通過啟用樂觀併發控制[25],[26]來提升系統的擴充套件性。雖然硬體事務記憶體還沒有實現,但已經有各種軟體事務記憶體(STM)實現。對STM和鎖同步的效能比較發現,只有當事務處理[27]不會頻繁修改共享資源時,STM才會優於鎖。因為替換演算法中的資料結構可以(基於每個頁訪問)被頻繁修改,事務記憶體幾乎無法提升替換演算法的擴充套件性。相比之下,可以在軟體中簡單實現BP-Wrapper中的批量和預載入技術,且不需要硬體支援。

VI. 總結和後續工作

在本論文中,我們解決了DBMS系統中高階替換演算法由於鎖競爭導致的擴充套件性問題。我們提出了一種有效且可擴充套件的框架,BP-Wrapper,該框架中的批量和預載入技術可以在不修改演算法的前提下適用於所有替換演算法。由於不需要對演算法進行修改,因此可以保證原始替換演算法的效能優勢,並節省人力。該框架的唯一開銷是為每個事務處理執行緒分配一個小型的FIFO佇列,該佇列中儲存了執行緒最近的訪問資訊。
我們已經在postgreSQL 8.2.3中實現了該框架,並使用一個TPC-W-like負載,一個TPC-C-like負載以及一個組合負載進行測試。我們的效能評估表明,與未修改的替換演算法(如LRU和2Q)相比,BP-Wrapper可以增加近兩倍的系統吞吐量,以及獲得如同在命中時不使用鎖的演算法一樣的擴充套件性(如時鐘演算法)。後續,我們計劃在大型系統上(特別是具有更多個多核處理器的系統)以及使用真實負載的生產環境上評估BP-Wrapper。

引用

[1] S. Jiang and X. Zhang, "LIRS: an efficient low inter-reference recency set replacement policy to improve buffer cache performance,"in SIGMETRICS '02: Proceedings of the 2002 ACM SIGMETRICS international conference on Measurement and modeling of computer systems, 2002, pp. 31–42.

[2] T. Johnson and D. Shasha, "2Q: A low overhead high performance buffer management replacement algorithm," in VLDB ’94: Proceedings of the 20th International Conference on Very Large Data Bases. Morgan Kaufmann Publishers Inc., 1994, pp. 439–450.

[3] N. Megiddo and D. S. Modha, ”ARC: A self-tuning, low overhead replacement cache,“ in FAST ’03: Proceedings of the 2nd USENIX Conference on File and Storage Technologies, 2003, pp. 115–130.

[4] T. E. Anderson, “The performance of spin-lock alternatives for sharedmemory multiprocessors,” in IEEE Transactions on Parallel and Distributed Systems, vol. 1, no. 1, 1990, pp. 6–16.

[5] J. M. Mellor-Crummey and M. L. Scott, “Algorithms for scalable synchronization on shared-memory multiprocessors,” ACM Trans. Comput. Syst., vol. 9, no. 1, pp. 21–65, 1991.

[6] X. Zhang, R. Castaneda, and E. W. Chan, “Spin-lock synchronization on ˜ the butterfly and KSR1,” IEEE Parallel Distrib. Technol., vol. 2, no. 1, pp. 51–63, 1994.

[7] W. Bridge, A. Joshi, M. Keihl, T. Lahiri, J. Loaiza, and N. MacNaughton, “The oracle universal server buffer manager,” in VLDB’97, Proceedings of 23rd International Conference on Very Large Data Bases, August 25-29, 1997, Athens, Greece, M. Jarke, M. J. Carey, K. R. Dittrich, F. H. Lochovsky, P. Loucopoulos, and M. A. Jeusfeld, Eds. Morgan Kaufmann, 1997, pp. 590–594.

[8] H. Schoning, “The ADABAS buffer pool manager,” in ¨ VLDB ’98: Proceedings of the 24rd International Conference on Very Large Data Bases. Morgan Kaufmann Publishers Inc., 1998, pp. 675–679.

[9] F. J. Corbato, “A paging experiment with the multics system,” in In Honor of Philip M. Morse, Feshbach and Ingard, Eds. Cambridge, Mass: MIT Press, 1969, p. 217.

[10] S. Jiang, F. Chen, and X. Zhang, “CLOCK-Pro: An effective improvement of the CLOCK replacement,” in Proceedings of the Annual USENIX Technical Conference ’05, Anaheim, CA, 2005.

[11] S. Bansal and D. S. Modha, “CAR: Clock with adaptive replacement,” in FAST ’04: Proceedings of the 3rd USENIX Conference on File and Storage Technologies, 2004, pp. 187–200.

[12] G. Glass and P. Cao, “Adaptive page replacement based on memory reference behavior,” in SIGMETRICS ’97: Proceedings of the 1997 ACM SIGMETRICS international conference on Measurement and modeling of computer systems, 1997, pp. 115–126.

[13] “DB2 for z/OS: DB2 database design,” 2004, URL: http://www.ibm.com/developerworks/db2/library/techarticle/dm0408whitlark/index.html.

[14] M. Blasgen, J. Gray, M. Mitoma, and T. Price, “The convoy phenomenon,” SIGOPS Oper. Syst. Rev., vol. 13, no. 2, pp. 20–25, 1979.

[15] Y. Zhou, J. Philbin, and K. Li, “The multi-queue replacement algorithm for second level buffer caches,” in Proceedings of the General Track: 2002 USENIX Annual Technical Conference, 2001, pp. 91–104.

[16] The Open Source Development Laboratory, “OSDL - database test suite,” URL: http://old. linux-foundation.org/lab activities/kernel testing/ osdl database test suite/.

[17] Transaction Processing Performance Council, “TPC-W,” URL: http://www.tpc.org/tpcw.

[18] Transaction Processing Performance Council., “TPC-C,” URL: http://www.tpc.org/tpcc.

[19] W. Wang, “Storage management for large scale systems,” Ph.D. dissertation, Department of Computer Science, University of Saskatchewan, Canada, 2004.

[20] L. Huang and T. cker Chiueh, “Charm: An I/O-driven execution strategy for high-performance transaction processing,” in Proceedings of the General Track: 2002 USENIX Annual Technical Conference, 2002, pp. 275–288.

[21] S. Yamamura, A. Hirai, M. Sato, M. Yamamoto, A. Naruse, and K. Kumon, “Speeding up kernel scheduler by reducing cache misses,” in Proceedings of the FREENIX Track: 2002 USENIX Annual Technical Conference, 2002, pp. 275–285.

[22] M. Herlihy, “Wait-free synchronization,” ACM Trans. Program. Lang. Syst., vol. 13, no. 1, pp. 124–149, 1991.

[23] M. Herlihy and J. E. B. Moss, “Transactional memory: Architectural support for lock-free data structures,” in Proceedings of the 20th Annual International Symposium on Computer Architecture, 1993, pp. 289–300.

[24] N. Shavit and D. Touitou, “Software transactional memory,” in PODC ’95: Proceedings of the fourteenth annual ACM symposium on Principles of distributed computing, 1995, pp. 204–213.

[25] H. T. Kung and J. T. Robinson, “On optimistic methods for concurrency control,” ACM Trans. Database Syst., vol. 6, no. 2, pp. 213–226, 1981.

[26] D. Gawlick and D. Kinkade, “Varieties of concurrency control in IMS/VS fast path,” IEEE Database Eng. Bull., vol. 8, no. 2, pp. 3–10, 1985.

[27] B. Saha, A.-R. Adl-Tabatabai, R. L. Hudson, C. C. Minh, and B. Hertzberg, “McRT-STM: a high performance software transactional memory system for a multi-core runtime,” in PPoPP ’06: Proceedings of the eleventh ACM SIGPLAN symposium on Principles and practice of parallel programming, 2006, pp. 187–197.

參考

相關文章