大廈,這裏是使用略有不同的方法一個C++ 11版:
List rowCounts_2(IntegerMatrix x) {
int n = x.nrow() ;
int nc = x.ncol() ;
std::vector<int> hashes(n) ;
for(int k=0, pow=1; k<nc; k++, pow*=2){
IntegerMatrix::Column column = x.column(k) ;
std::transform(column.begin(), column.end(), hashes.begin(), hashes.begin(), [=](int v, int h){
return h + pow*v ;
}) ;
}
using Pair = std::pair<int,int> ;
std::unordered_map<int, Pair> map_counts ;
for(int i=0; i<n; i++){
Pair& p = map_counts[ hashes[i] ] ;
if(p.first == 0){
p.first = i+1 ; // using directly 1-based index
}
p.second++ ;
}
int nres = map_counts.size() ;
IntegerVector idx(nres), counts(nres) ;
auto it=map_counts.begin() ;
for(int i=0; i<nres; i++, ++it){
idx[i] = it->second.first ;
counts[i] = it->second.second ;
}
return List::create(_["counts"] = counts, _["idx"] = idx);
}
的想法是換內存速度。第一個變化是我分配並填充了一個std::vector<int>
來承載哈希。這樣做可以讓我遍歷輸入矩陣逐列,這是更有效的。
一旦完成,我正在訓練對(索引,計數)的散列圖std::unordered_map<int, std::pair<int,int>>
。該映射的關鍵是散列,值是一對(索引,計數)。
然後我只需要遍歷哈希映射並收集結果。結果不會按照idx
的升序出現(如果我們確實需要,很容易做到這一點)。
我得到這些結果爲n=1e5
和n=1e7
。
> m <- matrix(sample(0:1, 1e+05, TRUE), ncol = 10)
> microbenchmark(rowCounts(m), rowCountsR(m), rowCounts_2(m))
Unit: microseconds
expr min lq median uq max neval
rowCounts(m) 1194.536 1201.273 1213.1450 1231.7295 1286.458 100
rowCountsR(m) 575.004 933.637 962.8720 981.6015 23678.451 100
rowCounts_2(m) 421.744 429.118 442.5095 455.2510 530.261 100
> m <- matrix(sample(0:1, 1e+07, TRUE), ncol = 10)
> microbenchmark(rowCounts(m), rowCountsR(m), rowCounts_2(m))
Unit: milliseconds
expr min lq median uq max neval
rowCounts(m) 97.22727 98.02716 98.56641 100.42262 102.07661 100
rowCountsR(m) 57.44635 59.46188 69.34481 73.89541 100.43032 100
rowCounts_2(m) 22.95741 23.38186 23.78068 24.16814 27.44125 100
利用線程有助於進一步提高。以下是我的機器上4個線程之間的時間分配情況。請參閱此gist中的代碼。
下面是與最後一個版本太多基準:
> microbenchmark(rowCountsR(m), rowCounts_1(m), rowCounts_2(m), rowCounts_3(m,4))
Unit: milliseconds
expr min lq median uq max neval
rowCountsR(m) 93.67895 127.58762 127.81847 128.03472 151.54455 100
rowCounts_1(m) 120.47675 120.89169 121.31227 122.86422 137.86543 100
rowCounts_2(m) 28.88102 29.68101 29.83790 29.97112 38.14453 100
rowCounts_3(m, 4) 12.50059 12.68981 12.87712 13.10425 17.21966 100
非常好的答案。雖然我不太確定線程是如何進一步改進的。整個操作需要23ms。創建/銷燬線程的開銷會不會成爲一個更大的因素?測試它會很好。 – Arun
它確實有所作爲,我有數據。稍後我會在一些要點中加入一些代碼。 –
我已經更新了這裏的文字,並將代碼放在這個要點中:https://gist.github.com/romainfrancois/10016972。 –