亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

std :: function vs template

std :: function vs template

C++
慕村9548890 2019-11-05 14:41:55
感謝C ++ 11,我們獲得了std::function函子包裝器系列。不幸的是,我一直只聽到關于這些新功能的壞消息。最受歡迎的是,它們運行緩慢。我測試了一下,與模板相比,它們確實很爛。#include <iostream>#include <functional>#include <string>#include <chrono>template <typename F>float calc1(F f) { return -1.0f * f(3.3f) + 666.0f; }float calc2(std::function<float(float)> f) { return -1.0f * f(3.3f) + 666.0f; }int main() {    using namespace std::chrono;    const auto tp1 = system_clock::now();    for (int i = 0; i < 1e8; ++i) {        calc1([](float arg){ return arg * 0.5f; });    }    const auto tp2 = high_resolution_clock::now();    const auto d = duration_cast<milliseconds>(tp2 - tp1);      std::cout << d.count() << std::endl;    return 0;}111毫秒和1241毫秒。我想這是因為模板可以很好地內聯,而function封面通過虛擬呼叫的內部。顯然,模板在我看來有其問題:他們的頭是不是你可能不希望釋放你的庫作為一個封閉的代碼時做的提供,除非extern template引入類似政策,否則它們可能會使編譯時間更長。沒有(至少在我的面前)代表要求的清潔方式(概念,任何人嗎?)模板,扎描述什么樣的函子有望評論。因此,我可以假定functions可以用作傳遞函子的事實上的標準,并且應該在期望使用高性能模板的地方使用嗎?
查看完整描述

3 回答

?
開滿天機

TA貢獻1786條經驗 獲得超13個贊

一般來說,如果你正面臨著一個設計情況,給你一個選擇,使用模板。我之所以強調“ 設計 ”一詞,是因為我認為您需要關注的是的用例std::function和模板之間的區別,兩者之間有很大的不同。


通常,模板的選擇只是更廣泛原則的一個實例:嘗試在編譯時指定盡可能多的約束。理由很簡單:如果你能趕上一個錯誤,或者類型不匹配,產生程序,甚至之前,你將不會運送一個錯誤的程序給客戶。


此外,當你正確地指出,調用靜態解析模板函數(即在編譯時),所以編譯器具有所有必要的信息,以優化和可能的內聯的代碼(如果呼叫是通過執行這將是不可能的vtable)。


是的,確實模板支持不是完美的,并且C ++ 11仍然缺少對概念的支持;這是對的。但是,我看不出如何std::function在這方面為您省錢。std::function不能替代模板,而是用于無法使用模板的設計情況的工具。


當您需要在運行時通過調用遵循特定簽名但在編譯時未知其具體類型的可調用對象來解決調用時,就會出現這種用例。當您具有可能不同類型的回調的集合,但是需要統一調用時,通常會出現這種情況;注冊的回調的類型和數量是在運行時根據程序的狀態和應用程序邏輯確定的。這些回調中的某些可能是函子,某些可能是簡單的函數,某些可能是將其他函數綁定到某些參數的結果。


std::function并且std::bind還提供了一個自然的習慣用法,用于啟用C ++中的函數式編程,其中將函數視為對象,并自然進行咖喱化和組合以生成其他函數。盡管也可以通過模板來實現這種組合,但是類似的設計情況通常伴隨用例,這些用例需要在運行時確定組合的可調用對象的類型。


最后,還有其他一些std::function不可避免的情況,例如,如果您要編寫遞歸lambda;但是,這些限制更多是由技術限制決定的,而不是我認為是概念上的區別。


綜上所述,專注于設計并嘗試了解這兩種構造的概念性用例是什么。如果按照您做的方式將它們進行比較,則會迫使他們進入他們可能不屬于的競技場。


查看完整回答
反對 回復 2019-11-05
?
飲歌長嘯

TA貢獻1951條經驗 獲得超3個贊

