簡體   English   中英

性能:類的向量或包含向量的類

[英]Performance: vector of classes or a class containing vectors

我有一個包含許多double值的類。 這存儲在向量中,其中類的索引很重要(它們從其他地方引用)。 該類看起來像這樣:

矢量的類

class A
{
  double count;
  double val;
  double sumA;
  double sumB;

  vector<double> sumVectorC;
  vector<double> sumVectorD;
}

vector<A> classes(10000);

需要盡可能快地運行的代碼是這樣的:

vector<double> result(classes.size());
for(int i = 0; i < classes.size(); i++)
{
  result[i] += classes[i].sumA;
  vector<double>::iterator it = find(classes[i].sumVectorC.begin(), classes[i].sumVectorC.end(), testval);
  if(it != classes[i].sumVectorC.end())
    result[i] += *it;
}

替代方案是代替一個巨大的循環,將計算分成兩個獨立的循環,例如:

for(int i = 0; i < classes.size(); i++)
{
  result[i] += classes[i].sumA;
}
for(int i = 0; i < classes.size(); i++)
{
 vector<double>::iterator it = find(classes[i].sumVectorC.begin(), classes[i].sumVectorC.end(), testval);
  if(it != classes[i].sumVectorC.end())
    result[i] += *it;
}

或者將類的每個成員存儲在這樣的向量中:

矢量類

vector<double> classCounts;
vector<double> classVal;
...
vector<vector<double> > classSumVectorC;
...

然后運作:

for(int i = 0; i < classes.size(); i++)
{
  result[i] += classCounts[i];
  ...
}

哪種方式通常會更快(跨x86 / x64平台和編譯器)? 預測和緩存行是最重要的事情嗎?

更新

我在這里進行線性搜索(即查找)而不是哈希映射或二進制搜索的原因是因為sumVectors非常短,大​​約有4或5個元素。 分析顯示哈希映射較慢,二進制搜索稍慢。

由於兩種變體的實現看起來很容易,我會構建兩個版本並對它們進行分析以找到最快的版本。

經驗數據通常勝過猜測。

作為一個副作用:當前,最內層循環中的find()classes[i].sumVectorC所有元素進行線性掃描,直到找到匹配的值。 如果該向量包含許多值,並且您沒有理由相信testVal出現在向量的開頭附近,那么這將是緩慢的 - 考慮使用更快查找的容器類型(例如std::map或其中一個非標准但通常實現的hash_map類型)。

作為一般准則: 在低級實現優化之前考慮算法改進。

正如Lothar所說,你真的應該測試一下。 但是要回答你的上一個問題,是的,緩存未命中將成為一個主要問題。

此外,您的第一個實現似乎會在編碼時遇到load-hit-store停頓,但我不確定x86上有多少問題(這是XBox 360和PS3上的一個大問題)。

看起來優化find()將是一個巨大的勝利(配置文件肯定知道)。 根據不同的大小,除了用另一個容器替換向量之外,您還可以嘗試對sumVectorC進行排序並使用lower_bound形式的二進制搜索。 這會將線性搜索O(n)轉換為O(log n)。

如果你可以保證std::numeric_limits<double>::infinity不是一個可能的值,確保數組在末尾用虛擬無限條目排序,然后手動編碼查找,以便循環條件是一個單一的測試:

 array[i]<test_val

然后是一個平等測試。

然后你知道在未找到的情況下,查看值的平均數是(size()+ 1)/ 2。 當然,如果搜索陣列頻繁變化,那么保持排序的問題就成了問題。

當然,你沒有告訴我們很多關於sumVectorC或其他A的事情,所以很難確定並給出非常好的建議。 例如,如果sumVectorC永遠不會更新,那么很可能找到一個非常便宜的哈希(例如,轉換ULL和位提取),這對於適合double [8]的sumVectorC值是完美的。 然后,開銷是比特提取和1比較3或6

另外如果你有一個合理的sumVectorC.size()綁定(你提到4或5所以這個假設似乎不錯)你可以考慮使用聚合數組甚至只是一個boost::array<double>並添加你自己的動態大小例如:

class AggregatedArray : public boost::array<double>{
   size_t _size;
   size_t size() const {
      return size;
   }
   ....
   push_back(..){...
   pop(){...
   resize(...){...
};

這消除了對sumVectorC分配的數組數據的額外緩存行訪問。

在sumVectorC非常不經常更新的情況下,如果找到一個完美的哈希(在你的哈希算法類中)相對便宜,那么當sumVectorC改變時你可以帶來利潤。 這些小的查找可能會有問題,而算法的復雜性往往是無關緊要的 - 它是占​​主導地位的常數。 這是一個工程問題,而不是理論問題。

除非你能保證小地圖在緩存中,否則幾乎可以保證使用std :: map會使性能提高約130%,因為樹中的每個節點幾乎都在一個單獨的緩存行中

因此,不是每次搜索訪問(4次1 + 1次2)/ 5 = 1.2緩存行(前4個在第一個緩存行中,第5個在第二個緩存行中,您將訪問(1 + 2次2 + 2次3) )= 9/5)樹本身+ 1 =每次搜索2.8個緩存行(1是根節點1節點,2個節點是根節點的子節點,最后2個節點是根節點的孫子節點,加上樹本身)

所以我預測使用std :: map對於具有5個條目的sumVectorC來說需要2.8 / 1.2 = 233%

這就是我說的時候的意思:“這是一個工程問題而不是理論問題。”

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM