2009-07-25 48 views
10

模板Metaprogramming比等效的C代碼更快嗎? (我正在談論運行時性能):)模板Metaprogramming比等效的C代碼更快嗎?

+1

爲什麼Downvote? – n00ki3 2009-07-25 18:30:59

+1

你在問什麼? – 2009-07-25 18:33:01

+2

這得到了downvoted?真的嗎?這對我來說似乎是一個非常好的問題。 (雖然標題和問題的主體似乎提出了兩個不同的問題,但澄清你對哪一個感興趣可能是一個好主意) – jalf 2009-07-25 19:07:01

回答

25

首先,一個免責聲明: 你在問什麼不僅僅是模板元編程,還包括泛型編程。這兩個概念是密切相關的,對每個概念都沒有確切的定義。但簡而言之,模板元編程本質上是使用模板編寫程序,在編譯時進行評估。 (這使得它在運行時完全免費,沒有任何反應。值(或者更常見的是,類型)已經由編譯器計算出來,並且可以作爲常量(const變量或枚舉)或者嵌套在類中的typedef(如果您已經使用它「計算」一種類型)。

泛型編程使用模板,並在必要時使用模板元編程來創建泛型代碼,它可以與所有類型和任何類型相同(並且性能沒有損失)。我將在下面使用這兩個示例。

模板元編程的一個常見用途是使類型能夠在泛型編程中使用,即使它們不是爲它設計的。

由於模板元編程技術上完全是在編譯時發生的,因此您的問題與泛型編程有點相關,而泛型編程仍然在運行時發生,但效率很高,因爲它可以專用於精確類型編譯時間。

反正...


取決於你如何定義 「等價的C代碼」。

關於模板元編程(或一般泛型編程)的技巧是它允許將大量計算移動到編譯時,並且它實現了與硬編碼值一樣高效的靈活的參數化代碼。

顯示的代碼here例如在編譯時計算斐波那契數列。

C++代碼'unsigned long fib11 = fibonacci<11uL>::value'依賴於該鏈接中定義的模板元程序,並且與C代碼'unsigned long fib11 = 89uL'一樣高效。模板在編譯時進行評估,生成一個可以分配給變量的常量。所以在運行時,代碼實際上與簡單的賦值相同。

所以,如果這是「等效的C代碼」,性能是相同的。 如果等價的C代碼是「一個可以計算任意斐波那契數的程序,用於查找序列中的第11個數字」,那麼C版本將會慢得多,因爲它必須作爲一個函數來實現,該函數計算運行時的值。但是這是「等價的C代碼」,因爲它是一個具有相同靈活性的C程序(它不僅是一個硬編碼常量,而且是一個實際函數,可以返回斐波那契數列中的任何數字)。

當然,這並不常用。但它幾乎是模板元編程的典型例子。

泛型編程更實際的例子是排序。

在C中,您有qsort標準庫函數採用數組和比較函數指針。對這個函數指針的調用不能被內聯(除了微不足道的情況),因爲在編譯時,不知道哪個函數將被調用。

當然,另一種方法是爲您的特定數據類型設計的手寫排序功能。

在C++中,等價函數是函數模板std::sort。它也需要一個比較,但不是這是一個函數指針,它是一個函數對象,看起來像這樣:

struct MyComp { 
    bool operator()(const MyType& lhs, const MyType& rhs) { 
    // return true if lhs < rhs, however this operation is defined for MyType objects 
    } 
}; 

,這可以被內聯。函數std::sort傳遞一個模板參數,所以它知道比較器的確切類型,所以它知道比較函數不僅僅是一個未知的函數指針,而是MyComp::operator()

最終的結果是,在C++函數std::sort恰好一樣有效在相同的排序算法的C本手工編碼的實施方式。

如此反覆,如果是相當於「C代碼」,那麼性能是一樣的。 但是,如果「等價的C代碼」是「它可以被應用到任何類型廣義分類功能,並允許用戶定義比較器」,那麼通用編程版本在C++是大大更有效。

這真是個訣竅。泛型編程和模板元編程不是「比C更快」。他們要實現一般,可重用的代碼這是一樣快,手工編寫的,並硬編碼ç

這是一種方式來獲得兩全其美的方法。的硬編碼算法的性能,並且一般情況下,那些參數的靈活性和可重用性。

+0

jalf我在我的答案中使用了排序示例,所以+1對於C++的戰士:D – AraK 2009-07-25 19:37:34

+1

恩,我認爲泛型編程和TMP有很大的區別,但是我明白你引入了GP示例來表達你的觀點。不過,我會錯過提示比較器不是TMP。 – sbi 2009-07-25 20:43:51

+0

公平點。我在GP和TMP開始的時候加了一些解釋,並且提到排序的例子通常會被認爲是GP,但是我認爲我會放棄它。 – jalf 2009-07-25 21:03:09

11

模板Metaprogramming(TMP)在編譯時運行,因此在將其與普通的C/C++代碼進行比較時,並不真正比較蘋果和蘋果。

但是,如果你有一些TMP評估的東西,那麼根本沒有運行成本。

1

答案取決於。

模板元編程可用於輕鬆編寫遞歸下降語言解析器,與精心設計的C程序或基於表的實現(例如flex/bison/yacc)相比,這些效率可能很低。

在另一方面,你可以編寫生成展開的循環可能會比使用一個循環的更傳統的C實現更高效的元程序。

主要優點是元程序允許程序員用更少的代碼來做更多的事情。

不足之處在於,它也給了你一把槍,可以在腳下拍攝自己。

1

模板元編程可以被認爲是編譯時執行。

編譯時會需要更長的時間來編譯代碼,因爲它編譯,然後執行模板,生成代碼,然後重新編譯。

運行時開銷我不確定,它應該不會比你自己用C代碼編寫我想象的要多得多。

1

我曾參與過另一位程序員嘗試元編程的項目。這太可怕了。這是一個完整的頭痛。我是一個擁有豐富C++經驗的普通程序員,試圖設計他們想要做的事情,比他們直接寫出來的更容易。

因爲這個經驗我厭倦了C++ MetaProgramming。

我堅信最好的代碼是最容易被普通開發人員讀取的。這是軟件的可讀性是首要任務。我可以使用任何語言做任何事情...但是這項技能是爲了使項目的下一個人更易讀易操作。 C++ MetaProgramming未能通過測試。

+3

也許問題在於你的同事沒有充分記錄他的元編程代碼在做什麼?或者,他可能不擅長編寫可理解的代碼。無論如何,我不確定基於一個代碼庫的經驗譴責整個技術是公平的。 – 2009-07-25 19:17:46

+0

我同意傑里米。也許你的同事不善於編寫代碼,或者乾淨利落,但我認爲你不會閱讀它會導致錯誤。也許它*寫得很好,你只需要更多的模板經驗? – GManNickG 2009-07-25 19:29:46

+0

也許它自己的代碼並不能讓它自我被元編程! – AraK 2009-07-25 19:46:55

4

如果你的意思可重用代碼,則是毫無疑問的。元編程是生產圖書館的最好方式,而不是客戶代碼。客戶端代碼不是通用的,它被寫入做特定的事情。

例如,看看的qsort從C標準庫,和C++標準排序。這是快速排序如何作品:

int compare(const void* a, const void* b) 
{ 
    return (*(int*)a > *(int*)b); 
} 

int main() 
{ 
    int data[5] = {5, 4, 3, 2, 1}; 

    qsort(data, 5, sizeof(int), compare); 
} 

現在看排序

struct compare 
{ 
    bool operator()(int a, int b) 
    { return a < b; } 
}; 

int main() 
{ 
    int data[5] = {5, 4, 3, 2, 1}; 
    std::sort(data, data+5, compare()); 
} 

排序是更清潔,更安全,更高效,因爲比較函數的內內聯分類。這是元編程的好處在我看來,你寫通用代碼,但編譯器產生的代碼,如手工編碼一個


,我發現當你寫一個像庫的boost ::精神或升壓:: xpressive中,與精神你可以寫EBNF內C++,讓編譯檢查EBNF語法元編程非常美麗的另一個地方爲您和xpressive中你可以寫正則表達式,讓編譯器檢查的正則表達式語法也!


我不確定提問者是否意味着由TMP在編譯時計算值。這是我使用boost :)

unsigned long greatestCommonDivisor = boost::math::static_gcd<25657, 54887524>::value; 

你不能模仿你使用C做什麼都在上面的代碼寫了一個例子,基本上你必須手工計算則結果分配給greatestCommonDivisor

+0

這不是元程序設計。這只是模板。 – GManNickG 2009-07-25 19:38:00

+0

嗯,這是在編譯時計算類型:)它不一定是數值計算。 – AraK 2009-07-25 19:44:14

+0

從技術上講,它甚至不需要計算類型...... – GManNickG 2009-07-25 19:52:56

0

模板元編程在性能方面不會給你任何魔力。它基本上是一個非常複雜的預處理器;你可以隨時在C或C++中編寫相應的代碼,但這可能需要很長時間。