CPU 程序性能優(yōu)化

一個(gè)程序首先要保證正確性族扰,在保證正確性的基礎(chǔ)上厌丑,性能也是一個(gè)重要的考量。要編寫高性能的程序渔呵,第一怒竿,必須選擇合適的算法和數(shù)據(jù)結(jié)構(gòu);第二厘肮,應(yīng)該編寫編譯器能夠有效優(yōu)化以轉(zhuǎn)換成高效可執(zhí)行代碼的源代碼愧口,要做到這一點(diǎn),需要了解編譯器的能力和限制类茂;第三耍属,要了解硬件的運(yùn)行方式托嚣,針對(duì)硬件特性進(jìn)行優(yōu)化。本文著重展開(kāi)第二點(diǎn)和第三點(diǎn)厚骗。

簡(jiǎn)單認(rèn)識(shí)編譯器

要寫出高性能的代碼示启,首先需要對(duì)編譯器有基礎(chǔ)的了解,原因在于現(xiàn)代編譯器有很強(qiáng)的優(yōu)化能力领舰,但有些代碼編譯器不能進(jìn)行優(yōu)化夫嗓。對(duì)編譯器有了基礎(chǔ)的了解,才能寫出編譯器友好型高性能代碼冲秽。

編譯器的優(yōu)化選項(xiàng)

GCC 為例舍咖,GCC 支持以下優(yōu)化級(jí)別:

  • -O<number>,其中 number 為 0/1/2/3锉桑,數(shù)字越大排霉,優(yōu)化級(jí)別越高。默認(rèn)為 -O0民轴。

  • -Ofast攻柠,除了開(kāi)啟 -O3 的所有優(yōu)化選項(xiàng)外,會(huì)額外打開(kāi) -ffast-math 和 -fallow-store-data-races后裸。注意這兩個(gè)選項(xiàng)可能會(huì)引起程序運(yùn)行錯(cuò)誤瑰钮。

-ffast-math: Sets the options -fno-math-errno, -funsafe-math-optimizations, -ffinite-math-only, -fno-rounding-math, -fno-signaling-nans, -fcx-limited-range and -fexcess-precision=fast. It can result in incorrect output for programs that depend on an exact implementation of IEEE or ISO rules/specifications for math functions. It may, however, yield faster code for programs that do not require the guarantees of these specifications.

-fallow-store-data-races: Allow the compiler to perform optimizations that may introduce new data races on stores, without proving that the variable cannot be concurrently accessed by other threads. Does not affect optimization of local data. It is safe to use this option if it is known that global data will not be accessed by multiple threads.

  • -Og,調(diào)試代碼時(shí)推薦使用的優(yōu)化級(jí)別微驶。

gcc -Q --help=optimizer -Ox 可查看各優(yōu)化級(jí)別開(kāi)啟的優(yōu)化選項(xiàng)浪谴。

參考鏈接:https://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html

編譯器的限制

為了保證程序運(yùn)行的正確性,編譯器不會(huì)對(duì)代碼的使用場(chǎng)景做任何假設(shè)祈搜,所以有些代碼編譯器不會(huì)進(jìn)行優(yōu)化较店。下面舉兩個(gè)比較隱晦的例子。

1容燕、memory aliasing

void twiddle1(long *xp, long *yp) {
    *xp += *yp;
    *xp += *yp;
}
void twiddle2(long *xp, long *yp) {
    *xp += 2 * *yp;
}

當(dāng) xpyp 指向同樣的內(nèi)存(memory aliasing)時(shí)梁呈,twiddle1twiddle2 是兩個(gè)完全不同的函數(shù),所以編譯器不會(huì)嘗試將 twiddle1 優(yōu)化為 twiddle2蘸秘。如果本意是希望實(shí)現(xiàn) twiddle2 的功能官卡,應(yīng)該寫成 twiddle2 而非 twwidle1 的形式,twiddle2 只需要 2 次讀 1 次寫醋虏,而 twiddle1 需要 4 次讀 2 次寫寻咒。

可以顯式使用 __restrict 修飾指針,表明不存在和被修飾的指針指向同一塊內(nèi)存的指針颈嚼,此時(shí)編譯器會(huì)將 twiddle3 優(yōu)化為和 twiddle2 等效毛秘。可自行通過(guò)反匯編的方式觀察匯編碼進(jìn)一步理解。

