精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

Linux高性能網(wǎng)絡(luò)編程十談 | 性能優(yōu)化(CPU和內(nèi)存)

系統(tǒng) Linux
上一篇文章講了高性能編程的工具,這一篇我們基于前面的一些知識點和工具來聊一下Linux下的性能優(yōu)化(本知識點分為兩篇,當(dāng)前主要介紹CPU和內(nèi)存性能優(yōu)化)。

上一篇文章講了高性能編程的工具,這一篇我們基于前面的一些知識點和工具來聊一下Linux下的性能優(yōu)化(本知識點分為兩篇,當(dāng)前主要介紹CPU和內(nèi)存性能優(yōu)化)。

第一部分:CPU和內(nèi)存性能度量

系統(tǒng)調(diào)用系統(tǒng)調(diào)用

這張圖闡述一個應(yīng)用程序需要經(jīng)過這些模塊調(diào)用,對于性能每一部分都可能會有影響,那么我們先需要了解每個模塊需要怎么度量?

1、CPU度量

(1)CPU使用率

CPU使用率是最直觀描述當(dāng)前服務(wù)狀態(tài)的情況,如果CPU使用率過高,則表示當(dāng)前遇到了性能瓶頸,其中過高的這個具體值在線上一般是70%-90%之間,要么擴容服務(wù),要么就排查性能問題。

查看性能工具有很多,最常用的是通過top -p <進程ID>或者通過查看線程top -H -p <進程ID>觀察,另外可以使用上一篇的工具:mpstat -P ALL 1 2。

(2)用戶進程消耗CPU

用戶進程消耗CPU是常見的情況,往往和業(yè)務(wù)代碼或者使用的庫相關(guān),比如大量的循環(huán),JSON解析大包等,在用戶代碼層有很多耗CPU的操作,都會表現(xiàn)CPU使用率異常,定位其問題可以通過以下方式:

  • 先通過ps或者top查詢具體進程或者線程CPU消耗過高,然后查詢pidstat -p <進程ID>判斷%usr %system %guest占比情況,判斷是否為用戶態(tài)消耗
  • 由于用戶態(tài)涉及用戶代碼,可以通過perf top查看具體調(diào)用函數(shù)或者查看查看日志分析;

(3)內(nèi)核消耗CPU

消耗CPU不止用戶進程,還包括內(nèi)核進程,系統(tǒng)調(diào)用等內(nèi)核消耗CPU,可能的原因有大量的內(nèi)存拷貝,鎖,大量的上下文切換等等,具體分析和上面類似:

  • 先通過ps或者top查詢具體進程或者線程CPU消耗過高,然后查詢pidstat -p <進程ID>判斷%usr %system %guest占比情況,判斷是否為內(nèi)核態(tài)消耗;
  • 然后可以通過perf top或者strace查看系統(tǒng)調(diào)用情況,或者通過mpstat分析,總結(jié)中斷或者上下文切換頻率來判斷;

(4)CPU等待

CPU花費在等待上的時間,主要是看是否大量的IO導(dǎo)致,也可以通過top定位具體進程,然后跟蹤和分析該進程或者線程的網(wǎng)絡(luò)調(diào)用情況。

(5)Nice消耗CPU

描述的是花費的re-nicing進程上時間占比,主要是更改了進程的執(zhí)行順序或者優(yōu)先級。

(6)平均負(fù)載

平均負(fù)載是一個判斷系統(tǒng)快慢的重要原因,可能往往不是某個進程引起的,主要有兩個指標(biāo):

  • 隊列中等待處理的進程數(shù)(TASK_RUNNING狀態(tài)進程)
  • 等待不可中斷任務(wù)被完成的進程數(shù)(TASK_UNINTERRUPTIBLE狀態(tài)進程)

如果被阻塞,平均負(fù)載就會增加,可以通過uptime查看,往往負(fù)載增加這個時候需要優(yōu)化代碼或者增加機器資源。

(7)運行進程

當(dāng)前運行和已經(jīng)在隊列中的進程數(shù),往往進程過多會導(dǎo)致CPU調(diào)度繁忙,比如之前多進程的Apache Server,所以可以根據(jù)當(dāng)前CPU的核數(shù)決定進程個數(shù),一般繁忙情況下的進程不建議超過2倍CPU(當(dāng)前空閑的進程也不宜過大,建議不超過10倍)。

(8)阻塞進程

阻塞進程是當(dāng)前未達(dá)到執(zhí)行條件的進程,和上面的CPU等待事件對應(yīng),一般是IO問題導(dǎo)致,比如寫文件數(shù)據(jù)過慢,或者socket讀寫數(shù)據(jù)未到達(dá)等等情況,如何分析呢?可以通過strace跟蹤系統(tǒng)調(diào)用分析。

(9)上下文切換

在系統(tǒng)上發(fā)生上下文切換的情況,也是判斷CPU負(fù)載的重要因素,大量的上下文切換可能和大量中斷或者鎖相關(guān),上下文切換會導(dǎo)致CPU的緩存被刷新,數(shù)據(jù)需要從內(nèi)存換入換出等。

排查方案是通過perf或者vmstat工具查詢,比如vmstat輸出(也可以通過vmstat -s查看):

[root@VM-16-16-centos ~]# vmstat 2 2
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
 0  0      0 298404  96824 1189732    0    0     1    34    1    0  0  0 99  0  0
 0  0      0 298284  96824 1189736    0    0     0   214  760 1315  1  0 99  1  0

其中system包括:CPU在內(nèi)核態(tài)運行信息,包括in中斷次數(shù),cs上下文切換次數(shù)。

(10)中斷

