2013-07-12 72 views
3
這樣

我有數據:與大量數據的哈希效率

1 10 
1 30 
1 40 
1 10 
2 20 
2 20 
2 30 
3 50 
3 10 
3 10 
3 10 
4 20 
4 10 

我想如果第一列的值相匹配來概括所有的值了,結果會是這樣,

1 90 
2 70 
3 80 
4 30 

我這裏有我的代碼,

while (<DATA>) 
{ 
my ($a, $b) = split; 
$hash{$a} += $b; 
} 

foreach $a (sort keys %hash) 
{ 
$b = $hash{$a}; 
print OUT "$a $b\n"; 
} 

它與樣本數據(100MB左右),但它似乎採取年齡來處理我的真實數據(ARO和100G)。有什麼方法可以優化我的代碼嗎?

感謝任何建議提前!

+0

聽起來很適合MapReduce。你也可以考慮使用線程。 –

+2

定義「年齡」。這些數據來自哪裏?如果它來自硬盤,無論您正在做什麼處理,100GB將需要很多分鐘才能運行。 –

+0

@OliCharlesworth它來自硬盤.. – Sam

回答

3

正如其他人所說,最可能的瓶頸不是哈希或Perl,而是磁盤訪問。

將文件拆分爲更小的塊。 (如果可以的話,使用標準的Unix utils)。

將它們存儲在單獨的IO資源(理想情況下在不同控制器上的不同磁盤上,理想情況下在不同的PC上)。

  • 如果只有幾個鍵(例如> 100-1000每個鍵行),只需單獨運行塊,然後將它們全部連接成100X較小的文件,並處理一個文件作爲一個整體。

  • 否則,使用數據庫同步處理來存儲總和。

+0

謝謝,我想我會嘗試使用數據庫,因爲鍵的數量。無論如何分割文件是一個很好的嘗試! – Sam

+0

相信DVK!使用unix工具對塊(文件)進行排序,然後對值進行求和直到關鍵點發生更改。將結果寫入新文件。你可以用它來處理大量的數據。 (當你有多個密鑰時也可以使用) – smartmeta

2

哈希效率很高。它們可能是解決您的問題的最佳解決方案。但是,有可能是例外,這取決於你的數據:

  • 如果所有按鍵均爲整數的(或多或少)連續範圍,那麼你可以使用一個數組來代替,這甚至比散列更有效:

    while (<DATA>) { 
        my ($k, $v) = split; 
        $array[$k] += $v; 
    } 
    
    for my $i (grep defined $array[$_], 0 .. $#array) { 
        print "$i $array[$i]\n"; 
    } 
    
  • 如果密鑰已經排序,我們不需要任何中間數據結構。只需將總和累加爲標量即可。當鍵改變時,輸出最後一個鍵的總和。

  • 如果您有多個文件,可以將這些文件的算法並行應用併合並結果。這可以讓你的代碼運行在對數時間而不是線性時間(又名大贏)。要麼將大文件分割成更小的塊,我們要用seektell來分割文件。您擁有的處理器越繁忙,您的文件將被彙總得越快。 有一點需要注意: I/O很可能是您的瓶頸。如果此任務必須定期完成,則使用SSD(而不是HDD)可能會大大提高性能。

+0

非常感謝您的評論! – Sam

+0

謝謝!當按鍵處於連續範圍內時,使用陣列會更有效率。無論如何,如果密鑰不連續,使用哈希的任何有效的想法? – Sam

+0

@Sam數組的問題在於它們是緊湊的:如果你有一個關鍵字'1'和一個關鍵字'1000',將會有999個已分配的空字段(中間的所有索引以及零)。如果可能的密鑰足夠低(低位取決於內存對您的重要程度),那麼數組就可以。對於這個用例,如果任何鍵超過2E6(200萬),我會感到不舒服。 – amon

1

如果你的數據看起來像你給我們看,你似乎有它按鍵排序,所以散列是沒有必要的。

perl -anE'if($k!=$F[0]){say"$k $s"if$.>1;$k=$F[$s=0]}$s+=$F[1]}{say"$k $s"' 

會做伎倆。我懷疑它會變慢。