void twiddle3(long *__restrict xp, long *__restrict yp) {
    *xp += *yp;
    *xp += *yp;
}

2叫挟、side effect

long f();
long func1() {
    return f() + f() + f() + f();
}
long func2() {
    return 4 * f();
}

由于函數(shù) f 的實(shí)現(xiàn)可能如下艰匙,存在 side effect,所以編譯器不會(huì)將 func1 優(yōu)化為 func2抹恳。如果本意希望實(shí)現(xiàn) func2 版本员凝,則應(yīng)該直接寫成 func2 的形式,可減少 3 次函數(shù)調(diào)用奋献。

long counter = 0;
long f() {
    return counter++;
}

程序性能優(yōu)化

在介紹之前健霹,我們先引入一個(gè)程序性能度量標(biāo)準(zhǔn)每元素的周期數(shù)(Cycles Per Element, CPE),即每處理一個(gè)元素需要花費(fèi)的周期數(shù)瓶蚂,可以表示程序性能并指導(dǎo)性能優(yōu)化糖埋。

下面通過(guò)一個(gè)例子介紹幾個(gè)優(yōu)化程序性能的手段。首先定義一個(gè)數(shù)據(jù)結(jié)構(gòu) vector 以及一些輔助函數(shù), vector 使用一個(gè)連續(xù)存儲(chǔ)的數(shù)組實(shí)現(xiàn)窃这,可通過(guò) typedef 來(lái)指定元素的數(shù)據(jù)類型 data_t阶捆。

typedef struct {
    long len;
    data_t *data;
} vec_rec, *vec_ptr;

/* 創(chuàng)建vector */
vec_ptr new_vec(long len) {
    vec_ptr result = (vec_ptr)malloc(sizeof(vec_rec));
    if (!result)
        return NULL;
    data_t *data = NULL;
    result->len = len;
    if (len > 0) {
        data = (data_t*)calloc(len, sizeof(data_t));
        if (!data) {
            free(result);
            return NULL;
        }
    }
    result->data = data;
    return result;
}

/* 根據(jù)index獲取vector元素 */
int get_vec_element(vec_ptr v, long index, data_t *dest) {
    if (index < 0 || index >= v->len)
        return 0;
    *dest = v->data[index];
    return 1;
}

/* 獲取vector元素個(gè)數(shù) */
long vec_length(vec_ptr v) {
    return v->len;
}

下面的函數(shù)的功能是使用某種運(yùn)算,將一個(gè)向量中所有的元素合并為一個(gè)元素钦听。下面的 IDENTOP 是宏定義,#define IDENT 0#define OP + 進(jìn)行累加運(yùn)算倍奢,#define IDENT 1#define OP * 則進(jìn)行累乘運(yùn)算朴上。

void combine1(vec_ptr v, data_t *dest) {
    long i;

    *dest = IDENT;
    for (i = 0; i < vec_length(v); i++) {
        data_t val;
        get_vec_element(v, i, &val);
        *dest = *dest OP val;
    }
}

對(duì)于上面的 combine1,可以進(jìn)行下面三個(gè)基礎(chǔ)的優(yōu)化卒煞。

1痪宰、對(duì)于多次執(zhí)行返回同樣結(jié)果的函數(shù),使用臨時(shí)變量保存

combine1 的實(shí)現(xiàn)在循環(huán)測(cè)試條件中反復(fù)調(diào)用了函數(shù) vec_length畔裕,在此場(chǎng)景下衣撬,多次調(diào)用 vec_length 會(huì)返回同樣的結(jié)果,所以可以改寫為 combine2 的實(shí)現(xiàn)進(jìn)行優(yōu)化扮饶。在極端情況下具练,注意避免反復(fù)調(diào)用返回同樣結(jié)果的函數(shù)是更有效的。例如甜无,若在循環(huán)結(jié)束條件中調(diào)用測(cè)試一個(gè)字符串長(zhǎng)度的函數(shù)扛点,該函數(shù)時(shí)間復(fù)雜度通常是 O(n),若明確字符串長(zhǎng)度不會(huì)變化岂丘,反復(fù)調(diào)用會(huì)有很大的額外開(kāi)銷陵究。