中斷包含硬中斷和軟中斷,硬中斷是外設(shè)處理過程中產(chǎn)生的,通過硬件控制器通知cpu的狀態(tài)變化,而軟中斷是通過模擬硬中斷的一種信號處理方式,中斷過多會導(dǎo)致CPU花費一些時間相應(yīng)中斷,這里也會影響性能,如何排查?通過命令行mpstat -P ALL 5 2可以查看:

[root@VM-16-16-centos ~]# mpstat -P ALL 5 2
Linux 4.18.0-348.7.1.el8_5.x86_64 (VM-16-16-centos)  2023年08月19日  _x86_64_ (2 CPU)

10時02分15秒  CPU    %usr   %nice    %sys %iowait    %irq   %soft  %steal  %guest  %gnice   %idle
10時02分20秒  all    0.70    0.00    0.80    0.50    0.00    0.00    0.00    0.00    0.00   98.00
10時02分20秒    0    0.60    0.00    0.80    0.20    0.00    0.00    0.00    0.00    0.00   98.40
10時02分20秒    1    0.80    0.00    0.80    0.80    0.00    0.00    0.00    0.00    0.00   97.60

其中輸出中包含的:

  • %irq:CPU處理硬中斷的時間占比
  • %soft:CPU處理軟中斷的時間占比

2、內(nèi)存度量

(1)空閑內(nèi)存

通過free我們能看到當(dāng)前內(nèi)存情況:

[root@VM-0-11-centos ~]# free
              total        used        free      shared  buff/cache   available
Mem:        3880192      407228      713024         872     2759940     3182872
Swap:             0           0           0
  • total:物理內(nèi)存總量
  • used:已經(jīng)使用的物理內(nèi)存量
  • free:尚未使用的物理內(nèi)存量
  • shared:被共享使用的物理內(nèi)存量
  • buff:被緩存的物理內(nèi)存量
  • cache:被緩存的硬盤文件的物理內(nèi)存量
  • available:剩余可用的物理內(nèi)存量,包括free + buff + cache - 系統(tǒng)預(yù)留的緩沖區(qū)
  • Swap total:交換空間總量
  • Swap used:已經(jīng)使用的交換空間量
  • Swap free:尚未使用的交換空間量

從上面可以看出,free的內(nèi)存越大越好,這樣有剩余足夠多的物理內(nèi)存可以使用。

(2)Swap

Swap如上面說的是交換空間的內(nèi)存數(shù)據(jù),是linux為了釋放一部分物理內(nèi)存將數(shù)據(jù)臨時保存在Swap空間中,通過vmstat -s查看具體信息如下:

[root@VM-16-16-centos ~]# vmstat -s
      1860492 K total memory
       274936 K used memory
       701576 K active memory
       707432 K inactive memory
       299040 K free memory
        96824 K buffer memory
      1189692 K swap cache
            0 K total swap
            0 K used swap
            0 K free swap
     12318019 non-nice user cpu ticks
       124590 nice user cpu ticks
     11848347 system cpu ticks
   2844992141 idle cpu ticks
      4677889 IO-wait cpu ticks
            0 IRQ cpu ticks
       208152 softirq cpu ticks
            0 stolen cpu ticks
     15879112 pages paged in
    985253486 pages paged out
            0 pages swapped in
            0 pages swapped out
   1330511648 interrupts
    260667271 CPU context switches
   1678004734 boot time
     58996940 forks

其中如果pages swapped in和pages swapped out每秒增長很多大,表示內(nèi)存上遇到了瓶頸,需要升級機器的內(nèi)存或者優(yōu)化代碼。

(3)Slab

在Linux中,伙伴系統(tǒng)是以頁為單位管理和分配內(nèi)存,但是現(xiàn)實的需求卻以字節(jié)為單位,假如我們需要申請20Bytes,總不能分配一頁吧?那豈不是嚴(yán)重浪費內(nèi)存。那么該如何分配呢?Slab分配器就應(yīng)運而生了,專為小內(nèi)存分配而生,Slab分配器分配內(nèi)存以Byte為單位,但是Slab分配器并沒有脫離伙伴系統(tǒng),而是基于伙伴系統(tǒng)分配的大內(nèi)存進一步細(xì)分成小內(nèi)存分配,其作用如下:

  • 節(jié)省空間,減少內(nèi)存碎片化,Slab對小對象進行分配,不用為每個小對象分配一頁
  • 提高系統(tǒng)效率:當(dāng)對象擁有者釋放一個對象后,SLAB的處理是僅僅標(biāo)記對象為空閑,并不做多少處理,而又有申請者申請相應(yīng)大小的對象時,Slab會優(yōu)先分配最近釋放的對象

如果要排查Slab的詳細(xì)信息,可以通過slabtop或者cat /proc/slabinfo,輸出如下(執(zhí)行slabtop):

Active / Total Objects (% used)    : 1074142 / 1101790 (97.5%)
 Active / Total Slabs (% used)      : 39843 / 39843 (100.0%)
 Active / Total Caches (% used)     : 100 / 130 (76.9%)
 Active / Total Size (% used)       : 250498.05K / 253182.16K (98.9%)
 Minimum / Average / Maximum Object : 0.01K / 0.23K / 8.00K

  OBJS ACTIVE  USE OBJ SIZE  SLABS OBJ/SLAB CACHE SIZE NAME
