我正在爲一個函數生成C代碼的程序工作。這個生成的C函數駐留在另一個目標程序的中央循環中;此功能對性能非常敏感。生成的函數用於根據布爾值調用另一個函數 - 使用傳遞給生成函數的2個整數獲取此布爾值:一個狀態編號和一個模式編號。產生的函數看起來像這樣:執行預先計算的事實檢查的不同方法
void dispatch(System* system, int state, int mode) {
// Some other code here...
if (truthTable[state][mode]) {
doExpensiveCall(system, state, mode);
}
}
一些事實:
- 「國家」和範圍「模式」值從0開始,並在一些數量< 10000個終端。它們的可能值是連續的,兩者之間沒有差距。因此,例如,如果'狀態'的最終值是1000,那麼我們知道有1001個狀態(包括狀態0)。
- 代碼生成器知道狀態和模式,並且它知道狀態+模式的哪種組合會提前產生真值。理論上講,狀態+模式的任何組合都可以產生真正的價值,從而調用doExpensiveCall,但是在實踐中它將主要是少數狀態+模式組合,它們會產生真值。同樣,這些信息在代碼生成期間是已知的。
- 由於此函數將被稱爲很多,我想優化檢查真值。在平均情況下,我預計測試對於大量時間會產生錯誤。平均而言,我預計只有不到1%的電話會產生真值。但是,理論上,它可以高達100%的時間(這一點取決於最終用戶)。
我正在探索不同的方式,我可以計算一個狀態+模式是否會呼叫doExpensiveCall()。最後,我不得不選擇一些東西,所以我現在正在探索我的選擇。這些是目前我能想到的不同方式:
1)創建一個預先計算的包含布爾值的二維數組。這是我在上面的例子中使用的。這產生了我能想到的儘可能快的檢查。問題是如果狀態和模式的範圍很大(比如說10,000x1000),生成的表開始變得非常大(在10,000x1000的情況下,那個表只有10MB)。例如:
// STATE_COUNT=4, MODE_COUNT=3
static const char truthTable[STATE_COUNT][MODE_COUNT] = {
{0,1,0},
{0,0,0},
{1,1,0},
{0,0,1}
}
2)創建像#1的表,但壓縮的:不是每個陣列條目是單個布爾值,這將是一個字符的位域。然後,在檢查過程中,我會用狀態+模式做一些計算來決定如何索引到數組中。這通過MODE_MODE/8減少了預計算表的大小。缺點是減少的並不多,現在需要計算位域表中布爾值的索引,而不是像#1那樣只是簡單的數組訪問。
3)由於狀態+模式組合,將產生的真值的量預期爲小,switch語句也是可能的(在#1使用truthTable作爲參考):
switch(state){
case 0: // row
switch(mode){ // col
case 1: doExpensiveCall(system, state, mode);
break;
}
break;
case 2:
switch(mode){
case 0:
case 1: doExpensiveCall(system, state, mode);
break;
}
break;
case 3:
switch(mode){
case 2: doExpensiveCall(system, state, mode);
break;
}
break;
}
問題:
鑑於上述事實,有什麼其他方法可以用來計算調用doExpensiveCall()所需的布爾值嗎?
感謝
編輯: 我雖然關於延示例代碼,並出現以下情況給我。爲了只有一個switch語句,我可以在生成的代碼做這個計算:
// #if STATE_COUNT > MODE_COUNT
int i = s * STATE_COUNT + m;
// #else
int i = m * MODE_COUNT + s;
// #endif
switch(i) {
case 1: // use computed values here, too.
case 8:
case 9:
case 14:
doExpensiveCall(system, s, m);
}
您是否嘗試過分析/基準測試,看看它是否重要?所有事情都是平等的,我會從最簡單的解決方案(#1)開始,看看是否有任何優先考慮的事情。 – uesp 2014-09-22 15:01:41
具有完美散列函數的散列表? – 2014-09-22 15:08:18
優化還取決於什麼是重要的在你的情況:速度,內存,簡單/未來的維護等...... – uesp 2014-09-22 15:09:55