2013-07-12 4 views
3

У меня есть данные, как это:Hash Эффективность тонн данных

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"; 
} 

он работает с данными выборки (около 100Мб), но это, кажется, принимает возрастов, чтобы иметь дело с моими реальными данными (ARO и 100G). Существуют ли способы оптимизации моих кодов?

Цените любые рекомендации заранее!

+0

звучит как хороший кандидат для MapReduce. Вы также можете изучить Threads. –

+2

Определите «возрасты». Откуда берутся эти данные? Если это с жесткого диска, 100 ГБ займет много минут, независимо от обработки, которую вы делаете. –

+0

@OliCharlesworth это с жесткого диска. – Sam

ответ

3

Как утверждают другие, наиболее вероятным узким местом является не хеш или Perl, а доступ к диску.

Разделить файл на более мелкие куски. (используя стандартные Unix utils, если можно).

Храните их в источниках SEPARATE IO (разные диски идеально подходят для разных контроллеров, в идеале на разных ПК).

  • Если у вас есть только несколько клавиш (например,> 100-1000 строк на ключ), просто запустить куски по отдельности, а затем объединить их все в 100й меньший размере файла, и процесс, что один файл в целом.

  • В противном случае синхронизируйте обработку, используя базу данных для хранения сумм.

+0

спасибо, и я думаю, что попытаюсь использовать базу данных из-за количества ключей. В любом случае, расщепление файла - хорошая попытка! – Sam

+0

Trust 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"; 
    } 
    
  • Если ключи уже отсортирован, нам не нужны какие-либо промежуточной структуры данных. Просто аккумулируйте сумму в скаляр. Когда ключ изменяется, выведите сумму последнего ключа.

  • Если у вас несколько файлов, вы можете применить свой алгоритм для каждого из этих файлов параллельно и объединить результаты. Это позволяет вашему коду работать в логарифмическом времени вместо линейного времени (например, большой выигрыш). Либо разделить большой файл на более мелкие куски, мы делаем некоторые магии с seek и tell, чтобы разделить файл. Чем более занятые процессоры у вас есть, тем быстрее будет скомпилирован ваш файл. С одной оговоркой: Вполне возможно, что I/O является вашим узким местом. Если эту задачу нужно выполнять регулярно, использование SSD (вместо жесткого диска) может значительно повысить производительность.

+0

Большое спасибо за ваш комментарий! – Sam

+0

спасибо! использование массива является более эффективным, если клавиши находятся в непрерывном диапазоне. В любом случае, если ключи не являются непрерывными, любые эффективные идеи, использующие хэш? – Sam

+0

@Sam «Проблема» с массивами состоит в том, что они компактны: если у вас есть ключ «1» и ключ «1000», будут выделены 999, но пустые поля (все индексы между ними плюс ноль). Если возможные ключи достаточно низки (где низкий зависит от того, насколько важна память для вас), тогда массив будет в порядке. Для этого случая использования мне будет неудобно, если какой-либо ключ превышает «2E6» (два миллиона). – amon

1

Если ваши данные выглядят так, как будто вы показываете нам, кажется, что у вас есть сортировка по ключу, поэтому хэш не нужен вообще.

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

будет делать трюк. Я сомневаюсь, что это будет медленным.

Смежные вопросы