445302 445302 100%    0.10K  11418  39     45672K buffer_head
249102 249071  99%    0.19K  11862  21     47448K dentry
 83616  83557  99%    1.00K   5226  16     83616K ext4_inode_cache
 63240  40754  64%    0.04K    620 102  2480K ext4_extent_status
 54376  54297  99%    0.57K   3884  14     31072K radix_tree_node
 29547  29487  99%    0.19K   1407  21  5628K kmalloc-192
 28544  28488  99%    0.06K    446  64  1784K kmalloc-64
 21624  21624 100%    0.12K    636  34  2544K kernfs_node_cache
 20400  20400 100%    0.05K    240  85   960K shared_policy_node
 16276  15989  98%    0.58K   1252  13     10016K inode_cache
 10914  10914 100%    0.04K    107 102   428K selinux_inode_security
  7776   7776 100%    0.21K    432  18  1728K vm_area_struct
  7232   3921  54%    0.12K    226  32   904K kmalloc-128
  5376   5376 100%    0.02K     21 256        84K kmalloc-16
  5376   5376 100%    0.03K     42 128   168K kmalloc-32
  5120   5120 100%    0.01K     10 512        40K kmalloc-8
  4344   4306  99%    0.66K    362  12  2896K proc_inode_cache
  4096   4096 100%    0.03K     32 128   128K jbd2_revoke_record_s
  3822   3822 100%    0.09K     91  42   364K kmalloc-96
  3417   3217  94%    0.08K     67  51   268K anon_vma
  3344   3344 100%    0.25K    209  16   836K kmalloc-256
  3136   3136 100%    0.06K     49  64   196K ext4_free_data
  2190   2190 100%    0.05K     30  73   120K avc_xperms_node
  2112   2112 100%    1.00K    132  16  2112K kmalloc-1024
  • OBJS:由于Slab是按照object管理的,這里是對象數(shù)量
  • ACTIVE:當(dāng)前活躍的objects數(shù)量
  • USE:緩存的利用率
  • OBJ SIZE:object的size的大小
  • SLABS:Slab的個數(shù)
  • OBJ/SLAB:每個Slab中object個數(shù)
  • CACHE SIZE:緩存大小,這里是不精確值,可以忽略
  • NAME:分配Slab的名字

我們可以從以上的信息中判斷那些內(nèi)核模塊內(nèi)存分配較多(比如OBJ SIZE過大),進而分析模塊的性能瓶頸。

3、方法論

以下是我參照USE方法論整理排查性能度量指標(biāo)流程,其中最大挑戰(zhàn)點在于如何發(fā)現(xiàn)子模塊中的問題并且分析問題?后續(xù)可以單獨寫一篇分析。

方法論方法論

第二部分:系統(tǒng)層優(yōu)化

1、CPU

(1)緩存

#define N 2048

long timecost(clock_t t1, clock_t t2)
{
 long elapsed = ((double)t2 - t1) / CLOCKS_PER_SEC * 1000;
 return elapsed;
}

int main(int argc, char **argv)
{
 char arr[N][N];

 {
  clock_t start, end;
  start = clock();
  for (int i = 0; i < N; i++)
  {
   for (int j = 0; j < N; j++)
   {
    arr[i][j] = 0;
   }
  }
  end = clock();
  cout << "timecost: " << timecost(start, end) << endl;
 }
 {
  clock_t start, end;
  start = clock();
  for (int i = 0; i < N; i++)
  {
   for (int j = 0; j < N; j++)
   {
    arr[j][i] = 0;
   }
  }
  end = clock();
  cout << "timecost: " << timecost(start, end) << endl;
 }
}

先來看一下上面一段代碼,有兩個timecost輸出,大家覺得哪個性能更高呢?運行輸出:

timecost: 11
timecost: 67

可見第一段代碼性能比第二段代碼性能高6倍,之前了解過CPU緩存的應(yīng)該都知道其中的原理!先看看這張圖:

性能性能

CPU分位多級緩存,每一級比上一級耗時都差幾倍,所以如果寫的代碼讀取數(shù)據(jù)能命令更高級緩存,那么性能自然就會提高,我們再看代碼訪問array[i][j]和array[j][i ]的差異,array[i][j]是順序訪問,CPU讀取數(shù)據(jù)時,后面的元素已經(jīng)載入緩存中了,而array[j][i]是間隔訪問,可能每次都不能命中緩存,既然明白了緩存的作用,那如何判斷我們代碼是否由于緩存未命中而損失性能呢?使用工具perf,執(zhí)行 perf stat -e cache-references -e cache-misses ./a.out,輸出如下:

[root@VM-0-11-centos ~]# perf stat -e cache-references -e cache-misses ./a.out
// 第一段代碼
Performance counter stats for './a.out':

   6,115,254      cache-references
      13,450      cache-misses

// 第二段代碼
Performance counter stats for './a.out':

     913,732      cache-references
      17,954      cache-misses

因此,遇到這種遍歷訪問數(shù)組的情況時,按照內(nèi)存布局順序訪問將會帶來很大的性能提升。

(2)分支預(yù)測

#define N 128 * 1024 * 10

int main(int argc, char **argv)
{
    ofstream ofs;
    unsigned char arr[N];
    for (long i = 0; i < N; i++)
        arr[i] = rand() % 256;
    ofs.open("rand", ios::out | ios::binary);
    ofs.write((const char*)arr, N);
    ofs.close();
    sort(arr,arr+N);
    ofs.open("sort", ios::out | ios::binary);
    ofs.write((const char*)arr, N);
    ofs.close();

    {
        unsigned char arr[N];
        ifstream ifs;
  ifs.open("rand");
  ifs.read((char *)arr, N);
        clock_t start, end;
        start = clock();
        for (long i = 0; i < N; i++)
        {
            if (arr[i] < 128)
                arr[i] = 0;
        }
        end = clock();
        cout << "timecost: " << timecost(start, end) << endl;
    }
    {
        unsigned char arr[N];
        ifstream ifs;
        ifs.open("sort");
  ifs.read((char *)arr, N);
        clock_t start, end;
        start = clock();
        for (long i = 0; i < N; i++)
        {
            if (arr[i] < 128)
                arr[i] = 0;
        }
        end = clock();
        cout << "timecost: " << timecost(start, end) << endl;
    }
}

