偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

性能優(yōu)化利器之Constexpr

開(kāi)發(fā) 開(kāi)發(fā)工具
說(shuō)明符constexpr?是自C++11引入,我相信很多人跟我一樣,在第一次接觸這個(gè)的時(shí)候,會(huì)很容易和const混淆。

你好,我是雨樂(lè)!

最近在升級(jí)系統(tǒng)和進(jìn)行一些性能優(yōu)化,業(yè)余時(shí)間也看一些技術(shù)書(shū)籍和視頻,看了下上次更新文章的時(shí)間,大致在一個(gè)月前了,確實(shí)有點(diǎn)久了,所以趕緊拾起來(lái),不能讓大伙忘了我不是??。

今天,聊聊在升級(jí)過(guò)程中的一個(gè)比較重要的優(yōu)化點(diǎn)-編譯期優(yōu)化。

概述

說(shuō)明符constexpr是自C++11引入,我相信很多人跟我一樣,在第一次接觸這個(gè)的時(shí)候,會(huì)很容易和const混淆。

從概念上理解的話,constexpr即常量表達(dá)式,重點(diǎn)在表達(dá)式字段,用于指定變量或函數(shù)可以在常量表達(dá)式中使用,可以(或者說(shuō)一定)在編譯時(shí)求值的表達(dá)式,而const則為了約束變量的訪問(wèn)控制,表示運(yùn)行時(shí)不可以直接被修改,其往往可以在編譯期和運(yùn)行時(shí)進(jìn)行初始化。

前面提到了constexpr是在編譯階段進(jìn)行求值,那么也就是說(shuō)在程序運(yùn)行之前,就已經(jīng)計(jì)算完成,這種無(wú)疑大大提升了程序的運(yùn)行效率。因此提升運(yùn)行效率就是C++11引入constexpr說(shuō)明符的目的,也就是說(shuō)能在編譯階段做的事情就絕不放在運(yùn)行期做。

變量

代碼如下:

example1.cc

int main() {
    const int val = 1 + 2;
    return 0;
}

上述代碼匯編結(jié)果如下:

main:
        push    rbp
        mov     rbp, rsp
        mov     DWORD PTR [rbp-4], 3
        mov     eax, 0
        pop     rbp
        ret

從上述匯編結(jié)果可以看出,在編譯階段就將val賦值成3,也就是說(shuō)在編譯階段完成了求值操作。

再看另外一個(gè)示例2:

example2.cc

int Add(const int a, const int b) {
    return a + b;
}

int main() {
    const int val = Add(1, 2);
    return 0;
}

同樣的,其匯編如下:

Add(int, int):
        push    rbp
        mov     rbp, rsp
        mov     DWORD PTR [rbp-4], edi
        mov     DWORD PTR [rbp-8], esi
        mov     edx, DWORD PTR [rbp-4]
        mov     eax, DWORD PTR [rbp-8]
        add     eax, edx
        pop     rbp
        ret
main:
        push    rbp
        mov     rbp, rsp
        sub     rsp, 16
        mov     esi, 2
        mov     edi, 1
        call    Add(int, int)
        mov     DWORD PTR [rbp-4], eax
        mov     eax, 0
        leave
        ret

分析上述匯編,發(fā)現(xiàn)并沒(méi)有在編譯階段進(jìn)行求值,所以也就是說(shuō)上述的求值過(guò)程將會(huì)延后至編譯期進(jìn)行。

好了,既然示例一(使用const)可以在編譯期進(jìn)行求值,而constexpr也可以在編譯期求值,那么直接用constexpr替換示例一種的const是否可行?

example3.cc

int main() {
    constexpr int val = 1 + 2;
    return 0;
}

接著看下匯編代碼:

main:
        push    rbp
        mov     rbp, rsp
        mov     DWORD PTR [rbp-4], 3
        mov     eax, 0
        pop     rbp
        ret

呃??,與示例一完全一樣。。。

在上面示例2中,通過(guò)匯編代碼發(fā)現(xiàn)其是在運(yùn)行期求值,那么有沒(méi)有辦法在編譯期求值呢?那就是使用constexpr表達(dá)式:

example4.cc

constexpr int Add(const int a, const int b) {
    return a + b;
}

int main() {
    const int val = Add(1, 2);
    return 0;
}

匯編如下:

main:
        push    rbp
        mov     rbp, rsp
        mov     DWORD PTR [rbp-4], 3
        mov     eax, 0
        pop     rbp
        ret

有沒(méi)有發(fā)現(xiàn)很眼熟,對(duì),跟示例1和示例3的結(jié)果一樣,該代碼較示例2的唯一區(qū)別是多了個(gè)constexpr說(shuō)明符,但將求值時(shí)期從運(yùn)行期放到了編譯期,可想而知,效率提升那是杠杠的。。。??