void combine2(vec_ptr v, data_t *dest) {
    long i;
    long length = vec_length(v);

    *dest = IDENT;
    for (i = 0; i < length; i++) {
        data_t val;
        get_vec_element(v, i, &val);
        *dest = *dest OP val;
    }
}

2、減少過(guò)程調(diào)用

過(guò)程(函數(shù))調(diào)用會(huì)產(chǎn)生一定的開(kāi)銷奥帘,例如參數(shù)傳遞铜邮、clobber 寄存器保存恢復(fù)和轉(zhuǎn)移控制等。所以可以新增一個(gè)函數(shù) get_vec_start 返回指向數(shù)組的開(kāi)頭的指針,在循環(huán)中避免調(diào)用函數(shù) get_vec_element松蒜。這個(gè)優(yōu)化存在一個(gè) trade off扔茅,一方面可以一定程序提升程序性能,另一方面這個(gè)優(yōu)化需要知道 vector 數(shù)據(jù)結(jié)構(gòu)的實(shí)現(xiàn)細(xì)節(jié)牍鞠,會(huì)破壞程序的抽象咖摹,一旦 vector 修改為不使用數(shù)組的方式存儲(chǔ)數(shù)據(jù),則同時(shí)需要修改 combine3 的實(shí)現(xiàn)难述。

data_t *get_vec_start(vec_ptr v) {
    return v->data;
}
void combine3(vec_ptr v, data_t *dest) {
    long i;
    long length = vec_length(v);
    data_t *data = get_vec_start(v);

    *dest = IDENT;
    for (i = 0; i < length; i++) {
        *dest = *dest OP data[i];
    }
}

3萤晴、消除不必要的內(nèi)存引用

在上面的實(shí)現(xiàn)中,循環(huán)中每次都會(huì)去讀一次寫一次 dest胁后,由于可能存在 memory aliasing店读,編譯器會(huì)謹(jǐn)慎地進(jìn)行優(yōu)化。下面分別是 -O1-O2 優(yōu)化級(jí)別時(shí)攀芯,combine3for 循環(huán)部分的匯編代碼屯断。可以看到侣诺,開(kāi)啟 -O2 優(yōu)化時(shí)殖演,編譯器幫我們把中間結(jié)果存到了臨時(shí)變量中(寄存器 %xmm0),而不是像 -O1 優(yōu)化時(shí)每次從內(nèi)存中讀饶暝А趴久;但是考慮到 memory aliasing 的情況,即使 -O2 優(yōu)化搔确,依然需要每次循環(huán)將中間結(jié)果保存到內(nèi)存彼棍。

// combine3 -O1
.L1:
    vmovsd (%rbx), %xmm0
    vmulsd (%rdx), %xmm0, %xmm0
    vmovsd %xmm0, (%rbx)
    addq $8, %rdx
    cmpq %rax, %rdx
    jne .L1

// combine3 -O2
.L1
    vmulsd (%rdx), %xmm0, %xmm0
    addq $8, %rdx
    cmpq %rax, %rdx
    vmovsd %xmm0, (%rbx)
    jne .L1

為了避免頻繁進(jìn)行內(nèi)存讀寫,可以人為地使用一個(gè)臨時(shí)變量保存中間結(jié)果膳算,如 combine4 所示座硕。

void combine4(vec_ptr v, data_t *dest) {
    long i;
    long length = vec_length(v);
    data_t *data = get_vec_start(v);
    data_t acc = IDENT;
    for (i = 0; i < length; i++) {
        acc = acc OP data[i];
    }
    *dest = acc;
}
// combine4 -O1
.L1
    vmulsd (%rdx), %xmm0, %xmm0
    addq $8, %rdx
    cmpq %rax, %rdx
    jne .L1