以上代碼做了兩個操作,:一是循環(huán)遍歷數(shù)組,判斷每個數(shù)字是否小于128,如果小于則把元素的值置為0;二是將數(shù)組排序。那么,先排序再遍歷速度快,還是先遍歷再排序速度快呢?其輸出結(jié)果:

timecost: 11
timecost: 3

從耗時可以看出排序后的數(shù)據(jù)性能要比未排序的性能高3倍,為什么?我們可以通過perf stat -e branch-loads,branch-load-misses ./a.out獲得輸出():

// 第一段代碼
Performance counter stats for './a.out':

    263,372,189      branch-loads
     89,137,210      branch-load-misses

// 第二段代碼
Performance counter stats for './a.out':

    261,134,898      branch-loads
        137,210      branch-load-misses

可見分支預(yù)測對于性能提升有很大的影響,如果我們遇到類似的問題,可以通過優(yōu)化代碼提升指令緩存的命中率。

(3)多核

從CPU的緩存架構(gòu)圖可以看出,多核的CPU的L1,L2緩存是每顆核心獨享的,如果啟動某個線程,根據(jù)調(diào)度時間片,可能線程在某個時刻運行的核心1上,下一個調(diào)度時間片可能就在核心2上,這樣L1,L2緩存存在不命中的問題,但是如果我們能讓線程或者進程獨立的跑在一個核心上,這樣就不需要將緩存換入緩出,理論上就可以提升性能,在Linux系統(tǒng)中的確提供了這種能力,通過sched_setaffinity可以綁定CPU核心,然后perf查看cpu-migrations的CPU遷移次數(shù)發(fā)現(xiàn)會減少,這里就不展開代碼了,有興趣的可以研究一下Nginx的worker_cpu_affinity配置,設(shè)置Nginx進程與CPU進行綁定的。

(4)向量化優(yōu)化(SIMD)

SIMD全稱single-instruction multiple-data(單指令多數(shù)據(jù)),在傳統(tǒng)的計算機架構(gòu)中,CPU一次只能處理一個數(shù)據(jù)元素,但是,許多任務(wù)涉及對大量數(shù)據(jù)執(zhí)行相同的操作,例如對數(shù)組中的所有元素進行加法、乘法或邏輯操作等,SIMD編程通過向CPU提供專門的指令集,使得CPU能夠同時對多個數(shù)據(jù)元素執(zhí)行相同的操作,這種處理方式特別適合涉及向量、矩陣、圖像、音頻和視頻等數(shù)據(jù)的計算,使用樣例如下:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <emmintrin.h>
#define MAX 200000
#define COUNT 100

void mul_test1(float *buf)
{
    for (int i = 0; i < MAX; ++i)
    {
        buf[i] = buf[i] * buf[i];
    }
}

void mul_test2(float *buf)
{
    for (int i = 0; i < MAX; i += 4)
    {
        _mm_storeu_ps(buf + i, _mm_mul_ps(_mm_loadu_ps(buf + i), _mm_loadu_ps(buf + i)));
    }
}

int main()
{
    float buf[MAX];
    for (int i = 0; i < MAX; ++i)
    {
        buf[i] = (float)(rand() % 1000);
    }

    {
        clock_t start, end;
        float duration;
        for (int i = 0; i < COUNT; ++i)
        {
            start = clock();
            mul_test1(buf);
            end = clock();
            duration += ((double)(end - start)) / CLOCKS_PER_SEC;
        }
        printf("costtime =%.3f\n", duration * 1000 / COUNT);
    }
    {
        clock_t start, end;
        float duration;
        for (int i = 0; i < COUNT; ++i)
        {
            start = clock();
            mul_test2(buf);
            end = clock();
            duration += ((double)(end - start)) / CLOCKS_PER_SEC;
        }
        printf("costtime =%.3f\n", duration * 1000 / COUNT);
    }
    return 0;
}

從輸出來看,SIMD在性能上比通用寫法要快很多,如下(這里編譯時關(guān)閉優(yōu)化選項g++ O1/O2/O3等,防止編譯器優(yōu)化可以對比出性能):

costtime =0.513
costtime =0.274

(5)PGO和LTO等編譯器優(yōu)化

通常在代碼編譯期間,編譯器會做優(yōu)化有很多,除了gcc通過-O1 -O2 -O3,內(nèi)聯(lián),尾遞歸等優(yōu)化外,現(xiàn)在了解比較多的是PGO和LTO:

  • PGO(Profile-guided optimization)通常也叫做FDO(Feedback-directed optimization),它是一種編譯優(yōu)化技術(shù),它的原理是編譯器使用程序的運行時profiling信息,生成更高質(zhì)量的代碼,從而提高程序的性能。
  • LTO也叫鏈接期優(yōu)化,它相對于編譯期優(yōu)化的最大優(yōu)勢在于,在鏈接期,編譯器可以把整個程序放在一起看,以全局視角進行優(yōu)化,達(dá)到更好的效果。

PGO優(yōu)化樣例:

#include <time.h>
#include <iostream>
#include <unistd.h>
#include <stdlib.h>

using namespace std;

long m = 502000000;
char arr[4] = {'1', '2', '3', 0};

long timecost(clock_t t1, clock_t t2)
{
    long elapsed = ((double)t2 - t1) / CLOCKS_PER_SEC * 1000;
    return elapsed;
}

long test()
{
    long sum = 0;
    int a = 0;
    for (a = 0; a < m; ++a)
    {
        sum += atoi(arr + (a % 2));
    }
    return sum;
}
int main(int argc, const char *argv[])
{
    clock_t start, end;
    start = clock();
    long sum = test();
    end = clock();
    cout << "sum: " << sum << ", timecost: " << timecost(start, end) << endl;
    return 0;
}