使用Clang,兩者之間沒有性能差異

使用clang(3.2,trunk 166872)(在Linux上為-O2),兩種情況下的二進制文件實際上是相同的。


-我將在帖子結尾再說一遍。但首先,gcc 4.7.2:


已經有了很多洞察力,但是我想指出,由于內聯等原因,calc1和calc2的計算結果不相同。例如,比較所有結果的總和:


float result=0;

for (int i = 0; i < 1e8; ++i) {

  result+=calc2([](float arg){ return arg * 0.5f; });

}

與calc2成為


1.71799e+10, time spent 0.14 sec

而使用calc1時


6.6435e+10, time spent 5.772 sec

這是速度差的約40倍,值的是約4倍。第一個是與OP發布的內容(使用Visual Studio)相比,差異要大得多。實際上,最終輸出值也是防止編譯器刪除無可見結果的代碼的一個好主意(按規則)??ㄎ鳉W·內里(Cassio Neri)已經在回答中說了這一點。請注意結果有何不同-比較執行不同計算的代碼的速度因子時應格外小心。


同樣,公平地說,比較重復計算f(3.3)的各種方式可能并不那么有趣。如果輸入是恒定的,則不應循環。(優化程序很容易注意到)


如果我將用戶提供的value參數添加到calc1和2,則calc1和calc2之間的速度因數將從40降為5!使用Visual Studio時,差異接近2倍,而使用lang聲時則沒有差異(請參見下文)。


另外,由于乘法運算很快,談論減速的因素通常并不那么有趣。一個更有趣的問題是,您的函數有多???這些調用是否成為實際程序中的瓶頸?


鐺:

當我在示例代碼的calc1和calc2之間翻轉時,Clang(我使用3.2)實際上產生了相同的二進制文件(如下所述)。在問題中張貼原始示例的情況下,兩者也是相同的,但根本不需要時間(如上所述,循環已被完全刪除)。在我修改的示例中,使用-O2:


要執行的秒數(最好為3):


clang:        calc1:           1.4 seconds

clang:        calc2:           1.4 seconds (identical binary)


gcc 4.7.2:    calc1:           1.1 seconds

gcc 4.7.2:    calc2:           6.0 seconds


VS2012 CTPNov calc1:           0.8 seconds 

VS2012 CTPNov calc2:           2.0 seconds 


VS2015 (14.0.23.107) calc1:    1.1 seconds 

VS2015 (14.0.23.107) calc2:    1.5 seconds 


MinGW (4.7.2) calc1:           0.9 seconds

MinGW (4.7.2) calc2:          20.5 seconds 

所有二進制文件的計算結果都相同,并且所有測試都在同一臺計算機上執行。如果有更深的clang或VS知識的人可以評論可能已經進行了哪些優化,那將很有趣。


我修改的測試代碼:

#include <functional>

#include <chrono>

#include <iostream>


template <typename F>

float calc1(F f, float x) { 

  return 1.0f + 0.002*x+f(x*1.223) ; 

}


float calc2(std::function<float(float)> f,float x) { 

  return 1.0f + 0.002*x+f(x*1.223) ; 

}


int main() {

    using namespace std::chrono;


    const auto tp1 = high_resolution_clock::now();


    float result=0;

    for (int i = 0; i < 1e8; ++i) {

      result=calc1([](float arg){ 

          return arg * 0.5f; 

        },result);

    }

    const auto tp2 = high_resolution_clock::now();


    const auto d = duration_cast<milliseconds>(tp2 - tp1);  

    std::cout << d.count() << std::endl;

    std::cout << result<< std::endl;

    return 0;

}

更新:


新增了vs2015。我還注意到calc1,calc2中有double-> float轉換。刪除它們并不會改變Visual Studio的結論(兩者都快很多,但是比率大致相同)。


查看完整回答
反對 回復 2019-11-05
  • 3 回答
  • 0 關注
  • 524 瀏覽

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號