性能測試基礎知識-處理器調度程序性能
處理器調度程序 性能 概述 線程支持 線程可看作開銷低的進程。它是一個可分派實體,創建它需要的資源比創建一個進程需要的資源少。 進程由一個或多個線程組成。事實上,操作系統的早期發行版中負載的直接遷移就是繼續創建和管理進程。每個新進程在創建時只帶
處理器調度程序
性能概述
線程支持
線程可看作開銷低的進程。它是一個可分派實體,創建它需要的資源比創建一個進程需要的資源少。
進程由一個或多個線程組成。事實上,操作系統的早期發行版中負載的直接遷移就是繼續創建和管理進程。每個新進程在創建時只帶有單一的線程,該線程具有其父進程的優先級并與其它進程中的線程爭用處理器。進程在執行時擁有它所使用的資源,而線程僅僅擁有它的當前狀態。
當新的或修改的應用程序利用操作系統的線程支持創建額外的線程時,那些線程在該進程的上下文中創建。它們共享進程的私有段和其它資源。
進程中的一個用戶線程有一個特定的爭用作用域。如果爭用作用域是全局的,則該線程與系統中所有其它線程一起爭用處理器時間。在進程創建時產生的線程具有全局爭用作用域。如果爭用作用域本地的,則該線程與進程中的其它線程競爭以成為進程共享的處理器時間的接收方。
決定接下來應該運行哪個線程的算法叫調度策略。
進程和線程
進程是系統中的一個活動,它由某個命令、shell 程序或另一進程啟動。
進程的屬性如下:
pid
pgid
uid
gid
環境
cwd
文件描述符
信號操作
進程統計信息
nice
線程的屬性如下:
堆棧
調度策略
調度優先級
暫掛信號
阻塞信號
線程特定的數據
每個進程由一個或多個線程組成。線程是一個單獨的控制序列流。多個控制線程允許應用程序進行重疊操作,例如讀取終端和寫文件。
多個控制線程也允許應用程序同時為來自多個用戶的請求服務。線程提供了這些能力而不需多個進程那樣的額外開銷,例如要通過 fork() 系統調用創建多個進程。
AIX 4.3.1 中引入了一個快速的 fork 例程 f_fork()。該例程對多線程應用程序非常有用,它們將立刻調用 exec() 子例程,前提是之前應先調用 fork() 子例程。fork() 子例程運行起來較慢,因為在實際派生及讓其子例程運行全部子處理程序來初始化所有鎖之前,它必須調用 fork 處理程序獲得所有的庫鎖。f_fork() 子例程忽略這些處理程序并直接調用 kfork() 系統調用。
Web 服務器是一個可以使用 f_fork() 子例程的很好的應用程序示例。
進程和線程的優先級
優先級管理工具處理進程的優先級。在 AIX V4 中,進程優先級只是線程優先級的前驅。當調用 fork() 子例程時,會創建一個進程和一個要在其中運行的線程。線程的優先級歸結于進程。
內核為每個線程維護一個優先級值(有時稱為調度優先級)。優先級值是一個正整數且與關聯線程的重要性的變化方向相反。也就是說,較小的優先級值表示一個相對重要的線程。當調度程序尋找線程進行分派時,它選擇具有較小優先級值的可分派線程。
線程可以有固定的優先級或不固定的優先級。優先級固定的線程的優先級值是一個常量,而優先級不固定的線程的優先級值根據用戶線程最小優先級級別(常量 40)、線程的 nice 值(缺省值是 20,可隨意由 nice 或 renice 命令進行設置)和其處理器使用的損失而變化。
線程的優先級可以固定成某個值,如果用 setpri() 子例程設置(固定)它們的優先級的話,它們可以具有小于 40 的優先級值。這些線程不會受到調度程序重算算法的影響。如果它們的優先級值固定且小于 40,這些線程將在可以運行所有用戶線程之前運行和完成。例如,一個具有固定值 10 的線程將在具有固定值 15 的線程之前運行。
用戶可以應用 nice 命令使線程的不固定優先級變低。系統管理員可將一個負的 nice 值應用給線程,這樣就給了它較好的優先級。
下圖顯示了一些可以更改優先級值的方法。
圖 6. 如何確定優先級值. 插圖顯示了如何能在執行過程中或應用了 nice 命令之后更改線程調度優先級值。優先級值越小,線程優先級越高。開始時,nice 值缺省為 20 而基本優先級缺省為 40。在執行一些操作及處理器損失后,nice 的值仍為 20 且基本優先級仍為 40。在運行 renice --5 命令后及使用和以前相同的處理器的情況下,nice 值現在是 15 而基本優先級仍為 40。在以 50 的值發出子例程 setpri() 之后,固定優先級現在是 50 而 nice 值和處理器的使用無關。
線程的 nice 值在創建線程時設置并且在線程的整個生命期中都是常量,除非用戶通過 renice 命令或 setpri()、setpriority()、thread_setsched() 或 nice() 系統調用明確更改了它的值。
處理器損失是一個整數,它通過線程最近的處理器使用來計算。如果每次在一個 10 ms 的時鐘滴答結束時線程受處理器控制,則最近的處理器使用值近似加 1,直到達到最大值 120。每個滴答的實際優先級損失隨著 nice 的值增加。所有線程的最近處理器使用值每秒重算一次。
結果如下:
不固定優先級的線程的優先級隨著其最近處理器使用的增加而變低,反之亦然。這暗示一般來講,某線程最近被分配的時間片越多,則它被分配下一個時間片的可能性越小。
不固定優先級的線程的優先級隨著其 nice 值的增加而變低,反之亦然。
注:
使用多處理器運行隊列及其負載平衡機制以后,nice 或 renice 的值對線程的優先級可能沒有預期的影響,因為較低優先級的運行時間可能等于或大于較高優先級的運行時間。要求 nice 或 renice 產生預期效果的線程應該放在全局運行隊列中。
可以使用命令 ps 顯示進程的優先級值、nice 值和短期的處理器使用值。
請參閱『處理器的控制爭用』中對使用 nice 和 renice 命令的更詳細的討論。
請參閱『調諧線程優先級值的計算』,里面有處理器損失計算和最近處理器使用值衰減的詳細信息。
優先級機制也用于 AIX 工作負載管理器中來加強處理器資源管理。因為在工作負載管理器下分類的線程具有的優先級由工作負載管理器管理,它們可能與沒有在工作負載管理器下分類的線程具有不同的優先級行為。
線程的調度策略
下面是線程調度策略的可能值:
SCHED_FIFO
這種策略的線程被調度后,它會一直運行到結束,除非被阻塞或有一個較高優先級的線程可分派,它將自愿服從處理器的控制。只有固定優先級的線程才能有 SCHED_FIFO 調度策略。
SCHED_RR
當一個 SCHED_RR 線程在時間片的末尾有控制權時,它將移動到和它具有相同優先級的可分派線程隊列的尾部。只有固定優先級的線程才能有 SCHED_RR 的調度策略。
SCHED_OTHER
這個策略在“POSIX 標準 1003.4a”中作為定義的執行程序進行定義。在每個時鐘中斷時重算運行線程的優先級值意味著該線程可能失去控制權,因為它的優先級值已經超過了另一可分派線程的優先級值。
SCHED_FIFO2
該策略和 SCHED_FIFO 相同,只是它允許一個僅睡眠了很短時間的線程在被喚醒時可放置在其運行隊列的頭部。這個時間周期是相似性限制(可用 schedtune -a 進行調節)。該策略僅可用于 AIX 4.3.3 及其后續版本。
SCHED_FIFO3
調度策略設置成 SCHED_FIFO3 的線程總是放置在運行隊列的頭部。為了防止屬于 SCHED_FIFO2 調度策略的線程放置在 SCHED_FIFO3 之前,當 SCHED_FIFO3 線程入隊列時更改運行隊列參數,這樣屬于 SCHED_FIFO2 的線程就不滿足使其能夠加入運行隊列頭部時必須滿足的標準。該策略僅可用于 AIX 4.3.3 及其后續版本。
SCHED_FIFO4
只要優先級值相差 1,較高優先級的 SCHED_FIFO4 調度類線程就不會搶占當前正運行的低優先級線程。缺省行為是當前運行于某給定 CPU 的低優先級線程被有資格在同一 CPU 上運行的高優先級線程搶占。該策略僅可用于 AIX 5L V5100-01 + APAR IY22854 及其后續版本。
調度策略可用系統調用 thread_setsched() 進行設置并且僅對調用線程有效。然而,通過指定進程標識發出 setpri() 調用可將線程設置成 SCHED_RR 調度策略;setpri() 的調用者和 setpri() 的目標不必匹配。
只有那些具有 root 權限的進程可以發出 setpri() 系統調用。只有那些具有 root 權限的線程可將調度策略更改成任何 SCHED_FIFO 選項或 SCHED_RR。如果調度策略是 SCHED_OTHER,則優先級參數被 thread_setsched()子例程忽略。
線程的主要優點是適用于當前由多個異步進程組成的應用程序。這些應用程序可通過轉變成多線程結構使得系統中有較輕的負載。
調度程序運行隊列
調度程序維護一個所有就緒等待分派的線程的運行隊列。
給定優先級的所有可分派線程在運行隊列中占有一定的位置。
調度程序的基本可分派實體是線程。AIX 5.1 維護 256 個運行隊列(128 個在 AIX 4.3 及以前的發行版中)。在 AIX 5.1 中,運行隊列與每個線程優先級字段可能值的范圍(從 0 到 255)直接相關。這個方法使調度程序更容易確定哪個線程最先運行。調度程序無需搜索一個完整的運行隊列,只需要考慮一個掩碼,該掩碼的某一位啟用后可表示在相應的運行隊列中存在就緒等待運行的線程。
線程的優先級值快速而頻繁地變更。持續的變動歸因于調度程序重算優先級的方法。然而,這并不適用于固定優先級的線程。
從 AIX 4.3.3 開始,每個處理器都有自己的運行隊列。
性能工具中報告的運行隊列值將是每個運行隊列中所有線程的總和。讓每個處理器都有自己的運行隊列可節省分派鎖的開銷并改善總體的處理器相似性。線程通常會更加趨向于留在同一處理器中。如果因為另一處理器上的事件使某線程變得可運行且有空閑的處理器的話,即使不同于最近可運行線程曾經運行過的處理器,該線程也只會立即被分派。在可以檢查處理器狀態(例如在該線程的處理器上的中斷)之前不會出現搶占。
在具有多個運行隊列的多處理器系統中,可能出現瞬間的優先級倒置。在任何一個時間點都可能出現這種情況:某個運行隊列能使若干線程具有的優先級比另一運行隊列更有利。AIX 有一些機制可以隨著時間的推移來進行優先級平衡,但是如果要求嚴格的優先級(例如,對于實時應用程序)可用一個叫做 RT_GRQ 的環境變量,如果將它設置成 ON,將導致該線程位于一個全局運行隊列中。在那種情況下,將搜索全局運行隊列來察看哪個線程具有最佳優先級。這可以改善中斷驅動線程的性能。如果將 schedtune -F 設置成 1,以固定優先級運行的線程就放置在全局運行隊列中。
運行隊列中的線程平均數可在命令 vmstat 輸出的第一列中看到。如果用處理器數去除這個數,結果是每個處理器上可運行線程的平均數。如果這個值大于 1,這些線程必須等待輪到它們使用處理器(這個數越大,性能延遲可能越明顯)。
當某線程移到運行隊列的末端時(例如,當線程在時間片的末尾擁有控制權時),它會移動到具有相同優先級值的隊列中最后一個線程之后的位置上。
調度程序處理器時間片
處理器時間片是調度程序轉換到另一個具有相同優先級的線程之前,一個 SCHED_RR 線程能獲得的時間的總和??梢允褂妹?schedtune 的選項 -t 在時間片上以 10 毫秒的增量來增加時鐘滴答數(參閱『用 schedtune 命令修改調度程序時間片』)。
注:
時間片并不是保證的處理器時間量。它是一個線程在面臨由另一線程取代的可能性之前可以受控的最長時間。在控制時間達到完整時間片之前有很多方法可使線程失去處理器的控制。
方式轉換
用戶進程在需要訪問系統資源時會經歷一個方式轉換。這通過系統調用接口或諸如缺頁故障這樣的中斷來實現。有兩種方式:
用戶方式
內核方式
花在用戶方式(應用程序和共享庫)下的處理器時間作為用戶時間在一些命令的輸出中反映出來,例如,vmstat、
iostat 和 sar 命令?;ㄔ趦群朔绞较碌奶幚砥鲿r間作為系統時間在這些命令的輸出中反映出來。
用戶方式
在用戶保護域中執行的程序是用戶進程。在這種保護域中執行的代碼以用戶執行方式執行,且具有下列訪問:
讀/寫訪問進程專用區域中的用戶數據
讀訪問用戶文本和共享文本區域
使用共享內存功能訪問共享數據區域
在用戶保護域中執行的程序不能訪問內核或內核數據段,除非通過使用系統調用間接訪問。在該保護域中的程序只能影響自身的執行環境并在進程或非特權狀態下執行。
內核方式
在內核保護域中執行的程序包含中斷處理程序、內核進程、基內核和內核擴展(設備驅動程序、系統調用和文件系統)。這個保護域暗示以內核執行方式執行代碼,具有下列訪問:
讀/寫訪問全局內核地址空間
在進程中執行時讀/寫訪問進程區域中的內核數據
內核服務必須用來訪問進程地址空間中的用戶數據。
在該保護域中執行的程序會影響所有程序的執行環境,因為它們具有下列特征:
它們可訪問全局系統數據
它們可使用內核服務
它們免受所有
安全性約束
它們執行于處理器特權狀態下。
方式轉換
用戶方式的進程使用的系統調用允許通過用戶方式調用內核函數。直接或間接地調用系統調用來訪問的函數一般由程序設計庫提供,它們提供對操作系統函數的訪問。
方式轉換應該不同于在命令 vmstat(cs 列)和 sar(cswch/s)的輸出中所看到的上下文轉換。在當前運行的線程不同于該處理器上先前運行的線程時會出現上下文轉換。
當下列任一情況出現時調度程序執行上下文轉換:
線程必須等候某個資源(自愿),比如磁盤 I/O、網絡 I/O、睡眠或鎖
一個較高優先級線程被喚醒(非自愿)
線程已經用完了它的時間片(通常是 10 ms)。
上下文轉換的時間、系統調用、設備中斷、NFS I/O 和內核中任何其它活動都看作系統時間。
2004-8-23 19:48:39 鮮花(0) 雞蛋(0)
snappyboy
等級:
論壇游民
文章:123
積分:283
注冊:2003-10-20
第2樓
多處理介紹
無論何時,單處理器芯片的運行速度都存在著技術上的限制。如果單處理器無法令人滿意地處理系統的工作負載,一種響應是使用多處理器來解決這個問題。
這種響應是否成功不僅僅取決于系統設計者的技術熟練程度,還取決于工作負載是否服從多處理控制。就人的任務而言,如果任務是應答一個免費電話號碼的呼叫,增加人員也許不失為一個好主意,但是假如任務是開車的話,這種做法是否有效就值得懷疑了。
如果建議從一個單處理器系統遷移到一個多處理器系統的目標是為了改進性能,則下列條件必須成立:
工作負載受處理器限制并且已經使得它的單處理器系統飽和。
工作負載包含多種處理器密集的元素,例如事務或者復雜計算,這些操作可以同時并且各自獨立地執行。
現有的單處理器不能升級,也不能由另一個能量充足的單處理器代替。
雖然正常情況下不變的單線程應用程序在某個多處理器環境中能正確運行,但它們的性能常常會有意外的變化。遷移到多處理器可以改善系統的吞吐量,并能改進復雜的多線程應用程序的執行時間,但是很少能改進個別的單線程命令的響應時間。
要從一個多處理器系統獲得最佳性能,需要對多處理器系統獨有的操作系統和硬件執行動態有所了解
對稱多處理器(SMP)概念和體系結構
對于增加系統復雜性的任何變化,為了獲得令人滿意的操作和性能,使用多處理器產生了一些設計時必須引起注意的事項。額外的復雜性使得軟/硬件權衡的作用域更大,并且比在單處理器系統中更需要軟/硬件的密切配合。設計響應和權衡的不同組合使得多處理器系統的體系結構更加多樣化。
這一節描述了多處理器系統的主要設計注意事項和這些事項的硬件響應。
多處理的類型
有幾種多處理(MP)系統,如下所述:
非共享 MP(純群集)
每個處理器都是一個完全獨立的機器,運行操作系統的一個副本。處理器之間沒有共享的部分(每一個都有自己的內存,高速緩存和磁盤),但是它們是互聯的。通過 LAN 連接時,處理器之間是松散耦合的。而通過轉換器連接時,處理器之間是緊密耦合的。處理器之間的通信是通過消息傳送來實現的。
這樣一個系統的優點是它具有很好的可伸縮性和高可用性。而缺點則是該系統是一個不為人熟悉的
編程模型(消息傳送)。
共享磁盤 MP
處理器擁有自身的內存和高速緩存。處理器并行運行并共享磁盤。每個處理器都運行操作系統的一份副本,并且處理器之間是松散耦合的(通過 LAN 連接)。處理器之間的通信是通過信息傳送實現的。
共享磁盤的優點是保留了熟悉的編程模型的一部分(磁盤數據是可尋址和連續的,而內存則不是),而且與共享內存的系統相比,這種系統更容易實現高可用性。缺點是由于在對共享數據進行物理和邏輯訪問時存在瓶頸,它的可伸縮性受到限制。
共享內存群集(SMC)
一個共享內存群集中的所有處理器有自己的資源(主存儲器、磁盤和 I/0),并且每個處理器運行一份操作系統的副本。處理器之間是緊密耦合的(通過一個轉換器連接)。處理器之間的通信是通過共享內存實現的。
共享內存 MP
所有處理器通過一條高速總線或者一個轉換器在同一機器中緊密耦合。處理器共享同樣的全局內存、磁盤和 I/0 設備。只有一份操作系統的副本跨所有處理器運行,并且操作系統必須設計為能利用這種體系結構(多線程操作系統)。
SMP 有幾個優點:
它們是增加吞吐量的一種劃算的方法。
由于操作系統由所有處理器共享,它們提供了一個單獨的系統映像(易于管理)。
它們對一個單獨的問題應用多處理器(并行編程)。
負載平衡是由操作系統實現的。
這種單處理器(UP)編程模型可用于一個 SMP 中。
對于共享數據來說,它們是可伸縮的。
所有數據可由所有處理器尋址,并且由硬件監視邏輯保持連續性。
由于通信經由全局共享內存執行,在處理器之間通信不必使用消息傳送庫。
更多能量的
需求可通過向系統添加更多處理器來解決。然而,在一個 SMP 系統里添加更多處理器時,您必須設置關于性能增強的現實期望值。
現在越來越多的應用程序和工具都可以使用。大多數 UP 應用程序可以在 SMP 體系結構中運行或者被移植到 SMP 體系結構中。
SMP 系統有一些局限性,如下所述:
由于高速緩存相關性、鎖定機制、共享對象和其它問題,可伸縮性受到限制。
需要新技術來利用多處理器,例如線程編程和設備驅動程序編程。
并行化應用程序
有兩種方法可以在一個 SMP 中使應用程序并行化,如下所述:
傳統方法是把應用程序分解為多個進程。這些進程使用進程間通信(IPC)方法進行通信,例如管道、信號量或者共享內存。必須能夠阻塞進程使其等待事件的發生(例如來自其它進程的消息),并且進程必須用類似鎖的東西協調對共享對象的訪問。
另一種方法是使用面向
UNIX(POSIX)線程的可移植操作系統接口。線程和進程一樣存在協調的問題,并有類似的處理機制。因此一個單獨的進程可以同時有很多線程運行在不同的處理器上。協調這些線程并且使得對共享數據的訪問序列化是
開發者的責任。
在并行化一個應用程序的時候,考慮線程和進程兩者各自的優勢并且決定使用哪種方法。線程可能比進程快,并且它對內存的共享也比較容易。另一方面,進程的實現更容易分布到多個機器或者群集中。如果一個應用程序需要創建或者刪除新實例,則線程會更快(在派生進程中開銷更大)。就其它功能而言,線程的開銷和進程差不多。
數據序列化
任何可由多個線程讀或寫的存儲元素在程序運行中都可能改變。通常,這對多程序設計環境以及多處理環境都是成立的,但是多處理器的出現以兩種方式增加了這種注意事項的作用域和重要性。
多處理器和線程的支持使得編寫在線程中共享數據的應用程序具有吸引力和更容易。
內核再也不能通過簡單地禁用中斷來解決序列化問題。
注:
為了避免產生嚴重問題,共享數據的程序必須安排好,以對數據進行串行訪問,而不是并行訪問。在一個程序更新一個共享數據項之前,必須確保沒有其它程序(包括它本身在另一個線程里運行的另一副本)會改變該項。通常讀操作可以并行地執行。
用來避免程序互相干擾的主要機制是鎖。鎖是一種抽象概念,它代表對訪問一個或多個數據項的許可。鎖定和解鎖的請求是原子級的;也就是說,它們的實現方式為:其結果既不受中斷也不受多處理器訪問的影響。所有訪問一個共享數據項的程序在處理它之前必須先獲得與它相關的鎖。如果這個鎖已經由另一個程序(或者另一個運行同一程序的線程)占有,則請求的程序必須推遲訪問,直到鎖變得可用。
除了等待鎖所花的時間之外,序列化也增加了一個線程成為不可分派線程所花的時間。當線程不可分派時,其它線程很可能會使這個不可分派線程的高速緩存線路被替換,這將導致線程最后獲得鎖并被分派時內存等待時間成本增加。
操作系統的內核包含很多共享的數據項,所以它必須在內部進行序列化。因此序列化延遲甚至可能在一個不與其它程序共享數據的應用程序中發生,因為由該程序使用的內核服務必須序列化共享的內核數據。
鎖的類型
開放軟件基金會/1(OSF/1)1.1 的鎖定方法被作為一個 AIX 的多處理器鎖定功能模型使用。然而,由于系統是可搶占和可調頁的,對 OSF/1 1.1 鎖定模型增加了一些特征。簡單和復雜的鎖都是可搶占的。一個線程在嘗試獲得一個忙狀態的簡單鎖時也可以睡眠,如果鎖的所有者當前并不在運行的話。另外,當一個處理器在一個簡單鎖上自旋一段時間(這段時間是一個全系統的變量)以后,這個簡單鎖會變成睡眠鎖。
鎖粒度
一個在多處理器環境中工作的
程序員必須決定對共享數據一定要創建多少單獨的鎖。如果只有一個鎖來序列化整個共享數據項的集合,則相比之下很可能出現鎖爭用。廣泛使用鎖的存在給系統吞吐量加了上限。
如果每一個不同的數據項都有自己的鎖,則兩個線程爭用這個鎖的概率相對來說就比較低。然而,每一個附加的鎖定和解鎖調用都會消耗處理器時間,并且多個鎖的存在使得可能發生死鎖。最簡單的死鎖情況如下圖所示,其中線程 1 擁有鎖 A 并且正在等待鎖 B。同時,線程 2 擁有鎖 B 并且正在等待鎖 A。這兩個程序都永遠用不上會打破死鎖的 unlock() 調用。通常對死鎖的預防措施是建立一個協議,根據該協議,所有使用一個指定的鎖集合的程序必須始終按照完全相同的順序獲得它們。
根據排隊理論,一個資源閑置得越少,要得到它的平均等待時間就越長。這種關系是非線性的;如果鎖的個數翻倍,平均等待這個鎖的時間就比原來的兩倍還要多。
減少對鎖的等待時間的最有效方法是減少這個鎖所保護的范圍大小。下面是一些準則:
減少對任何鎖的請求頻率。
只鎖定訪問共享數據的代碼,而不是一個組件的所有代碼(這將減少鎖的持有時間)。
只鎖定特定的數據項或結構,而不是整個例程。
始終將鎖和特定的數據項或結構關聯起來,而不是和例程關聯。
對于大的數據結構,為結構的每一元素選擇一個鎖,而不是為整個結構選擇一個鎖。
當持有一個鎖時,從不執行同步 I/O 或者任何其它阻塞活動。
如果您對您組件中的同一數據有多個訪問,請試著把它們移到一起,以便它們可以包含在一個鎖定 — 解鎖操作中。
避免雙喚醒的情況。如果您在一個鎖下修改了一些數據,并且不得不通知某人您做了這件事,則在公布喚醒之前請釋放該鎖。
如果必須同時持有兩個鎖,則最后請求那個最忙的鎖。
另一方面,過細粒度將增加對鎖的請求和釋放的頻率,因而會增加額外的指令。您必須在過細和過粗粒度之間找到平衡。最佳粒度不得不通過試驗和錯誤找到,這也是一個 MP 系統中的最大挑戰之一。
鎖定開銷
請求鎖,等待鎖和釋放鎖在幾方面增加了處理開銷:
一個支持多處理的程序總是進行相同的鎖定和解鎖處理,即使它是在一個單處理器里運行或者是一個多處理器系統里對于這個鎖的唯一使用者。
當一個線程請求一個由另一線程持有的鎖時,發出請求的線程可能會自旋一會或者置于睡眠狀態,如果可能的話,會分派另一個線程。這會消耗處理器時間。
廣泛使用鎖的存在給系統吞吐量加了一個上限。例如,如果一個給定的程序花 20% 的執行時間來持有一個互斥鎖,這個程序最多只有五個實例能同時運行,不管系統里有多少個處理器。事實上,即使只有五個實例,它們也很可能永遠不會精確同步,以免互相等待。(參閱『多處理器吞吐量可伸縮性』)。
等待鎖
當一個線程需要另一個線程已擁有的鎖時,該線程被阻塞并且必須等到鎖變得可用為止。有兩種不同的等待方式:
對于只被持有很短時間的鎖來說,自旋鎖是很適合的。它允許等待中的線程保持其處理器重復檢查某個死循環(自旋)里的鎖定位,直到鎖變得可用。自旋導致 CPU 時間(內核或內核擴展鎖定的時間)增加。
睡眠鎖適合于可能會被持有較長時間的鎖。線程會睡眠到鎖可用為止,當鎖變得可用后,它會被放回到運行隊列里。睡眠導致更多的閑置時間。
等待總會降低系統性能。如果使用自旋鎖,處理器是繁忙的,但是它不是在做有用功(不是在為吞吐量出力)。如果使用睡眠鎖,會導致上下文切換和分派的開銷以及隨之而來的高速緩存未命中的增加。
操作系統
開發者們可以在兩種類型的鎖之間選擇:在等待鎖變得可用時允許進程自旋和睡眠的互斥簡單鎖,和在等待鎖變得可用時可以自旋和阻塞進程的復雜讀寫鎖。
一些約定管理著使用鎖的規則。不管是硬件還是軟件都沒有實施或校驗的機制。盡管使用鎖已經使得 AIX V4 是“MP
安全”的,開發者們還是有責任定義和實現一個合適的鎖定策略來保護他們自己的全局數據。
高速緩存一致性
在設計多處理器時,
工程師們對保證高速緩存的一致性給予了相當多的注意。他們取得了成功;但是高速緩存一致性是以性能為代價的。我們需要理解這個遭受攻擊的問題:
如果每個處理器都有一個反映內存不同部分狀態的高速緩存,就可能會有兩個或更多高速緩存擁有相同線路的副本。也有可能是一個給定的線路會包含不止一個可鎖定的數據項。如果兩個線程對那些數據項作了適當的序列化更改,結果可能是兩個高速緩存都以不同的,錯誤版本的內存線路而告終。換句話說,系統的狀態不再一致,因為系統包含了應該是一個特定內存區域的內容的兩個不同版本。
對高速緩存一致性問題的解決方案通常包括在線路修改之后,除了一條線路以外,使所有重復線路都失效。盡管硬件使用監視邏輯使線路失效,沒有任何軟件干預的話,任何高速緩存線路已經失效的處理器由于隨之而來的延遲,將會在下一次尋址到該線路時出現高速緩存未命中。
監視是用來解決高速緩存一致性問題的邏輯。處理器中的監視邏輯每次修改了其高速緩存中的一個字后,會在總線上廣播一條消息。監視邏輯也在總線上監視,尋找來自其它處理器的這種消息。
當一個處理器檢測到另一個處理器已經更改了存在于它本身高速緩存內的一個地址的值時,監視邏輯會使得它自己的高速緩存中的該項失效。這被稱為交叉式失效。交叉式失效提醒處理器高速緩存中的值已經無效了,處理器必須在別處(內存或其它高速緩存)尋找正確的值。由于交叉式失效增加了高速緩存未命中率,而監視協議增加了總線流量,因而解決高速緩存的一致性問題會降低所有 SMP 的性能和可伸縮性。
處理器相似性和綁定
如果一個線程中斷后又重新分派到同一個處理器中,該處理器的高速緩存也許仍含有屬于該線程的線路。如果該線程被分派到不同的處理器,它將很可能經歷一系列高速緩存未命中,直到它的高速緩存工作集從 RAM 或其它處理器的高速緩存中檢索到。另一方面,如果一個可分派的線程必須等到它先前在其中運行的處理器可用,該線程也許會經歷一個更長的延遲。
處理器相似性是指將一個線程分派到先前運行它的處理器之上的概率。對處理器相似性的強調程度應隨線程的高速緩存工作集大小直接變化,而隨自它上一次分派以來的時間長短反向變化。AIX V4 分派器強制對處理器的相似性,因此相似性是由操作系統暗中完成的。
最高程度的處理器相似性是把一個線程綁定到一個特定處理器上。綁定意味著線程將只分派到該處理器,不管其它處理器是否可用。bindprocessor 命令和 bindprocessor() 子例程將一個特定進程的線程綁定到一個特殊的處理器(參閱 bindprocessor 命令)上。顯式綁定是通過 fork() 和 exec() 系統調用繼承而來的。
綁定對于 CPU 密集的很少經歷中斷的程序是有用的。有時,它對一般的程序可能會有反作用,因為它也許會在一個 I/O 之后延遲對一個線程的重新分派,直到線程所綁定的處理器變得可用。如果線程已阻塞了一個 I/O 操作的持續時間,它的處理上下文中的大部分不太可能還保留在它所綁定的處理器的高速緩存中。如果該線程被分派到下一個可用的處理器中,它很可能會得到更好的服務。
內存和總線爭用
在一個單處理器中,一些內部資源(例如內存條和 I/O 或者內存總線)的爭用通常是組件使用時間的一小部分。在一個多處理器中,這些影響會變得更重要,特別是如果高速緩存一致性算法增加了對 RAM 的訪問數量
SMP 性能問題
為了有效使用 SMP,當您嘗試提高性能時請考慮以下問題:
工作負載并行性
SMP 系統特有的主要性能問題是工作負載的并行性,這個問題可以這樣表達:“現在我們有 n 個處理器,我們如何保持它們全都有效地工作”?如果在任何指定時間,一個四路的多處理器系統中只有一個處理器在做有用功,則它比一個單處理器好不了多少。由于用來避免處理器間干擾的額外代碼,它可能會更糟。
工作負載并行性是序列化的補充。在系統軟件或應用程序工作負載(或者是這兩者之間的交互作用)要求序列化這一點上,工作負載并行性就得遭受損失。
工作負載并行性也可以通過增加處理器相似性來更像期望的那樣下降。從處理器相似性得來的提高的高速緩存效率可能會使得程序更快的完成。工作負載并行性是降低了(除非有更多可分派的線程處于可用狀態),但是響應時間得到了改善。
工作負載并行性的一個組成部分,進程并行性,是指一個多線程進程在任何時候都擁有多個可分派線程的程度。
吞吐量
一個 SMP 系統的吞吐量主要由以下因素決定:
一直處于高級別的工作負載并行性。處理器在特定時間里擁有更多的可分派線程并不能補償一些處理器在其它時間閑置的情況。
鎖爭用的數量。
處理器相似性的程度。
響應時間
一個處于 SMP 系統中的特定程序的響應時間取決于:
該程序的進程并行性級別。如果該程序一直擁有兩個或更多可分派線程,它的響應時間很可能會在 SMP 環境里得到改善。如果程序只包含一個單獨的線程,它的響應時間最多也就是和一個處于相同速度單處理器中的程序相當。
與程序其它實例或者其它使用相同鎖的程序之間的鎖爭用的數量。
程序對處理器的相似性程度。如果程序每次都被分派到不同的處理器中,該處理器中沒有它的任何高速緩存線,則該程序可能會比在一個相當的單處理器中運行得更慢。
工作負載多處理
在快速計算機上運行繁重工作負載的多程序設計操作系統給人的感覺印象是有幾件事情在同時發生。事實上,很多費力的工作負載在任意給定時刻并沒有大量的可分派線程,即使是當它運行在一個序列化相對來說不是大問題的單處理器系統中時。除非至少總是有與處理器一樣多的可分派線程,要不然總有一個或多個處理器在一部分時間里閑置。
可分派線程的數量是系統中線程的總數
減去正在等待 I/O 的線程數,
減去正在等待共享資源的線程數,
減去正在等待另一個線程結果的線程數,
減去正對它們自己的請求睡眠的線程數。
工作負載據說是可以多處理的,從這一點來說,它不論何時都顯示出與系統中的處理器數一樣多的可分派線程數。請注意,這并不只意味著可分派線程的平均數量和處理器一樣多。如果可分派線程數在一半時間里為零,剩余時間里是處理器計數的兩倍,則可分派線程的平均數將等于處理器數,但是系統里任一給定的處理器只能在一半時間里工作。
增加工作負載的多處理性涉及到以下的一個或兩個方面:
確認并解決引起線程等待的任何瓶頸
增加系統中的線程總數
這些解決方案不是獨立的。假如有一個單獨的、主要的系統瓶頸,增加現有的通過該瓶頸的工作負載的線程數將只會僅僅增加線程等待的比例。假如目前沒有瓶頸,增加線程數可能會創建一個瓶頸。
多處理器吞吐量可伸縮性
實際工作負載并不能在 SMP 系統中極佳的伸縮。一些禁止極佳伸縮的因素如下所述:
當處理器的數量增加時,總線/開關的爭用也增加。
內存爭用增加(所有內存都為所有處理器共享)
隨著內存不斷消耗,高速緩存未命中的成本增加
高速緩存交叉式失效和讀取另一個高速緩存以保持高速緩存一致性
由于更高分派率而引起的增加的高速緩存未命中(更多的進程/線程需要在系統中分派)
增加的同步指令成本
由于更大的操作系統和應用程序數據結構而增加的高速緩存未命中
為鎖定/解鎖而增加的操作系統和應用程序路徑長度
由于等待鎖而增加的操作系統和應用程序路徑長度
所有這些因素都對稱為工作負載的可伸縮性起作用??缮炜s性是工作負載吞吐量受益于其它處理器可用性的程度。它通常表示為一個多處理器的工作負載吞吐量由一個相當的單處理器的吞吐量所除得到的商。例如,如果一個單處理器在給定的工作負載下每秒獲得 20 個請求,而一個四處理器的系統每秒獲得 58 個請求,則比例因子將是 2.9。這個工作負載是高度可伸縮的。一個專門由長期運行、計算機密集的程序組成的工作負載,如果其 I/O 或其它內核活動是可忽略的,并且沒有共享數據,則可以在一個四路系統中達到 3.2 到 3.9 的比例因子。然而,現實中大多數工作負載不能達到這個水平。由于可伸縮性是很難估計的,可伸縮性的假設應基于真實工作負載的評估值。
在多處理器上,兩個處理器處理程序執行,但是仍然只有一個鎖。為簡單起見,顯示了所有影響處理器 B 的鎖爭用。在所示的時間段里,多處理器處理 14 個命令。因此比例因子為 1.83。我們只討論兩個處理器,因為更多處理器的情況不會有什么變化?,F在鎖在 100% 的時間里都處于使用狀態。在一個四路的多處理器中,比例因子可能是 1.83 或更小。
實際程序很少會像插圖中的命令那樣對稱。另外,我們僅僅考慮了爭用的一個尺度:鎖定。如果我們把高速緩存一致性和處理器相似性的影響包括進來,無疑比例因子幾乎會更小。
該示例說明了工作負載通常不能通過簡單添加處理器來使它更快運行。確定和最小化線程之間的爭用源也是必要的。
伸縮是與工作負載相關的。一些公布的基準程序暗示高水平的可伸縮性是容易獲得的。大多數這樣的基準程序是通過運行小型的 CPU 密集程序的組合而構造出來的,這些 CPU 密集程序幾乎不用什么內核服務。這些基準程序的結果代表了可伸縮性的上限,而不是現實期望。
基準程序的另一個值得注意的有趣觀點是通常情況下,一個單路 SMP 的運行速度會比運行操作系統的 UP 版本的同等單處理器慢(大約 5%-15%)。
多處理器響應時間
一個多處理器只能把一個獨立程序的執行時間改進到讓該程序可以多線程方式運行的程度。有幾種方法可以讓一個單獨程序的某些部分實現并行執行:
顯式調用 libpthreads.a 子例程(或者,在老式程序里調用 fork() 子例程)來創建多個同時運行的線程。
用一個并行化的編譯器或者預處理器處理程序,該編譯器或預處理器會檢測到可同時執行的代碼序列,并生成多個線程來并行運行這些代碼。
使用一個本身是多線程的軟件包。
除非使用這些技術的一種或多種,程序在一個多處理器系統中不會比在一個相當的單處理器中運行得快。事實上,由于程序會經歷更多的鎖定開銷和在不同時間分派到不同處理器而產生的延遲,它有可能會更慢。
即使所有可用的技術都用到了,最大限度的改進也受到一個稱為“Amdahl 定律”規則的限制。
舉例來說,如果一個程序的 50% 的處理必須順序執行,50% 可以并行執行,則最大的響應時間改進小于因子 2(在另一個閑置的 4 路多處理器中,該值至多為 1.6)。
原文轉自:http://www.kjueaiud.com