// 執(zhí)行如下命令:
g++ test5.cc -O2 -o origin
g++ test5.cc -O2 -fprofile-generate -o trace
./trace
g++ test5.cc -O2 -fprofile-use -o optimized 
./origin
./optimized

// 輸出結(jié)果:
[root@VM-0-11-centos ~]# ./trace
sum: 36646000000, timecost: 4710
[root@VM-0-11-centos ~]# g++ test5.cc -O2 -fprofile-use -o optimized
[root@VM-0-11-centos ~]# ./optimized
sum: 36646000000, timecost: 4670
[root@VM-0-11-centos ~]# ./origin
sum: 36646000000, timecost: 4710

從輸出的結(jié)果看提升一小部分性能,如果程序更加復(fù)雜,性能提升會更多,如果有興趣也可以了解關(guān)于微軟的團隊使用Profile Guided Optimization(PGO)和Link-time Optimization(LTO)來優(yōu)化Linux內(nèi)核和Redis提升性能。

2、內(nèi)存

(1)內(nèi)存池

內(nèi)存池或者對象池是高性能編程一種重要的優(yōu)化方式,假設(shè)在實際代碼開發(fā)過程中,需要頻繁申請和釋放內(nèi)存4個字節(jié)的內(nèi)存,與其把這4字節(jié)釋放給操作系統(tǒng),不如先緩存著放進內(nèi)存池里,仍然當(dāng)作用戶態(tài)內(nèi)存留下來,進程再次申請4字節(jié)內(nèi)存時就可以直接復(fù)用,這樣速度快了很多,其中ptmalloc,tcmalloc和jemalloc庫都是通過類似方式實現(xiàn),這里為了快速了解,我們直接tcmalloc為例剖析。

tcmalloctcmalloc

  • Front-end:負(fù)責(zé)提供快速分配和重分配內(nèi)存給應(yīng)用,由Per-thread cache和Per-CPU cache兩部分組成,這里是ThreadCache,用于小對象分配,線程本地緩存,每個線程獨立維護一個該對象,多線程在并發(fā)申請內(nèi)存時不會產(chǎn)生鎖競爭;
  • Middle-end(中臺):負(fù)責(zé)給Front-end提供緩存,當(dāng)Front-end緩存內(nèi)存不夠用時,從Middle-end申請內(nèi)存,這里是CentralCache,全局cache,所有線程共享,當(dāng)thread cache空閑鏈表為空時,會批量從CentralCache中申請內(nèi)存,當(dāng)thread cache總內(nèi)存超過閾值,會進行內(nèi)存垃圾回收,將空閑內(nèi)存返還給CentralCache;
  • Back-end(后端):負(fù)責(zé)從操作系統(tǒng)獲取內(nèi)存,并給Middle-end提供緩存使用,這里包括Page Heap(小/大對象)和系統(tǒng)內(nèi)存,其中Page Heap(小/大對象)是全局頁堆,所有線程共享,對于小對象,當(dāng)centralcache為空時,會從page heap中申請一個span,當(dāng)一個span完全空閑時,會將該span返還給page heap,對于大對象,直接從page heap中分配,用完直接返還給page heap。而系統(tǒng)內(nèi)存是在當(dāng)page cache內(nèi)存用光后,會通過sbrk、mmap等系統(tǒng)調(diào)用向OS申請內(nèi)存;

(2)一些場景下可以優(yōu)先使用棧

從以下代碼我們驗證一下堆上和棧上分配內(nèi)存,看看性能對比(這里取出了編譯器優(yōu)化):

void test_on_stack()
{
    int a = 10;
}

void test_on_heap()
{
    int *a = (int *)malloc(sizeof(int));
    *a = 10;
    free(a);
}

// 輸出如下:
timecost: 258
timecost: 6664

可見棧上分配內(nèi)存性能更高,為什么?這里主要是棧是編譯期提前分配好了,而且棧是順序訪問,再者棧的數(shù)據(jù)可以直接到寄存器映射,還有一個最大的優(yōu)勢是線程在棧是獨立的,訪問的數(shù)據(jù)是無需加鎖的,所以在實際寫代碼過程中,對于占用空間少且頻繁訪問的都可以通過棧上內(nèi)存分配來操作。順便說以下,golang為了更好的性能,底層代碼中很多都是通過棧分配,當(dāng)分析非逃逸的變量,即使使用make分配內(nèi)存也是在棧上(具體可以讀讀golang的源碼)。

第三部分:鎖

多線程情況下,為了保證臨界區(qū)數(shù)據(jù)一致性,往往通過加鎖解決問題,包括互斥鎖,自旋鎖,樂觀鎖等等,當(dāng)然不同場景的方式不一樣,那下面我們來介紹幾種高性能情況下鎖的使用。

(1)互斥鎖與自旋鎖

互斥鎖:當(dāng)你無法判斷鎖住的代碼會執(zhí)行多久時,應(yīng)該首選互斥鎖,互斥鎖是一種獨占鎖,但是互斥鎖有對應(yīng)的問題是:內(nèi)核會不斷嘗試獲取鎖,如果獲取不到就會休眠,只有獲取到了才會執(zhí)行邏輯,這里要注意的是在線程獲取鎖失敗時,會增加兩次上下文切換的成本,從運行中切換為休眠,以及鎖釋放時從休眠狀態(tài)切換為運行中,這種頻繁的上下文切換和休眠在高并發(fā)服務(wù)無法容忍的行為;

