來源:http://www.ibm.com/developerworks/cn/linux/l-affinity.html#downloadhtml
爲何(3 個緣由)以及如何使用硬(相對於軟)CPU 親和性(affinity)linux
瞭解 Linux® 2.6 調度器如何處理 CPU 親和性(affinity)能夠幫助您更好地設計用戶空間的應用程序。軟親和性(affinity) 意味着進程並不會在處理器之間頻繁遷移,而 硬親和性(affinity) 則意味着進程須要在您指定的處理器上運行。本文介紹了當前的親和性(affinity)機制,解釋爲何和如何使用親和性(affinity),並給出了幾個樣例代碼來顯示如何使用這種功能。算法
簡單地說,CPU 親和性(affinity) 就是進程要在某個給定的 CPU 上儘可能長時間地運行而不被遷移到其餘處理器的傾向性。Linux 內核進程調度器天生就具備被稱爲 軟 CPU 親和性(affinity) 的特性,這意味着進程一般不會在處理器之間頻繁遷移。這種狀態正是咱們但願的,由於進程遷移的頻率小就意味着產生的負載小。編程
2.6 版本的 Linux 內核還包含了一種機制,它讓開發人員能夠編程實現 硬 CPU 親和性(affinity)。這意味着應用程序能夠顯式地指定進程在哪一個(或哪些)處理器上運行。緩存
在 Linux 內核中,全部的進程都有一個相關的數據結構,稱爲 task_struct
。這個結構很是重要,緣由有不少;其中與 親和性(affinity)相關度最高的是 cpus_allowed
位掩碼。這個位掩碼由 n 位組成,與系統中的 n 個邏輯處理器一一對應。 具備 4 個物理 CPU 的系統能夠有 4 位。若是這些 CPU 都啓用了超線程,那麼這個系統就有一個 8 位的位掩碼。數據結構
若是爲給定的進程設置了給定的位,那麼這個進程就能夠在相關的 CPU 上運行。所以,若是一個進程能夠在任何 CPU 上運行,而且可以根據須要在處理器之間進行遷移,那麼位掩碼就全是 1。實際上,這就是 Linux 中進程的缺省狀態。多線程
Linux 內核 API 提供了一些方法,讓用戶能夠修改位掩碼或查看當前的位掩碼:分佈式
sched_set_affinity()
(用來修改位掩碼)sched_get_affinity()
(用來查看當前的位掩碼)注意,cpu_affinity
會被傳遞給子線程,所以應該適當地調用 sched_set_affinity
。ide
回頁首函數
一般 Linux 內核均可以很好地對進程進行調度,在應該運行的地方運行進程(這就是說,在可用的處理器上運行並得到很好的總體性能)。內核包含了一些用來檢測 CPU 之間任務負載遷移的算法,能夠啓用進程遷移來下降繁忙的處理器的壓力。
通常狀況下,在應用程序中只需使用缺省的調度器行爲。然而,您可能會但願修改這些缺省行爲以實現性能的優化。讓咱們來看一下使用硬親和性(affinity) 的 3 個緣由。
基於大量計算的情形一般出如今科學和理論計算中,可是通用領域的計算也可能出現這種狀況。一個常見的標誌是您發現本身的應用程序要在多處理器的機器上花費大量的計算時間。
測試複雜軟件是咱們對內核的親和性(affinity)技術感興趣的另一個緣由。考慮一個須要進行線性可伸縮性測試的應用程序。有些產品聲明能夠在 使用更多硬件 時執行得更好。
咱們不用購買多臺機器(爲每種處理器配置都購買一臺機器),而是能夠:
若是應用程序隨着 CPU 的增長能夠線性地伸縮,那麼每秒事務數和 CPU 個數之間應該會是線性的關係(例如斜線圖 —— 請參閱下一節的內容)。這樣建模能夠肯定應用程序是否能夠有效地使用底層硬件。
Amdahl 法則說明這種加速比在現實中可能並不會發生,可是能夠很是接近於該值。對於一般狀況來講,咱們能夠推論出每一個程序都有一些串行的組件。隨着問題集不斷變大,串行組件最終會在優化解決方案時間方面達到一個上限。
Amdahl 法則在但願保持高 CPU 緩存命中率時尤爲重要。若是一個給定的進程遷移到其餘地方去了,那麼它就失去了利用 CPU 緩存的優點。實際上,若是正在使用的 CPU 須要爲本身緩存一些特殊的數據,那麼全部其餘 CPU 都會使這些數據在本身的緩存中失效。
所以,若是有多個線程都須要相同的數據,那麼將這些線程綁定到一個特定的 CPU 上是很是有意義的,這樣就確保它們能夠訪問相同的緩存數據(或者至少能夠提升緩存的命中率)。不然,這些線程可能會在不一樣的 CPU 上執行,這樣會頻繁地使其餘緩存項失效。
咱們對 CPU 親和性(affinity)感興趣的最後一個緣由是實時(對時間敏感的)進程。例如,您可能會但願使用硬親和性(affinity)來指定一個 8 路主機上的某個處理器,而同時容許其餘 7 個處理器處理全部普通的系統調度。這種作法確保長時間運行、對時間敏感的應用程序能夠獲得運行,同時能夠容許其餘應用程序獨佔其他的計算資源。
下面的樣例應用程序顯示了這是如何工做的。
如今讓咱們來設計一個程序,它可讓 Linux 系統很是繁忙。可使用前面介紹的系統調用和另一些用來講明系統中有多少處理器的 API 來構建這個應用程序。實際上,咱們的目標是編寫這樣一個程序:它可讓系統中的每一個處理器都繁忙幾秒鐘。能夠從後面的「下載」一節中 下載樣例程序。
/* This method will create threads, then bind each to its own cpu. */ bool do_cpu_stress(int numthreads) { int ret = TRUE; int created_thread = 0; /* We need a thread for each cpu we have... */ while ( created_thread < numthreads - 1 ) { int mypid = fork(); if (mypid == 0) /* Child process */ { printf("\tCreating Child Thread: #%i\n", created_thread); break; } else /* Only parent executes this */ { /* Continue looping until we spawned enough threads! */ ; created_thread++; } } /* NOTE: All threads execute code from here down! */
正如您能夠看到的同樣,這段代碼只是經過 fork 調用簡單地建立一組線程。每一個線程都執行這個方法中後面的代碼。如今咱們讓每一個線程都將親和性(affinity)設置爲本身的 CPU。
cpu_set_t mask; /* CPU_ZERO initializes all the bits in the mask to zero. */ CPU_ZERO( &mask ); /* CPU_SET sets only the bit corresponding to cpu. */ CPU_SET( created_thread, &mask ); /* sched_setaffinity returns 0 in success */ if( sched_setaffinity( 0, sizeof(mask), &mask ) == -1 ) { printf("WARNING: Could not set CPU Affinity, continuing...\n"); }
若是程序能夠執行到這兒,那麼咱們的線程就已經設置了本身的親和性(affinity)。調用 sched_setaffinity
會設置由 pid
所引用的進程的 CPU 親和性(affinity)掩碼。若是 pid
爲 0,那麼就使用當前進程。
親和性(affinity)掩碼是使用在 mask
中存儲的位掩碼來表示的。最低位對應於系統中的第一個邏輯處理器,而最高位則對應於系統中最後一個邏輯處理器。
每一個設置的位都對應一個能夠合法調度的 CPU,而未設置的位則對應一個不可調度的 CPU。換而言之,進程都被綁定了,只能在那些對應位被設置了的處理器上運行。一般,掩碼中的全部位都被置位了。這些線程的親和性(affinity)都會傳遞給從它們派生的子進程中。
注意不該該直接修改位掩碼。應該使用下面的宏。雖然在咱們的例子中並無所有使用這些宏,可是在本文中仍是詳細列出了這些宏,您在本身的程序中可能須要這些宏。
void CPU_ZERO (cpu_set_t *set) 這個宏對 CPU 集 set 進行初始化,將其設置爲空集。 void CPU_SET (int cpu, cpu_set_t *set) 這個宏將 cpu 加入 CPU 集 set 中。 void CPU_CLR (int cpu, cpu_set_t *set) 這個宏將 cpu 從 CPU 集 set 中刪除。 int CPU_ISSET (int cpu, const cpu_set_t *set) 若是 cpu 是 CPU 集 set 的一員,這個宏就返回一個非零值(true),不然就返回零(false)。
對於本文來講,樣例代碼會繼續讓每一個線程都執行某些計算量較大的操做。
/* Now we have a single thread bound to each cpu on the system */ int computation_res = do_cpu_expensive_op(41); cpu_set_t mycpuid; sched_getaffinity(0, sizeof(mycpuid), &mycpuid); if ( check_cpu_expensive_op(computation_res) ) { printf("SUCCESS: Thread completed, and PASSED integrity check!\n", mycpuid); ret = TRUE; } else { printf("FAILURE: Thread failed integrity check!\n", mycpuid); ret = FALSE; } return ret; }
如今您已經瞭解了在 Linux 2.6 版本的內核中設置 CPU 親和性(affinity)的基本知識。接下來,咱們使用一個 main
程序來封裝這些方法,它使用一個用戶指定的參數來講明要讓多少個 CPU 繁忙。咱們可使用另一個方法來肯定系統中有多少個處理器:
int NUM_PROCS = sysconf(_SC_NPROCESSORS_CONF);
這個方法讓程序可以本身肯定要讓多少個處理器保持繁忙,例如缺省讓全部的處理器都處於繁忙狀態,並容許用戶指定系統中實際處理器範圍的一個子集。
當運行前面介紹的 樣例程序 時,可使用不少工具來查看 CPU 是不是繁忙的。若是隻是簡單地進行測試,可使用 Linux 命令 top
。在運行top
命令時按下 「1」 鍵,能夠看到每一個 CPU 執行進程所佔用的百分比。
這個樣例程序雖然很是簡單,可是它卻展現了使用 Linux 內核中實現的硬親和性(affinity)的基本知識。(任何使用這段代碼的應用程序都無疑會作一些更有意義的事情。)瞭解了 CPU 親和性(affinity)內核 API 的基本知識,您就能夠從複雜的應用程序中榨取出最後一點兒性能了。
來源:http://blog.csdn.net/yfkiss/article/details/7464968
在Linux中,咱們知道能夠經過nice、renice命令改變進程的執行優先級,優先級高的進程優先執行,從而必定程度上保證重要任務的運行。
除了nice、renice外,能夠經過CPU affinity指定進程在哪些處理器上運行。CPU affinity表示進程要在某個給定的 CPU 上儘可能長時間地運行而不被遷移到其餘處理器的傾向性。
2.6 版本的Linux內核,實現了CPU affinity的接口,
須要說明的說:應用程序顯示指定了CPU affinity的話,表示應用程序只會在指定的處理器上運行,就算其餘處理器空閒,也不會切換到別的處理器上運行。
如下兩種狀況有可能須要使用自定義進程CPU affinity:
1. 保證高優先級任務
若有存在一些優先級高的任務,能夠將其餘任務都分配到指定的處理器執行,這個高優先級任務設定CPU affinity到其餘處理器,避免非重要任務對高優先級任務的干擾。
一個比較好的case是,在分佈式計算系統中,在凌晨經常會進行一些數據/索引merge或者導數據的工做,爲了不對服務進程的影響,能夠將這些任務都放到一個處理器上執行
2. 測試複雜程序
對於號稱scale out的程序,在資源受限的狀況下進行測試,能夠經過定義進程的cpu affinity,逐步測試在提供不一樣數量處理器的狀況下,程序的處理能力!
linux下能夠經過taskset(需安裝schedutils)顯示指定進程的cpu affinity
taskset功能以下:
$ taskset -help
Usage: taskset [options] [mask | cpu-list] [pid|cmd [args...]]
Options:
-a, --all-tasks operate on all the tasks (threads) for a given pid
-p, --pid operate on existing given pid
-c, --cpu-list display and specify cpus in list format
......
來源:http://blog.csdn.net/xsckernel/article/details/8543359 有進程源碼和線程源碼
一.CPU affinity
linux中將某個進程或者線程綁定到特定的一個或者多個cpu上執行。
二.爲何須要CPU affinity
cache性能的提高,多個進程不斷交替地在某個cpu上執行,致使緩存無效。
多線程程序運行於某個cpu,每一個線程輪流佔據cpu資源,共享cache,致使cache性能降低。
專有程序的實時性,當把專有進程保定與一個核,其他全部進程綁定與剩餘的核,保證了專有程序的性能。
三.Linux cpu affinity 調用函數a.進程cpu affinity設置int sched_setaffinity(pid_t pid, size_t cpusetsize, cpu_set_t *mask);int sched_getaffinity(pid_t pid, size_t cpusetsize, cpu_set_t *mask);第一個函數設置進程號爲pid的cpu親和性第二個函數獲取進程號爲pid的cpu親和性 b.線程cpu affinity設置int pthread_setaffinity_np(pthread_t thread, size_t cpusetsize, onst cpu_set_t *cpuset); int pthread_getaffinity_np(pthread_t thread, size_t cpusetsize, cpu_set_t *cpuset);第一個函數設置線程號爲thread的cpu 親和性第二個函數獲取線程號爲thread的cpu 親和性