最近負責的svr壓力比較大,商務邏輯有點複雜,能最佳化的地方已經全部最佳化了,
目前每秒3k次,cpu負載還是比較高
top看一下,4核的cpu負載不是太均衡,打算考慮一下將業務進程指定到3個cpu上運行,另外一個cpu專門負責處理網路收發包;打算嘗試一下,如果還是不行,再過段時間,訪問量再增加的話,就要加機器了,嗚嗚
補充:今天測試了一下,效果挺好,同樣進程數的情況下,進行cpu綁定
每個cpu都利用起來了,負載也比不綁定的情況下好了很多
分析一下有效果的原因:
看了《linux核心設計與實現》的42節,覺得人為控制一下cpu的綁定還是有用處的
1、linux的SMP負載平衡是基於進程數的,每個cpu都有一個可執行進程隊列,只有當其中一個cpu的可執行隊列裡進程數比其他cpu隊列進程數多25%時,才會將進程移動到另外空閑cpu上,也就是說cpu0上的進程數應該是比其他cpu上多,但是會在25%以內
2、我們的業務中耗費cpu的分四種類型,(1)網卡中斷(2)1個處理網路收發包進程(3)耗費cpu的n個worker進程(4)其他不太耗費cpu的進程
基於1中的 負載平衡是針對進程數,那麼(1)(2)大部分時間會出現在cpu0上,(3)的n個進程會隨著調度,平均到其他多個cpu上,(4)裡的進程也是隨著調度分配到各個cpu上;
當發生網卡中斷的時候,cpu被打斷了,處理網卡中斷,那麼分配到cpu0上的worker進程肯定是運行不了的
其他cpu上不是太耗費cpu的進程獲得cpu時,就算它的時間片很短,它也是要執行的,那麼這個時候,你的worker進程還是被影響到了;按照調度邏輯,一種非常惡劣的情況是:(1)(2)(3)的進程全部分配到cpu0上,其他不太耗費cpu的進程數很多,全部分配到cpu1,cpu2,cpu3上。。那麼網卡中斷髮生的時候,你的業務進程就得不到cpu了
如果從業務的角度來說,worker進程運行越多,肯定業務處理越快,人為的將它捆綁到其他負載低的cpu上,肯定能提高worker進程使用cpu的時間
找了個例子:
現在多CPU的趨勢越來越大了. 有時候為了更好地操作機器, 需要將某個進程綁定到具體的CPU上去. 下面給出了一個進程綁定到具體的CPU上去的一個例子.
#include<stdlib.h> #include<stdio.h> #include<sys/types.h> #include<sys/sysinfo.h> #include<unistd.h> #define __USE_GNU #include<sched.h> #include<ctype.h> #include<string.h> int main(int argc, char* argv[]) { int num = sysconf(_SC_NPROCESSORS_CONF); int created_thread = 0; int myid; int i; int j = 0; cpu_set_t mask; cpu_set_t get; if (argc != 2) { printf("usage : ./cpu num/n"); exit(1); } myid = atoi(argv[1]); printf("system has %i processor(s). /n", num); CPU_ZERO(&mask); CPU_SET(myid, &mask); if (sched_setaffinity(0, sizeof(mask), &mask) == -1) { printf("warning: could not set CPU affinity, continuing.../n"); } while (1) { CPU_ZERO(&get); if (sched_getaffinity(0, sizeof(get), &get) == -1) { printf("warning: cound not get cpu affinity, continuing.../n"); } for (i = 0; i < num; i++) { if (CPU_ISSET(i, &get)) { printf("this process %d is running processor : %d/n",getpid(), i); } } } return 0; }
下面是在兩個終端分別執行了./cpu 0 ./cpu 2 後得到的結果. 效果比較明顯. QUOTE: Cpu0 : 5.3%us, 5.3%sy, 0.0%ni, 87.4%id, 0.0%wa, 0.0%hi, 2.0%si, 0.0%st
Cpu1 : 0.0%us, 0.0%sy, 0.0%ni,100.0%id, 0.0%wa, 0.0%hi, 0.0%si, 0.0%st
Cpu2 : 5.0%us, 12.2%sy, 0.0%ni, 82.8%id, 0.0%wa, 0.0%hi, 0.0%si, 0.0%st
Cpu3 : 0.0%us, 0.0%sy, 0.0%ni,100.0%id, 0.0%wa, 0.0%hi, 0.0%si, 0.0%st
Cpu4 : 0.0%us, 0.0%sy, 0.0%ni, 99.7%id, 0.3%wa, 0.0%hi, 0.0%si, 0.0%st
Cpu5 : 0.0%us, 0.0%sy, 0.0%ni,100.0%id, 0.0%wa, 0.0%hi, 0.0%si, 0.0%st
Cpu6 : 0.0%us, 0.0%sy, 0.0%ni,100.0%id, 0.0%wa, 0.0%hi, 0.0%si, 0.0%st
Cpu7 : 0.0%us, 0.0%sy, 0.0%ni,100.0%id, 0.0%wa, 0.0%hi, 0.0%si, 0.0%st /////////////////////////////////////////////// CPU Affinity (CPU親合力)
CPU親合力就是指在Linux系統中能夠將一個或多個進程綁定到一個或多個處理器上運行.
一個進程的CPU親合力掩碼決定了該進程將在哪個或哪幾個CPU上運行.在一個多處理器系統中,設定CPU親合力的掩碼可能會獲得更好的效能.
一個CPU的親合力掩碼用一個cpu_set_t結構體來表示一個CPU集合,下面的幾個宏分別對這個掩碼集進行操作:
CPU_ZERO() 清空一個集合
CPU_SET()與CPU_CLR()分別對將一個給定的CPU號加到一個集合或者從一個集合中去掉.
CPU_ISSET()檢查一個CPU號是否在這個集合中.
其實這幾個的用法與select()函數那幾個調用差不多.
下面兩個函數就是最主要的了:
sched_setaffinity(pid_t pid, unsigned int cpusetsize, cpu_set_t *mask)
該函數設定進程為pid的這個進程,讓它運行在mask所設定的CPU上.如果pid的值為0,則表示指定的是當前進程,使當前進程運行在mask所設定的那些CPU上.第二個參數cpusetsize是
mask所指定的數的長度.通常設定為sizeof(cpu_set_t).如果當前pid所指定的CPU此時沒有運行在mask所指定的任意一個CPU上,則該指定的進程會從其它CPU上遷移到mask的指定的
一個CPU上運行.
sched_getaffinity(pid_t pid, unsigned int cpusetsize, cpu_set_t *mask)
該函數獲得pid所指示的進程的CPU位元遮罩,並將該掩碼返回到mask所指向的結構中.即獲得指定pid當前可以運行在哪些CPU上.同樣,如果pid的值為0.也表示的是當前進程.
這幾個宏與函數的具體用法前面已經有講解.
關於cpu_set_t的定義
# define __CPU_SETSIZE 1024
# define __NCPUBITS (8 * sizeof (__cpu_mask))
typedef unsigned long int __cpu_mask;
# define __CPUELT(cpu) ((cpu) / __NCPUBITS)
# define __CPUMASK(cpu) ((__cpu_mask) 1 << ((cpu) % __NCPUBITS))
typedef struct
{
__cpu_mask __bits[__CPU_SETSIZE / __NCPUBITS];
} cpu_set_t;
# define __CPU_ZERO(cpusetp) /
do { /
unsigned int __i; /
cpu_set_t *__arr = (cpusetp); /
for (__i = 0; __i < sizeof (cpu_set_t) / sizeof (__cpu_mask); ++__i) /
__arr->__bits[__i] = 0; /
} while (0)
# define __CPU_SET(cpu, cpusetp) /
((cpusetp)->__bits[__CPUELT (cpu)] |= __CPUMASK (cpu))
# define __CPU_CLR(cpu, cpusetp) /
((cpusetp)->__bits[__CPUELT (cpu)] &= ~__CPUMASK (cpu))
# define __CPU_ISSET(cpu, cpusetp) /
(((cpusetp)->__bits[__CPUELT (cpu)] & __CPUMASK (cpu)) != 0)
在我的機器上sizeof(cpu_set_t)的大小為128,即一共有1024位.第一位代表一個CPU號.某一位為1則表示某進程可以運行在該位所代表的cpu上.例如
CPU_SET(1, &mask);
則mask所對應的第2位被設定為1.
此時如果printf("%d/n", mask.__bits[0]);就列印出2.表示第2位被置為1了.
具體我是參考man sched_setaffinity文檔中的函數的.
然後再參考了一下IBM的 developerWorks上的一個講解.
http://www.ibm.com/developerworks/cn/linux/l-affinity.html