自旋鎖:通常如果對于一些耗時很短的操作,可以嘗試使用自旋鎖,自旋鎖比互斥鎖快得多,因為它通過CPU提供的CAS函數(shù)(全稱 Compare And Swap),在用戶態(tài)代碼中完成加鎖與解鎖操作,比如while (!(CAS(lock, 0, args))) { ... },CAS是原子操作,有三個參數(shù)(內(nèi)存位置V、預(yù)期原值A(chǔ)、新值B),其中這段代碼如果lock==0則更新lock=args,否則繼續(xù)循環(huán)。但是自旋鎖會面臨ABA的問題(線程1讀到A值,但是線程2搶占將A改為B,再修改回A,然后線程1搶占就會認(rèn)為沒有修改,然后繼續(xù)執(zhí)行),所以在為了追求高性能,同時也要考慮各個鎖的缺點,從而避免BUG;

讀寫鎖:如果業(yè)務(wù)場景能明確讀寫,可以選擇使用讀寫鎖,當(dāng)寫鎖未被鎖住時,讀鎖可以實現(xiàn)多線程并發(fā),當(dāng)寫鎖鎖住后,讀鎖阻塞,所以讀寫鎖真正發(fā)揮優(yōu)勢的場景,必然是讀多寫少的場景,否則讀鎖將很難并發(fā)持有;

(2)樂觀鎖

什么是樂觀鎖?基于樂觀的情況,假設(shè)認(rèn)為數(shù)據(jù)一般情況下不會造成沖突,所以在數(shù)據(jù)進行提交更新的時候,才會正式對數(shù)據(jù)的沖突與否進行檢測。

樂觀鎖常用實現(xiàn)方式通過版本號,每個數(shù)據(jù)記錄都有一個對應(yīng)的版本號,事務(wù)在更新數(shù)據(jù)時,先讀取數(shù)據(jù)的當(dāng)前版本號,并在提交時檢查該版本號是否發(fā)生變化,如果沒有變化,說明操作是安全的,可以提交,如果發(fā)生變化,就需要進行回滾或重試操作。

從樂觀鎖的場景可以看出,對于讀多寫少的情況下,樂觀鎖是能減少沖突,提升性能。

(3)無鎖編程

為了高性能,我們前面提到減少上下文切換,減少臨界區(qū)沖突,其中鎖是最大的障礙之一,如果能通過無鎖編程,這樣能提升性能。

樂觀鎖是一種無鎖編程,上面已經(jīng)介紹了,通過版本號或者CAS減少沖突,能實現(xiàn)不加鎖;

線程局部變量,通過在GCC定義__thread變量,實現(xiàn)線程局部存儲,存取效率可以和全局變量相比,__thread變量每一個線程有一份獨立實體,各個線程的值互不干擾,某些場景下可以通過操作線程內(nèi)的局部變量后,統(tǒng)一同步到全局變量,實現(xiàn)不加鎖或者減少鎖;

臨界區(qū)Hash,之前在業(yè)務(wù)場景中遇到需要頻繁操作指定全局?jǐn)?shù)據(jù),但是線程之前操作的數(shù)據(jù)卻在某個時刻是獨立,這種場景可以將臨界區(qū)的數(shù)據(jù)Hash到各個槽中,當(dāng)線程需要操作數(shù)據(jù),可以先取槽的位置,然后到對應(yīng)的槽位上操作數(shù)據(jù)即可,這樣減少鎖鎖住的數(shù)據(jù)區(qū)域或者直接不加鎖可以提升性能;

將功能設(shè)計為單線程,如果是單線程程序自然就不需要加鎖了,比如Redis6.x之前的版本都是單線程處理,這樣數(shù)據(jù)結(jié)構(gòu)簡單,避免上下文切換等。

責(zé)任編輯:華軒 來源: 周末程序猿
相關(guān)推薦

2023-11-01 11:59:13

2024-03-18 13:43:20

Linux架構(gòu)

2023-11-01 10:38:46

Linux高性能網(wǎng)絡(luò)編程

2023-11-01 11:27:10

Linux協(xié)程

2023-11-01 11:40:46

Linux高性能網(wǎng)絡(luò)編程工具

2023-11-01 10:58:31

系統(tǒng)調(diào)用高性能網(wǎng)絡(luò)編程Linux

2023-11-01 11:07:05

Linux高性能網(wǎng)絡(luò)編程線程

2023-11-01 11:13:58

Linux信號處理定時器

2023-11-01 11:20:57

2023-11-01 10:43:31

Linux高性能網(wǎng)絡(luò)編程

2025-06-26 01:27:00

2020-11-06 18:51:17

LinuxTCP服務(wù)器

2019-12-10 08:10:35

LinuxCPU性能優(yōu)化

2024-10-06 14:37:52

2024-08-06 08:22:18

2024-09-03 09:15:37

2024-10-16 11:03:30

Linux高性能編程

2009-01-05 10:00:11

JSP優(yōu)化Servlet性能優(yōu)化

2021-02-06 09:40:11

LinuxCPU高性能

2014-07-28 16:47:41

linux性能
點贊
收藏

51CTO技術(shù)棧公眾號