以上優(yōu)化方法的效果可以通過(guò) CPE 來(lái)度量,在 Intel Core i7 Haswell 的測(cè)試結(jié)果如下涕蜂。從測(cè)試結(jié)果來(lái)看:

  • combine1 版本不同編譯優(yōu)化級(jí)別华匾,-O1 的性能是 -O0 的兩倍,表明開(kāi)啟適當(dāng)?shù)鼐幾g優(yōu)化級(jí)別是很有必要的机隙。

  • combine2 將 vec_length 移出循環(huán)后瘦真,在同樣的優(yōu)化級(jí)別編譯,相較 combine1 的性能有微小的提升黍瞧。

  • 但是 combine3 相比 combine2 并沒(méi)有性能提升诸尽,原因是由于循環(huán)中的其它操作的耗時(shí)可以掩蓋調(diào)用 get_vec_element 的耗時(shí),之所以可以掩蓋印颤,得益于 CPU 支持分支預(yù)測(cè)亂序執(zhí)行您机,本文的后面會(huì)簡(jiǎn)單介紹這兩個(gè)概念。

  • 同樣地,combine3 的 -O2 版本比 -O1 版本性能好很多际看,從匯編碼可以看到咸产,-O2 時(shí)比 -O1 每次循環(huán)減少了一次對(duì)(%rbx)的讀,更重要的是消除了對(duì)(%rbx)寫后讀的訪存依賴仲闽。

  • 經(jīng)過(guò) combine4 將中間結(jié)果暫存到臨時(shí)變量的優(yōu)化脑溢,可以看到即使使用 -O1 的編譯優(yōu)化,也比 combine3 -O2 的編譯優(yōu)化性能更好赖欣,表明即使編譯器有強(qiáng)大的優(yōu)化能力屑彻,但是注意細(xì)節(jié)來(lái)編寫高性能代碼也是非常有必要的。

以下測(cè)試數(shù)據(jù)引用自《深入理解計(jì)算機(jī)系統(tǒng)》第五章顶吮。

函數(shù) 優(yōu)化方法 int + int * float + float *
combine1 -O0 22.68 20.02 19.98 20.18
combine1 -O1 10.12 10.12 10.17 11.14
combine2 移動(dòng) vec_length -O1 7.02 9.03 9.02 11.03
combine3 減少過(guò)程調(diào)用 -O1 7.17 9.02 9.02 11.03
combine3 減少過(guò)程調(diào)用 -O2 1.60 3.01 3.01 5.01
combine4 累積到臨時(shí)變量 -O1 1.27 3.01 3.01 5.01

指令級(jí)并行

以上優(yōu)化不依賴于目標(biāo)機(jī)器的任何特性社牲,只是簡(jiǎn)單地降低了過(guò)程調(diào)用的開(kāi)銷,以及消除一些“妨礙優(yōu)化的因素”悴了,這些因素會(huì)給編譯器優(yōu)化帶來(lái)困難搏恤。要進(jìn)行進(jìn)一步優(yōu)化,需要了解一些硬件特性湃交。下圖是 Intel Core i7 Haswell 的硬件結(jié)構(gòu)的后端部分:


haswell.png

完整的 Intel Core i7 Haswell 的硬件結(jié)構(gòu)見(jiàn):https://en.wikichip.org/w/images/c/c7/haswell_block_diagram.svg

硬件性能

該 CPU 支持以下特性:

  • 指令級(jí)并行:即通過(guò)指令流水線技術(shù)熟空,支持同時(shí)對(duì)多條指令求值。

  • 亂序執(zhí)行:指令的執(zhí)行順序未必和其書寫的順序一致搞莺,可以使硬件達(dá)到更好的指令級(jí)并行度痛阻。主要是通過(guò)亂序執(zhí)行、順序提交的機(jī)制腮敌,使得能夠獲得和順序執(zhí)行一致的結(jié)果。

  • 分支預(yù)測(cè):當(dāng)遇到分支時(shí)俏扩,硬件會(huì)預(yù)測(cè)分支的走向糜工,如果預(yù)測(cè)成功則能夠加快程序的運(yùn)行,但是預(yù)測(cè)失敗的話則需要把提前執(zhí)行的結(jié)果丟棄录淡,重新 load 正確指令執(zhí)行捌木,會(huì)帶來(lái)比較大的預(yù)測(cè)錯(cuò)誤懲罰。

上圖中嫉戚,主要關(guān)注執(zhí)行單元(EUs)刨裆,執(zhí)行單元由多個(gè)功能單元組成。功能單元的性能可以由延遲彬檀、發(fā)射時(shí)間容量來(lái)度量帆啃。

  • 延遲:執(zhí)行完一條指令需要的時(shí)鐘周期數(shù)。

  • 發(fā)射時(shí)間:兩個(gè)連續(xù)的同類型的運(yùn)算之間需要的最小時(shí)鐘周期數(shù)窍帝。

  • 容量:某種執(zhí)行單元的數(shù)量努潘。從上圖可以看出,在 EUs 中,有 4 個(gè)整數(shù)加法單元(INT ALU)疯坤、1 個(gè)整數(shù)乘法單元(INT MUL)报慕、1 個(gè)浮點(diǎn)數(shù)加法單元(FP ADD)和 2 個(gè)浮點(diǎn)數(shù)乘法單元(FP MUL)。

