日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > c/c++ >内容正文

c/c++

C++的高效从何而来

發布時間:2023/12/10 c/c++ 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 C++的高效从何而来 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

2019獨角獸企業重金招聘Python工程師標準>>>

前一段時間,實驗室的一哥們突然跑過來跟我說,“我自己寫了個C的快速排序,排了一個10000000個int的數組,貌似比C庫中是qsort算法要快,咋回事?C++的STL中快排(quick sort)算法的效率如何?”。

聽他這么一說,我就立即做了個實驗,寫了如下代碼:

<!-- lang: cpp --> #include <iostream> #include <algorithm> #include <time.h>using namespace std;#define MAX_LEN 10000000int arri[MAX_LEN];int compare(const void* i, const void* j) {return (*(int*)i - *(int*)j); }int main() {for (int i = 0; i < MAX_LEN; i++) {arri[i] = rand() % MAX_LEN;}::clock_t start, finish;//STL sortstart = ::clock();sort(arri, arri + MAX_LEN);finish = ::clock();cout << "STL sort:\t" << finish - start << "ms" << endl;for (int i = 0; i < MAX_LEN; i++) {arri[i] = rand() % MAX_LEN;}//C qsortstart = ::clock();qsort(arri, MAX_LEN, sizeof(arri[0]), compare);finish = ::clock();cout << "C qsort:\t\t" << finish - start << "ms" << endl;return 0; }

我機器上裝的是CodeBlocks(10.05)+MinGW(4.7.2)的環境。

首先,在debug模式下,CodeBlocks顯示出當前的編譯器命令:

<!-- lang: shell --> mingw32-g++.exe -Wall -fexceptions -g -std=c++0x -c D:\Workspaces\CodeBlocks\TestSortQ\main.cpp -o obj\Debug\main.o mingw32-g++.exe -o bin\Debug\TestSortQ.exe obj\Debug\main.o

運行結果:

運行結果讓我大吃一驚,STL不可能這么慢啊!后來仔細一想,這是debug模式,沒有加任何優化,加上優化看看什么結果:

<!-- lang: shell --> mingw32-g++.exe -Wall -fexceptions -O2 -std=c++0x -c D:\Workspaces\CodeBlocks\TestSortQ\main.cpp -o obj\Release\main.o mingw32-g++.exe -o bin\Release\TestSortQ.exe obj\Release\main.o -s

運行結果:

果然,O2選項一加上,STL sort瞬間完成了逆襲,運行時間優化了75%,而C qsort優化前后變化不是很明顯,大概減少了10%。

問題來了,為什么C++標準庫的快排的優化效果如此明顯,而C庫的快排優化不是很明顯呢?

答案是inline。

我們知道,STL是泛型編程的杰出成果,里面的容器、迭代器、算法幾乎都是通過泛型實現的,使得STL的通用性很強。泛型編程的一個負面效果就是破壞了接口與實現的分離,即頭文件中聲明,源文件中實現,源文件單獨編譯成庫,用戶只需要拿到頭文件和庫就可以使用了,看不到具體實現,這就是所謂的ABI,也是C的傳統做法。有人會問,為什么不能做到接口與實現的分離,因為泛型編程中的函數和類,在沒有接受一個模版參數之前,是沒辦法實例化的,只有當用戶給定了模版參數的時候,編譯器才會去實例化一個具體的類或函數。

如,C++ STL中的快速排序算法定義:

<!-- lang: cpp --> template< class RandomIt > void sort( RandomIt first, RandomIt last );

只有RandomIt這個類型真正確定了,編譯器才會去實例化這個方法。在上面的代碼中,我這么寫:

<!-- lang: cpp --> sort(arri, arri + MAX_LEN);

編譯器通過自動類型推導,知道了RandomIt其實是一個int*,于是產生這個函數:

<!-- lang: cpp --> sort(int*, int*);

具體實現中的RandomIt已經都替換成相應的int*,一個完整的函數就產生了。

關鍵的問題出現了!編譯器在實例化一個函數(類也一樣)的時候,它必須知道具體的實現代碼,才能夠產生完整的函數。這也是為什么如果大家自己寫模版的時候,.h文件和.cpp文件的關系變得十分奇怪的原因,一般做飯是在.h文件末尾,#include xx.cpp,其中xx.cpp中實現了.h文件中的函數聲明,或者干脆直接在.h文件中寫實現。否則,如果按照一般的.h和.cpp的關系,編譯器會報錯,說找不到函數的實現。寫過模版的程序猿應該都知道這個。

事實上,C++標準委員會為了解決這個問題,曾經引入了export關鍵字,來試圖解決這個問題,但很少有編譯器實現了(估計是實現難度較大,且增加了復雜度,得不償失),所以這個關鍵字后來基本上費了。

大家或許會問,你是不是走題了,剛開始不是討論C++的效率問題嗎?怎么說了半天泛型和模版的事情了?

答案是,真是由于泛型的這個“副作用”,使得編譯器可以做更多的優化!

既然編譯器知道具體的實現,那么inline是編譯器可以在優化上大顯身手的一個手段,sort函數中需要一個compare函數(在C++中還可以通過函數對象或者操作符重載實現)來知道如何比較兩個元素的大小,sort函數每次比較的時候,都會調用這個函數。對于一個10000000個元素的數組,一共會調用多少次這個compare函數是可想而知的(具體數目可以算出來),而一次函數調用的開銷比較大,如棧的分配等等,這就很大程度上限制了C庫中的qsort的威力,因為qsort的實現是在編譯在庫中的,它所調用的函數就沒法inline到qsort函數里面去。但是STL是可以做到的,所以它的優化效果非常明顯。

另外一個STL sort效率高的原因,在于算法的實現,不僅僅是快速排序算法,估計這也是為什么名字叫sort而不叫qsort的原因吧。在SGI STL(GNU所使用的STL)的實現中,sort函數一共采用了三種排序算法,分別是quick sort,heap sort和insert sort。使用策略如下:

1、函數主體為quick sort,但是在遞歸調用的時候,加上了一個參數記錄迭代層數,如果迭代次數超過一定數目,轉而采用heap sort。原因是如果迭代次數過多,很可能意味著quick sort落入了最壞情況(O(n2)),而heap sort的最壞情況依然是O(nlogn)。

2、當數組劃分到很小的一段時,采用insert sort。原因是對于小數據量采用quick sort有些不劃算(quick sort適合處理大量數據),因為quick sort本身是遞歸的,遞歸就是一次函數調用,開銷較大。而insert sort在較小數據量的情況下,表現很好。

具體算法可參考SGI STL和侯捷的《STL源碼剖析》。

說了這么一大堆,其實想表達的一點就是,C++為了提高效率,可以說無所不用其極,無論是STL算法實現,還是編譯器的優化(語言本身為了編譯器能做優化也下了很多功夫),都體現了C++的三大設計思想(或者叫做約束,可參考孟巖《關于C++復雜性的零碎思考》)之一:最高性能。

回到那位哥們的問題,為什么他的快排效率比C庫的要高?我不否認他的水平,但是我感覺最大的原因還是,自己寫的函數,編譯器可以將compare的功能內斂到函數里面去,所以效率比C庫的qsort效率要高。

關于C++的高效,我還想繼續寫一些文章,這篇博客且當作一個開始吧。

轉載于:https://my.oschina.net/chen0dgax/blog/156156

總結

以上是生活随笔為你收集整理的C++的高效从何而来的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。