[英]Performance of switch within a loop
我們有消息類型到消息列表的映射。 給定對性能至關重要的代碼,例如:
struct row_t {
int message_type; //0,1,2,3,4,5
};
map<int, vector<row_t>> message_map;
for (auto x : message_map) {
int message_type = x.first;
vector<row_t> message_rows = x.second;
for (row_t row : message_rows) {
//LARGE CODE CHUNK
switch(row.message_type) { //same as switch(message_type)
case 0:
add_0_to_database();
break;
case 1:
add_0_to_database();
break;
//...
default:
break;
}
}
}
即使message_rows中的每個元素都具有相同的類型,switch語句也會在內部循環的每次迭代中執行。
可以消除此問題,僅在內部循環開始之前運行一次switch語句:
for (auto x : message_map) {
int message_type = x.first;
vector<row_t> message_rows = x.second;
switch(message_type) {
case 0:
for (row_t row : message_rows) {
//LARGE CODE CHUNK
add_0_to_database(row);
}
break;
case 1:
for (row_t row : message_rows) {
//LARGE CODE CHUNK
add_1_to_database(row);
}
break;
//...
default:
break;
}
}
但是,現在我們有多個冗余內部循環,並且“大代碼塊”代碼需要重復幾次。
我的問題:現代編譯器(尤其是g ++)是否可以將版本1優化為與版本2一樣有效?
還是應該使用版本2,也許考慮使用其他方法來消除冗余,例如在switch語句add_{0/1}_to_database
函數指針設置為add_{0/1}_to_database
,然后在循環中使用函數指針?
大概您實際的row_t
比問題中顯示的要復雜。 如果不是,則無需遍歷向量。 只需使用長度即可。
與性能問題一樣,第一件事就是測試。 如果您的代碼庫的這一部分對性能不是至關重要的,則無需擔心。 使您的代碼簡潔明了。 該答案的其余部分假定測試表明這確實是性能瓶頸。 在獲得解決方案之前,我將首先解決兩個問題,這些問題可能是更大的性能問題。
使用std::map
。 遍歷有序地圖可能會很昂貴。 考慮切換到c ++ 11中引入的std::unordered_map
。 您使用基於范圍的循環表示您正在使用c ++ 11或更高版本。
復制過多。 for (auto x : message_map)
的外循環for (auto x : message_map)
vector<row_t> message_rows = x.second
和for (row_t row : message_rows)
的外循環都for (auto x : message_map)
復制。 您將向量復制兩次,外加每個元素的另一個副本。 使用參考。
解決方案:考慮使用函數指針。 一種簡單的方法是將switch語句移出內部循環,但不要像問題中所示那樣進行循環,而是設置一個函數指針:
for (auto& x : message_map) // Note the use of auto& to avoid copying.
{
int message_type = x.first;
std::vector<row_t>& message_rows = x.second; // Avoid copying!
void (* add_to_database)(const row_t&) = nullptr;
switch(message_type)
{
case 0:
add_to_database = add_0_to_database;
break;
case 1:
add_to_database = add_1_to_database;
break;
//...
default:
// This might be an error that should be handled here.
break;
}
for (row_t& row : message_rows) // Avoid copying!
{
// LARGE CODE CHUNK
add_to_database (row);
}
}
該switch語句有點難看。 2.0版將其轉換為另一張地圖:
typedef void (*AddFunction)(const row_t&);
std::unordered_map<int, AddFunction> add_function_map;
// Outside the loop, populate the map:
add_function_map[0] = add_0_to_database;
add_function_map[1] = add_1_to_database;
// Rest of add_function_map population elided.
// The loop is now short and sweet.
for (auto& x : message_map) // Note the use of auto& to avoid copying.
{
int message_type = x.first;
std::vector<row_t>& message_rows = x.second; // Avoid copying!
AddFunction add_to_database = add_function_map[message_type];
for (row_t& row : message_rows) // Avoid copying!
{
// LARGE CODE CHUNK
add_to_database (row);
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.