Intel Core i7 Haswell 的功能單元性能數(shù)據(jù)(單位為周期數(shù))如下压怠,引自《深入理解計(jì)算機(jī)系統(tǒng)》第五章:

運(yùn)算 延遲(int) 發(fā)射時(shí)間(int) 容量(int) 延遲(float) 發(fā)射時(shí)間(float) 容量(float)
加法 1 1 4 3 1 1
乘法 3 1 1 5 1 2

這些算術(shù)運(yùn)算的延遲眠冈、發(fā)射時(shí)間和容量會(huì)影響上述 combine 函數(shù)的性能,我們用 CPE 的兩個(gè)界限來(lái)描述這種影響菌瘫。吞吐界限是理論上的最優(yōu)性能蜗顽。

  • 延遲界限:任何必須按照嚴(yán)格順序完成 combine 運(yùn)算的函數(shù)所需要的最小 CPE,等于功能單元的延遲突梦。

  • 吞吐界限:功能單元產(chǎn)生結(jié)果的最大速率诫舅,由容量/發(fā)射時(shí)間決定。若使用 CPE 度量宫患,則等于容量/發(fā)射時(shí)間的倒數(shù)刊懈。

由于 combine 函數(shù)需要 load 數(shù)據(jù),故要同時(shí)受到加載單元的限制娃闲。由于只有兩個(gè)加載單元且其發(fā)射時(shí)間為 1 個(gè)周期虚汛,所以整數(shù)加法的吞吐界限在本例中只有 0.5 而非 0.25。

界限 int + int * float + float *
延遲 1.0 3.0 3.0 5.0
吞吐 0.5 1.0 1.0 0.5

處理器操作的抽象模型

為了分析在現(xiàn)代處理器上執(zhí)行的機(jī)器級(jí)程序的性能皇帮,我們引入數(shù)據(jù)流圖卷哩,這是一種圖形化表示方法,展現(xiàn)了不同操作之間的數(shù)據(jù)相關(guān)是如何限制它們的執(zhí)行順序的属拾。這些限制形成了圖中的關(guān)鍵路徑将谊,這是執(zhí)行一組機(jī)器指令所需時(shí)鐘周期的一個(gè)下界。

通常 for 循環(huán)會(huì)占據(jù)程序執(zhí)行的大部分時(shí)間渐白,下圖是 combine4 的 for 循環(huán)對(duì)應(yīng)的數(shù)據(jù)流圖尊浓。其中箭頭指示了數(shù)據(jù)的流向〈垦埽可以將寄存器分為四類:

  1. 只讀:這些寄存器只用作源值栋齿,在循環(huán)中不被修改,本例中的 %rax襟诸。

  2. 只寫:作為數(shù)據(jù)傳送的目的瓦堵。本例沒(méi)有這樣的寄存器。

  3. 局部:在循環(huán)內(nèi)部被修改和使用歌亲,迭代與迭代之間不相關(guān)菇用,比例中的條件碼寄存器。

  4. 循環(huán):這些寄存器既作為源值陷揪,又作為目的刨疼,一次迭代中產(chǎn)生的值會(huì)被下一次迭代用到泉唁,本例中的 %rdx%xmm0。由于兩次迭代之間有數(shù)據(jù)依賴揩慕,所以對(duì)此類寄存器的操作通常是程序性能的限制因素亭畜。

將上圖重排,并只留下循環(huán)寄存器相關(guān)的路徑迎卤,可得到簡(jiǎn)化的數(shù)據(jù)流圖拴鸵。

將簡(jiǎn)化完的數(shù)據(jù)流圖進(jìn)行簡(jiǎn)單地重復(fù),可以得到關(guān)鍵路徑蜗搔,如下圖劲藐。如果 combine4 中計(jì)算的是浮點(diǎn)數(shù)乘法,由于支持指令級(jí)并行樟凄,浮點(diǎn)數(shù)乘法的的延遲能夠掩蓋整數(shù)加法(指針移動(dòng)聘芜,圖中右半邊的路徑)的延遲,所以 combine4CPE 的理論下界就是浮點(diǎn)乘法的延遲 5.0缝龄,與上面給出的測(cè)試數(shù)據(jù) 5.01 基本一致汰现。