函數(shù)

constexpr也可以修飾普通函數(shù)或者成員函數(shù),其實(shí)這塊在上一節(jié)已經(jīng)有提過(guò),示例如下:

constexpr int Add(const int a, const int b) {
    return a + b;
}

int main() {
    const int val = Add(1, 2);
    int val1 = 3;
    int val2 = Add(val, val1);
    return 0;
}

匯編如下:

Add(int, int):
        push    rbp
        mov     rbp, rsp
        mov     DWORD PTR [rbp-4], edi
        mov     DWORD PTR [rbp-8], esi
        mov     edx, DWORD PTR [rbp-4]
        mov     eax, DWORD PTR [rbp-8]
        add     eax, edx
        pop     rbp
        ret
main:
        push    rbp
        mov     rbp, rsp
        sub     rsp, 16
        mov     DWORD PTR [rbp-4], 3
        mov     DWORD PTR [rbp-8], 3
        mov     eax, DWORD PTR [rbp-8]
        mov     esi, eax
        mov     edi, 3
        call    Add(int, int)
        mov     DWORD PTR [rbp-12], eax
        mov     eax, 0
        leave
        ret

從上述匯編代碼可以看出,val的求值是在編譯階段,而val2的求值則是在運(yùn)行階段,這是因?yàn)槠湟肓艘粋€(gè)非const變量val1。

通過(guò)本示例,可以看出,將函數(shù)聲明為constexpr可以提示效率,讓編譯器來(lái)決定是在編譯階段還是運(yùn)行階段來(lái)進(jìn)行求值,當(dāng)然了,如果想了解在編譯階段求值的各種細(xì)節(jié)規(guī)則,請(qǐng)參考constexpr in cppreference。

if語(yǔ)句

如果您目前使用C++11進(jìn)行編碼,那么需要仔細(xì)閱讀本節(jié),這樣可以為將來(lái)的版本升級(jí)打好基礎(chǔ);如果您正在使用C++17進(jìn)行編碼,那么更得閱讀本節(jié),相信讀完本節(jié)后,會(huì)有一個(gè)不一樣的認(rèn)識(shí)??。

自C++17起,引入了if constexpr語(yǔ)句,在本節(jié)中,將借助SFINAE 和 std::enable_if來(lái)實(shí)現(xiàn)一個(gè)簡(jiǎn)單的Square功能,最后借助if constexpr對(duì)代碼進(jìn)行優(yōu)化(如果對(duì)SFINAE 和 std::enable_if不是很了解的,建議自行閱讀哈)。

如果有個(gè)需求,實(shí)現(xiàn)一個(gè)Add函數(shù),其既支持算術(shù)類(lèi)型又支持用戶自定義類(lèi)型:

template <typename T>
struct Number {
    Number(const T& _val) :
        value(_val) {}

    T value;
};

template<typename T>
T Square(const T& t) {
    return t + t;
}

int main() {
  int i = 5;
  float f = 5.0;
  bool b = true;
  Number<int> n(5);

  auto res = Square(i); // 調(diào)用int Add(int);
  auto res2 = Square(f); // 調(diào)用 float Add(float);
  auto res3 = Square(b);  // call bool Square(bool);
  auto res4 = Square(n); //編譯失敗,因?yàn)镹umber<>沒(méi)有提供operator*操作
}

上述代碼編譯出錯(cuò),因?yàn)镹umber<>沒(méi)有提供operator*操作,所以這個(gè)時(shí)候第一個(gè)想法是修改Square函數(shù),如下:

template<typename T>
T Square(const T& t) {
    if (std::is_arithmetic<T>::value) {
        return t * t;
    } else {
        return t.value * t.value;
    }
}

在上述代碼中,如果T是算數(shù)類(lèi)型,則直接進(jìn)行*操作,否則取其value進(jìn)行*操作。

將上述代碼進(jìn)行編譯,報(bào)錯(cuò)如下:

example5.cc: In instantiation of ‘T Square(const T&) [with T = int]’:
example5.cc:26:20:   required from here
example5.cc:16:18: error: request for member ‘value’ in ‘t’, which is of non-class type ‘const int’
   16 |         return t.value * t.value;
      |                ~~^~~~~
example5.cc:16:28: error: request for member ‘value’ in ‘t’, which is of non-class type ‘const int’
   16 |         return t.value * t.value;
      |                          ~~^~~~~
....

以Square(i)為例,這是因?yàn)樵诰幾g的時(shí)候,會(huì)嘗試int.value操作,顯然int.value不存在,這就導(dǎo)致了上述的錯(cuò)誤輸出,為了更為清楚的顯示本錯(cuò)誤,將Square()修改如下:

int Square(const int& t) {
    if (true) {
        return t * t;
    } else {
        return t.value * t.value;
    }
}

