標籤:

constexpr對編譯時間影響大嗎?

在編譯期求值本身得耗時間吧,C++14允許在constexpr函數裡面放置for loop了,這個對編譯時間影響大嗎?


會有影響,但是影響多大呢,我覺得要看編譯器的實現,這個再怎麼大,直覺上應該不會比模板帶來的編譯時間長。不過constexpr很難做,我今天就來解釋一下吧。

首先,我想說,誰知道知乎怎麼開通專欄???每次一碰到這樣的好問題,我就想寫文章,如果有專欄,我一下就連接過去了,不用想著要去博客或者公眾號去搞了(博客編譯器教程的代碼我已經開始寫了,不要催我了哈,我知道你們又要來說這個,最近事情是很多)。

首先我想講講C++11的constexpr會有那麼多限制,然後C++14逆天的放鬆了要求。這篇提案出來的時候,大約2013年的時候,我們團隊就已經討論過如何實現C++14放鬆的constexpr,我們當時就說這篇提案真逆天,我們都覺得可能需要抽象出來一個虛擬機來做了,然後我現在也暫時不知道Clang怎麼做的,我後面肯定會去看Clang的代碼怎麼搞的,我很好奇。

對於constexpr我就不細細說了,請直接看鏈接:constexpr specifier (since C++11),簡單的提一句就說,constexpr聲明的會在編譯期間就搞定,而非放在運行期間。而constexpr不僅可以用於變數,還可以用於函數。由於編譯期間就計算好了,導致最後的代碼可以提高運行效率,而提高的運行效率我曾在這個回答中列舉過一個圖:constexpr和const數組的區別? - 藍色的回答,大家可以點擊過去查看。

對於C++編譯器來說,它不是一個虛擬機,它的主要任務就是把高級代碼翻譯成低級代碼,這其實應該就是它的本職工作,它也沒有義務來知道如何來執行你的代碼,如constexpr一樣,把你的代碼執行出一個結果,這其實本不應該屬於它的工作。但是呢,為了提高代碼的執行效率,我們會有優化器,也會做現在constexpr的類似工作,如constant folding, inlining等。這時候是不是想著constexpr也好做?其實不然。優化器實質需要在低級層次來做,所需要的一般就是IR,如Clang產生的就是LLVM IR。而逆天的constexpr是需要完全在C++前端就搞定,優化器的那一套用不到,他需要在一個比較高的層次來搞定,那麼自然我們想的是虛擬機的思想,而我覺得constexpr的本質其實就是在一個比較高的層次來向編譯器求一個虛擬機出來,然後抽象的執行結果。

所以,如果打破了很多限制,如不限制函數只能返回一條語句這樣的,那這個就基本上是直接往完整虛擬機的節奏一路狂奔的節奏,所幸有這樣的限制讓編譯器的實現要容易一些了(看向微軟的VC++ Compiler,最近VC++ 2015終於開始支持C++11的constexpr了,不容易啊)。

那麼,怎麼來實現這個呢?Hmm...我目前還沒有看Clang這部分代碼,但是如果要做到簡單Case的constexpr,其實逐步降低語法樹可能是一個方法(我們編譯器的實現我沒有看,Clang的也沒有看,GCC的也沒有看,這部分純屬我在猜測可以怎麼實現,特此聲明

考慮以下代碼

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

int main()
{
static const int value = foo(4 * sizeof(int), 5);
};

這個么,就是這樣的(這裡我默認是32位,int大小是4)

有沒有發現什麼特點?那就是所有的葉子節點都是常量,這就給了我們常量合併帶來了可能。

而你說編譯器裡面怎麼來分解呢?我猜想可能有這樣的東西,如clang_eval( ASTExpr, Variable s),clang_call(function, params) 這樣就一步一步的執行,然後來做吧。如上文所說,純屬我猜測的實現方法,這也只能應付簡單的Case,真這麼簡單的話,再次看向微軟,直到VC++ 2015才終於實現出來,而你說類似clang_eval,clang_call這樣的東西以前就有沒有相應的一些東西呢?大家可以思考一下,比如模板參......


假如「有影響」是指變慢的話,答案是:不一定。

前陣很火的一篇關於編譯時間的文章就提到了一個神奇的情況。

http://aras-p.info/blog/2017/10/24/Slow-to-Compile-Table-Initializers/

改成constexpr之後編譯時間反而下降了。這是因為constexpr只需要在編譯期算出結果嵌入到代碼里即可。而如果不constexpr,則需要生成一段優化的、線程安全的代碼來計算結果,於是優化這段代碼需要的時間遠遠大於直接算結果所需的時間。

結論:能夠編譯期的東西就讓它編譯期吧。


貼一段代碼

#define BOUND 0xffffb
constexpr int foo(int a) {
int b = 1;
for (int k = 0; k &< a; k++); return b; } int t[foo(BOUND)]; int main() { return 0; }

用clang-3.4編譯可以編譯通過,將BOUND的值改成 0xffffc,無法編譯通過,原因是t數組大小是一個變數。但實際上foo的返回時一個常數1,這說明什麼呢?

Clang在處理constexpr時,在編譯期間用一個C++的計算器計算constexpr的值,並且記錄計算的次數。當次數超過一定值時,這個constexpr退化為非constexpr。因為有這個閾值的存在,所以constexpr在編譯時的計算次數是有上限的。這個上限就是對編譯時間的影響。

別的編譯器我猜差不多。


推薦閱讀:

想裸寫編譯器,除了編譯原理外還有那些資料可以參考?應該從什麼開始寫起?(用c/c++)?
關於typedef的疑問?
大學3年立志像輪子哥寫個編譯器,可能嗎?
Clang對EDG會有衝擊嗎?

TAG:編譯器 | C11 |