循環(huán)展開(kāi)

目前為止,我們程序的性能只達(dá)到了延遲界限叔壤,這是因?yàn)橄乱淮胃↑c(diǎn)乘法必須等上一次乘法結(jié)束后才開(kāi)始瞎饲,不能充分利用硬件的指令級(jí)并行。使用循環(huán)展開(kāi)的技術(shù)炼绘,可以提高關(guān)鍵路徑的指令并行度嗅战。

void combine5(vec_ptr v, data_t *dest) {
    long i;
    long length = vec_length(v);
    long limit = length - 1;
    data_t *data = get_vec_start(v);
    data_t acc0 = IDENT;
    data_t acc1 = IDENT;

    for (i = 0; i < limit; i += 2) {
        acc0 = acc0 OP data[i];
        acc1 = acc1 OP data[i + 1];
    }

    for (; i < length; ++i) {
        acc0 = acc0 OP data[i];
    }

    *dest = acc0 OP acc1;
}

combine5 的關(guān)鍵路徑的數(shù)據(jù)流圖如下,圖中有兩條關(guān)鍵路徑俺亮,但兩條關(guān)鍵路徑是可以指令級(jí)并行的驮捍,每條關(guān)鍵路徑只包含 n/2 個(gè)操作,因此性能可以突破延遲界限脚曾,理論上浮點(diǎn)乘法的 CPE 約為 5.0/2=2.5东且。

假如增加臨時(shí)變量的個(gè)數(shù)進(jìn)一步增加循環(huán)展開(kāi)次數(shù),理論上可以提高指令并行度斟珊,最終達(dá)到吞吐界限。但是不能無(wú)限制地增加循環(huán)展開(kāi)次數(shù)富纸,一是由于硬件的功能單元有限囤踩,CPE 的下界由吞吐界限限制,達(dá)到一定程度后繼續(xù)增加也不能提高指令并行度晓褪;二是由于寄存器資源有限堵漱,增加循環(huán)展開(kāi)次數(shù)會(huì)增加寄存器的使用,使用的寄存器個(gè)數(shù)超過(guò)硬件提供的寄存器資源之后涣仿,則會(huì)發(fā)生寄存器溢出勤庐,可能會(huì)需要將寄存器的內(nèi)存臨時(shí)保存到內(nèi)存示惊,使用時(shí)再?gòu)膬?nèi)存恢復(fù)到寄存器,反而導(dǎo)致性能的下降愉镰,如下表中循環(huán)展開(kāi) 20 次相較展開(kāi) 10 次性能反而略有下降米罚。幸運(yùn)的是,大多數(shù)硬件在寄存器溢出之前已經(jīng)達(dá)到了吞吐界限丈探。

函數(shù) 展開(kāi)次數(shù) int + int * float + float *
combine5 2 0.81 1.51 1.51 2.51
combine5 10 0.55 1.00 1.01 0.52
combine5 20 0.83 1.03 1.02 0.68
延遲界限 / 1.00 3.00 3.00 5.00
吞吐界限 / 0.50 1.00 1.00 0.50

SIMD(single instruction multi data)

SIMD 是另外一種行之有效的性能優(yōu)化手段录择,不同于指令級(jí)并行,其采用數(shù)據(jù)級(jí)并行碗降。SIMD 即單指令多數(shù)據(jù)隘竭,一條指令操作一批向量數(shù)據(jù),需要硬件提供支持讼渊。X86 架構(gòu)的 CPU 支持 AVX 指令集动看,ARM CPU 支持 NEON 指令集。在我們開(kāi)發(fā)的一款深度學(xué)習(xí)編譯器 MegCC 中爪幻,就廣泛使用了 SIMD 技術(shù)菱皆。MegCC是曠視天元團(tuán)隊(duì)開(kāi)發(fā)的深度學(xué)習(xí)編譯器,其接受 MegEngine 格式的模型為輸入笔咽,輸出運(yùn)行該模型所需的所有 kernel搔预,方便模型部署,具有高性能和輕量化的特點(diǎn)叶组。為了方便用戶將其它格式的模型轉(zhuǎn)換為 MegEngine 格式模型拯田,曠視天元團(tuán)隊(duì)同時(shí)提供了模型轉(zhuǎn)換工具 MgeConvert,您可以將模型轉(zhuǎn)換為 onnx甩十,然后使用 MgeConvert 轉(zhuǎn)換為 MegEngine 格式模型船庇。同時(shí)如果您想測(cè)試您設(shè)備上某條指令的吞吐和延遲,以指導(dǎo)您的優(yōu)化侣监,可以使用 MegPeak鸭轮。