久久久久久久久久久久久久久99 | 亚洲综合电影一区二区三区| 亚洲成在人线av| 亚洲 欧美 日韩 国产综合 在线 | 久久天堂久久| 天天爽夜夜爽夜夜爽精品视频| 欧美性bbwbbwbbwhd| 国产精品久久久久久免费| 亚洲视频一区| 伊人久久久久久久久久久久久| 手机精品视频在线| 美女视频在线免费| 亚洲欧美成aⅴ人在线观看 | 五月开心六月丁香综合色啪| 亚洲韩国日本中文字幕| 小明看看成人免费视频| 91av亚洲| 亚洲二区视频在线| 日韩人妻精品一区二区三区| 久久久久国产精品嫩草影院| 国产真实乱子伦精品视频| 奇米影视亚洲狠狠色| 全网免费在线播放视频入口| 精品国产一区二区三区av片| 欧美精品一区二区三区久久久| 香蕉视频999| 高潮一区二区| 性感美女极品91精品| av动漫免费观看| 神马久久精品| 国产99一区视频免费| 国产日韩中文字幕| www毛片com| 国产精品久久777777毛茸茸| 欧美巨猛xxxx猛交黑人97人| 日本成人精品视频| 久草在线成人| 日韩精品中文字幕在线| 最新国产精品自拍| 色综合一区二区日本韩国亚洲| 日韩欧美主播在线| 男女啪啪免费视频网站| 男女视频在线| 亚洲精品乱码久久久久久黑人| 日韩一区二区电影在线观看| 理论视频在线| 久久久99精品久久| 欧美日韩一区二| 亚洲av成人无码久久精品老人| 国产91精品在线观看| 亚洲自拍偷拍网址| 一级黄色录像大片| 久热成人在线视频| 成人久久一区二区| 国产精品高潮呻吟AV无码| 久久精品国产一区二区三区免费看 | 久久丫精品国产亚洲av不卡| 秋霞在线一区| 亚洲毛片在线观看| 男人舔女人下部高潮全视频| 国产欧美高清视频在线| 亚洲午夜久久久久久久| 黄色三级生活片| 色婷婷一区二区三区| 色伦专区97中文字幕| 亚洲人做受高潮| 99久久99久久精品国产片桃花 | 日韩免费电影一区| 久久久国产精品久久久| 97久久亚洲| 亚洲国产精品电影| 西西大胆午夜视频| 免费久久精品| 久久精品国产久精国产思思| 日韩黄色免费观看| 99在线精品视频在线观看| 欧美一级成年大片在线观看| а中文在线天堂| 美女视频免费一区| 91精品国产高清久久久久久91裸体 | 一级全黄肉体裸体全过程| 成人影院在线看| 亚洲mv在线观看| 欧美视频第三页| av日韩一区| 亚洲精品久久久久| 午夜黄色福利视频| 欧美日韩国产高清| 欧洲日本亚洲国产区| 在线观看免费观看在线| 国产91在线观看| 日韩精品在在线一区二区中文| 免费观看成人高潮| 五月激情综合色| 粉色视频免费看| 女同另类激情重口| 北条麻妃久久精品| wwwxxx亚洲| 精品一区二区三区在线观看 | 亚洲成av人**亚洲成av**| 成人羞羞国产免费网站| 国产精品久久久久久av公交车| 亚洲第一精品福利| 久久视频精品在线观看| 欧美三区不卡| 国产裸体写真av一区二区| 亚洲国产精品欧美久久| 日本一区二区三区视频视频| 国内少妇毛片视频| 91p九色成人| 亚洲激情视频在线| 亚洲天堂黄色片| 日韩av在线发布| 成人综合av网| 日本三级视频在线观看| 欧美特级www| 男生操女生视频在线观看| 亚洲图区在线| 久久久免费电影| 国产精品久久免费| 国产欧美视频一区二区| 欧美日本视频在线观看| 日韩精品一区二区三区中文在线| 日韩国产中文字幕| 久久久一区二区三区四区| 久久国产欧美日韩精品| 欧美日韩一区在线观看视频| 182在线播放| 日韩欧美电影在线| 性欧美videos| 国内外成人在线| 亚洲人成人77777线观看| 东京一区二区| 亚洲另类xxxx| 中国一级特黄毛片| caoporm超碰国产精品| 免费观看亚洲视频| 2019中文亚洲字幕| 色哟哟入口国产精品| 日韩三级一区二区| 久久综合九色欧美综合狠狠| 免费一级特黄毛片| 久久午夜影院| 孩xxxx性bbbb欧美| 亚洲免费视频网| 亚洲午夜在线电影| 无码av免费精品一区二区三区| 欧美高清不卡| www.成人av.com| 美女航空一级毛片在线播放| 精品日产卡一卡二卡麻豆| 激情五月少妇a| 成人性生交大片免费看视频在线 | 美女黄色成人网| 欧美极品一区二区| 美女福利一区二区三区| 亚洲欧美日韩国产中文| 久操视频在线免费观看| 国产女人18水真多18精品一级做| 欧美日韩大尺度| 日韩欧美网站| 成人在线观看视频网站| 中文字幕有码在线观看| 欧美成人三级在线| 日本在线视频免费| 91麻豆高清视频| 久久精品影视大全| 亚洲色图欧美| 国产麻豆日韩| 天天综合网天天| 按摩亚洲人久久| 亚洲av色香蕉一区二区三区| 亚洲h精品动漫在线观看| a级大片在线观看| 七七婷婷婷婷精品国产| 8x8x华人在线| 日韩啪啪网站| 国产欧美一区二区三区在线| 天堂av最新在线| 日韩极品精品视频免费观看| 中文字幕av久久爽| 亚洲精品福利视频网站| 亚洲第九十七页| 蜜桃av一区二区| 日本wwwcom| 狠狠综合久久av一区二区蜜桃| 成人亚洲欧美一区二区三区| av资源网在线播放| 中文字幕亚洲国产| 黄色一级大片在线免费看国产一| 色综合视频一区二区三区高清| 亚洲AV成人无码网站天堂久久| 国产成+人+日韩+欧美+亚洲| 日本美女高潮视频| 国产精品v一区二区三区| 人禽交欧美网站免费| 日韩欧美中文在线观看| 国产91热爆ts人妖在线| 羞羞电影在线观看www| 亚洲天堂av在线免费观看| 99热这里只有精品66| 色哟哟精品一区| 青青草免费av| 国产精品系列在线| japanese在线观看| 久久99久久久久| 一本大道熟女人妻中文字幕在线| 亚洲视频电影在线| 日本不卡在线观看| 国产精品x8x8一区二区| 成人福利网站在线观看11| 综合毛片免费视频| 欧美精品情趣视频| 午夜小视频在线| 亚洲欧美激情视频| 日本免费网站在线观看| 91.麻豆视频| 欧美一级黄视频| 欧美日韩国产丝袜美女| 久久久国产成人| 亚洲欧美日本韩国| 国产精品1区2区3区4区| 国产清纯美女被跳蛋高潮一区二区久久w | 国产盗摄一区二区三区| 性chinese极品按摩| 亚洲男人影院| av在线播放亚洲| 伊人成人在线| 国产在线视频在线| 中文字幕一区二区三区在线视频 | 亚洲一区二区网站| 成年女人18级毛片毛片免费 | 欧美疯狂做受xxxx高潮| 黄色网在线免费看| 深夜精品寂寞黄网站在线观看| 无码国产精品一区二区色情男同| 日韩欧美国产电影| 亚洲第一页综合| 日韩欧美国产系列| 国产ts人妖调教重口男| 欧美一区二区三区小说| 国产普通话bbwbbwbbw| 制服丝袜成人动漫| 国产偷拍一区二区| 538prom精品视频线放| 国产理论视频在线观看| 欧美高清精品3d| 99热这里只有精品5| 日韩欧美电影一区| 欧洲av在线播放| 亚洲精品av在线播放| 色综合成人av| 亚洲偷欧美偷国内偷| 成人免费黄色网页| 日韩在线视频网| 日本在线天堂| 另类专区欧美制服同性| 亚洲大胆人体大胆做受1| 欧美精品videosex极品1| 91美女主播在线视频| 欧美又大又硬又粗bbbbb| 色老太综合网| 成人a在线观看| 日韩精品成人在线观看| 精品欧美日韩| 国产尤物久久久| 樱花www成人免费视频| 亚洲欧美文学| 欧洲黄色一级视频| 日日噜噜夜夜狠狠视频欧美人| 日韩在线不卡一区| 国产a级毛片一区| 精品人妻无码一区| 自拍偷拍亚洲综合| 69精品久久久| 欧美亚洲一区二区在线观看| 国产探花精品一区二区| 日韩精品福利网站| 日韩av中文| 久久久久久久久电影| 欧美性猛交xxx高清大费中文| 成人黄色av播放免费| 国产91精品入| 天堂精品一区二区三区| 国内精品久久久久久久97牛牛| 91免费视频网站在线观看| 久久av资源站| 中出视频在线观看| 国产精品免费久久| 日本一级淫片色费放| 欧美综合亚洲图片综合区| 午夜免费福利视频| 一区二区欧美久久| 欧美性猛片xxxxx免费中国| 国产不卡在线观看| 日本一区精品视频| 日日骚一区二区网站| 亚洲一本视频| 中文字幕一区久久| 91麻豆精品视频| 玖玖爱这里只有精品| 色久综合一二码| 亚洲乱码精品久久久久..| 在线观看久久av| 蜜桃麻豆av在线| 99理论电影网| 小处雏高清一区二区三区| 国产午夜伦鲁鲁| 国产91露脸合集magnet| 国产精品suv一区二区88| 一本大道久久精品懂色aⅴ| 亚洲成人久久精品| 日韩一区二区在线视频| 成人性生活视频| 国产视频精品网| 亚洲欧美一区在线| 亚洲精品免费一区亚洲精品免费精品一区| 91网址在线看| 国产一级在线播放| 日韩一级大片在线| av在线免费一区| 日韩美女视频免费在线观看| 欧美日韩另类图片| 日韩国产成人无码av毛片| 国产伦精品一区二区三区视频青涩 | 人体精品一二三区| 成人av地址| 男人c女人视频| 国产久卡久卡久卡久卡视频精品| 国产精品成人在线视频| 91国产免费看| 国产在线一二三| 国产91成人video| 亚洲第一福利社区| 国产精品沙发午睡系列| 成人美女视频在线观看| 久久久久久激情| 欧美不卡一区二区三区四区| 亚洲图区一区| 99re热精品| 欧美日韩国产在线一区| wwwxxx色| 亚洲一区免费视频| 你懂的网站在线| 7777免费精品视频| 香蕉久久精品| 18岁视频在线观看| 国产日韩欧美高清在线| 一区二区乱子伦在线播放| 亚洲一级片在线看| 欧美一级做a| 糖心vlog在线免费观看| 国产精品一级片在线观看| 久久久久99精品成人片毛片| 欧美成人猛片aaaaaaa| h片在线观看视频免费免费| 久久综合一区| 秋霞电影一区二区| 久久久精品少妇| 欧美v亚洲v综合ⅴ国产v| 成人超碰在线| 蜜桃成人在线| 免费成人你懂的| 全网免费在线播放视频入口| 亚洲国产天堂久久国产91| 天堂av中文在线观看| 天天综合色天天综合色hd| 精品亚洲porn| 国产精品美女毛片真酒店| 亚洲人成网站777色婷婷| 日本欧美在线| 国产精品视频二| 久久综合久久综合九色| 国产成人自拍偷拍| 超碰精品一区二区三区乱码 | 日韩尤物视频| 国产麻豆欧美日韩一区| 动漫精品一区一码二码三码四码 | 国产不卡在线| 精品一区2区三区| 麻豆精品国产传媒mv男同| 青青草偷拍视频| 国产偷亚洲偷欧美偷精品| 国产麻豆精品在线| 黄色片网站免费| 欧美成人猛片aaaaaaa| 亚洲www免费| 香蕉精品视频在线| 972aa.com艺术欧美| 在线观看日韩一区二区| 欧美激情在线有限公司| 欧美一区二区性| 成人免费看片载| 欧美日韩三级在线| 国产激情在线播放| 一区二区成人国产精品| 91视频在线观看免费| 国产毛片一区二区三区va在线| 欧美一区二区三区免费观看| 欧美一区免费|