這樣就能很清楚的知道為什么編譯失敗了,因?yàn)樵诖a中存在t.value * t.value操作,而對(duì)于一個(gè)int來(lái)說(shuō)并沒(méi)有value這個(gè)變量,所以編譯失敗。

為了解決這個(gè)問(wèn)題,我們嘗試引入std::enable_if操作,如下:

template<typename T>
typename std::enable_if<std::is_arithmetic<T>::value, T>::type Square(const T& t) {
    return t * t;
}

template<typename T>
typename std::enable_if<! std::is_arithmetic<T>::value, T>::type Square(const T& t) {
    return t.value * t.value;
}

現(xiàn)在有兩個(gè)函數(shù)模板,如果是算術(shù)類(lèi)型,則調(diào)用第一個(gè),否則調(diào)用第二個(gè),完整代碼如下:

#include <type_traits>

template<typename T>
typename std::enable_if<std::is_arithmetic<T>::value, T>::type Square(const T& t) {
    return t * t;
}

template<typename T>
typename std::enable_if<! std::is_arithmetic<T>::value, T>::type Square(const T& t) {
    return t.value * t.value;
}

template <typename T>
struct Number {
    Number(const T& _val) :
        value(_val) {}

    T value;
};

int main() {
  int i = 5;
  float f = 5.0;
  bool b = true;
  Number<int> n(5);

  auto res = Square(i); // 調(diào)用int Add(int);
  auto res2 = Square(f); // 調(diào)用 float Add(float);
  auto res3 = Square(b);  // call bool Square(bool);
  auto res4 = Square(n); // 成功
  
  return 0;
}

上述代碼編譯成功。

在上述代碼中,為了編譯成功,我們引入了兩個(gè)Square()模板函數(shù)借助std::enable_if來(lái)實(shí)現(xiàn),代碼上多少有點(diǎn)冗余,在這個(gè)時(shí)候,本節(jié)的主角if constexpr 出場(chǎng),完整代碼如下:

#include <type_traits>
template<typename T>
T Square(const T& t) {
    if constexpr (std::is_arithmetic<T>::value) {
        return t * t;
    } else {
        return t.value * t.value;
    }
}

template <typename T>
struct Number {
    Number(const T& _val) :
        value(_val) {}

    T value;
};

int main() {
  int i = 5;
  float f = 5.0;
  bool b = true;
  Number<int> n(5);

  auto res = Square(i); // 調(diào)用int Add(int);
  auto res2 = Square(f); // 調(diào)用 float Add(float);
  auto res3 = Square(b);  // call bool Square(bool);
  auto res4 = Square(n); // 成功
  
  return 0;
}

編譯成功。

我們借助一個(gè)Square()函數(shù)模板以及更加符合編碼習(xí)慣的if語(yǔ)句就能解決上面的問(wèn)題,且比使用std::enable_if方式更為優(yōu)雅和符合閱讀習(xí)慣,進(jìn)而提高代碼的可閱讀性。

責(zé)任編輯:武曉燕 來(lái)源: 高性能架構(gòu)探索
相關(guān)推薦

2020-06-10 10:40:03

JavaJMH字符串

2021-07-29 14:20:34

網(wǎng)絡(luò)優(yōu)化移動(dòng)互聯(lián)網(wǎng)數(shù)據(jù)存儲(chǔ)

2022-02-16 14:10:51

服務(wù)器性能優(yōu)化Linux

2021-11-29 11:13:45

服務(wù)器網(wǎng)絡(luò)性能

2024-04-03 09:12:03

PostgreSQL索引數(shù)據(jù)庫(kù)

2009-06-30 11:23:02

性能優(yōu)化

2018-01-09 16:56:32

數(shù)據(jù)庫(kù)OracleSQL優(yōu)化

2019-12-13 10:25:08

Android性能優(yōu)化啟動(dòng)優(yōu)化

2011-07-11 15:26:49

性能優(yōu)化算法

2013-02-20 14:32:37

Android開(kāi)發(fā)性能

2025-01-20 09:09:59

2013-09-17 10:32:08

Android性能優(yōu)化數(shù)據(jù)庫(kù)

2021-07-16 23:01:03

SQL索引性能

2011-06-14 14:17:23

性能優(yōu)化系統(tǒng)層次

2011-06-14 11:14:10

性能優(yōu)化代碼

2023-04-10 11:18:38

前端性能優(yōu)化

2011-06-14 14:32:46

性能優(yōu)化

2017-08-25 15:28:20

Oracle性能優(yōu)化虛擬索引

2023-11-01 17:57:56

React應(yīng)用程序性能

2017-01-19 19:07:28

iOS進(jìn)階性能優(yōu)化
點(diǎn)贊
收藏

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