MegCC 中實(shí)現(xiàn)了許多高性能的深度學(xué)習(xí)算子,卷積和矩陣乘法是典型的計(jì)算密集型的算子橄霉,同時(shí)卷積也可以借助矩陣乘法來(lái)實(shí)現(xiàn)(im2col/winograd 算法等)窃爷。

MegCC 在 ARM 平臺(tái)支持了 NEON DOTI8MM 指令實(shí)現(xiàn)的矩陣乘和卷積。一條 DOT 指令可完成 32 次乘加運(yùn)算(16 次乘法和 16 次加法運(yùn)算)姓蜂;一條 I8MM 指令可完成 64 次乘加運(yùn)算(32 次乘法和 32 次加法運(yùn)算)按厘。這就是 SIMD 技術(shù)能夠加速計(jì)算的原理。

參考資料

  1. Randal E. Bryant, David R. O’Hallaron. Computer Systems: A Programmer’s Perspective, Chapter 5.

  2. Antonio González, Fernando Latorre, Grigorios Magklis. Processor Microarchitecture: An Implementation Perspective, Chapter 1.

  3. https://github.com/MegEngine/MegCC

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末钱慢,一起剝皮案震驚了整個(gè)濱河市逮京,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌束莫,老刑警劉巖懒棉,帶你破解...
    沈念sama閱讀 216,591評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件草描,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡策严,警方通過(guò)查閱死者的電腦和手機(jī)穗慕,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,448評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)享钞,“玉大人揍诽,你說(shuō)我怎么就攤上這事±跏” “怎么了暑脆?”我有些...
    開(kāi)封第一講書人閱讀 162,823評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)狐肢。 經(jīng)常有香客問(wèn)我添吗,道長(zhǎng),這世上最難降的妖魔是什么份名? 我笑而不...
    開(kāi)封第一講書人閱讀 58,204評(píng)論 1 292
  • 正文 為了忘掉前任碟联,我火速辦了婚禮,結(jié)果婚禮上僵腺,老公的妹妹穿的比我還像新娘鲤孵。我一直安慰自己,他們只是感情好辰如,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,228評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布普监。 她就那樣靜靜地躺著,像睡著了一般琉兜。 火紅的嫁衣襯著肌膚如雪凯正。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書人閱讀 51,190評(píng)論 1 299
  • 那天豌蟋,我揣著相機(jī)與錄音廊散,去河邊找鬼。 笑死梧疲,一個(gè)胖子當(dāng)著我的面吹牛允睹,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播幌氮,決...
    沈念sama閱讀 40,078評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼缭受,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了浩销?” 一聲冷哼從身側(cè)響起贯涎,我...
    開(kāi)封第一講書人閱讀 38,923評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤听哭,失蹤者是張志新(化名)和其女友劉穎慢洋,沒(méi)想到半個(gè)月后塘雳,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,334評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡普筹,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,550評(píng)論 2 333
  • 正文 我和宋清朗相戀三年败明,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片太防。...
    茶點(diǎn)故事閱讀 39,727評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡妻顶,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出蜒车,到底是詐尸還是另有隱情讳嘱,我是刑警寧澤,帶...
    沈念sama閱讀 35,428評(píng)論 5 343
  • 正文 年R本政府宣布酿愧,位于F島的核電站沥潭,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏嬉挡。R本人自食惡果不足惜钝鸽,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,022評(píng)論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望庞钢。 院中可真熱鬧拔恰,春花似錦、人聲如沸基括。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 31,672評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)阱穗。三九已至饭冬,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間揪阶,已是汗流浹背昌抠。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 32,826評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留鲁僚,地道東北人炊苫。 一個(gè)月前我還...
    沈念sama閱讀 47,734評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像冰沙,于是被迫代替她去往敵國(guó)和親侨艾。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,619評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容