сравнение 2 наборов данных, возможно, с параллельным/асинхронным/параллельным подходом
В настоящее время я пытаюсь улучшить существующий механизм (для сравнения данных из 2 источников, реализованных в perl5) и хотел бы использовать perl6 вместо этого.
Объем целевых данных составляет около 20-30 ГБ в несжатых плоских файлах. В терминах строк файл может содержать от 18 до 28 миллионов строк. Он имеет около 40-50 столбцов в строке.
Я делаю этот тип согласования данных ежедневно, и для чтения из файла и заполнения хэша может потребоваться около 10 минут. ~ 20 минут потрачено на чтение обоих файлов и заполнение хэша.
процесс сравнения занимает около 30-50 минут, включая итерацию по хэшу, сбор желаемого результата (ов) и запись в выходной файл (csv, psv).
В целом, для выполнения этого процесса может потребоваться от 30 минут до 60 минут на 32-ядерном процессоре с двумя xeon cpu с 256 ГБ ОЗУ, включая прерывистую нагрузку на сервер.
Теперь я пытаюсь еще больше сократить время обработки.
Вот мой текущий однопоточный подход с использованием perl5.
- извлекать данные из 2 источников (скажем, s1 и s2) один за другим и заполнять мой хэш на основе пар ключ-значение. Источником данных может быть либо плоский CSV файл, либо psv файл, либо результат запроса массива Array из массива через клиент DBI. Для начала всегда данные не сортируются.
- Чтобы быть конкретным, я читал файл строки за строкой, разделял поля и выбирал нужные индексы для пары ключей, значений и вставлял в хэш.
- После сбора данных и заполнения хэша с помощью желаемых пар ключ/значение, я начинаю сравнивать и собирать результаты (главное сравнение того, что отсутствует или отличается в s2 по s1 и наоборот).
- дамп-выход в файле excel (очень дорогостоящий, если нет строк больших, например, ~ 1 млн или более) или в простой CSV (дешевая операция. предпочтительный метод).
Мне было интересно, смогу ли я как-нибудь сделать первый шаг параллельно, т.е. собрать данные из обоих источников сразу и заполнить мой глобальный хэш, а затем перейти к сравнению и вывести вывод?
Какие варианты могут помочь решить эту ситуацию? Я читал о параллелизмах, асинхронных и параллельных операциях с использованием perl6, но я не настолько уверен, что могу помочь мне здесь.
Я бы очень признателен за любое общее руководство по этому вопросу. Надеюсь, я хорошо объяснил свою проблему, но, к сожалению, мне нечего показать, что я пробовал до сих пор? и причина в том, что я только начинаю заниматься этим. Я просто не могу увидеть прошлый однопоточный подход и нуждаюсь в некоторой помощи.
Благодарю.
РЕДАКТИРОВАТЬ
Поскольку мое существующее выражение о проблеме было сочтено сообществом "слишком широким", позвольте мне попытаться выделить мои пункты боли ниже:
- Я хотел бы сделать сравнение файлов, используя все 32 ядра, если это возможно. Я просто не могу придумать стратегию или первоначальную идею.
- Какие новые методы доступны или применимы к perl6 для решения этой проблемы или типа проблемы.
- Если я создаю 2 процесса для чтения файлов и сбора данных - можно ли вернуть результат в виде массива или хэша?
- Можно ли параллельно сравнивать данные (хранимые в хеше)?
Моя текущая логика сравнения p5 показана ниже для вашей справки. Надеюсь, это поможет и не позволит этому вопросу отключиться.
package COMP;
use strict;
use Data::Dumper;
sub comp
{
my ($data,$src,$tgt) = @_;
my $result = {};
my $ms = ($result->{ms} = {});
my $mt = ($result->{mt} = {});
my $diff = ($result->{diff} = {});
foreach my $key (keys %{$data->{$src}})
{
my $src_val = $data->{$src}{$key};
my $tgt_val = $data->{$tgt}{$key};
next if ($src_val eq $tgt_val);
if (!exists $data->{$tgt}{$key}) {
push (@{$mt->{$key}}, "$src_val|NULL");
}
if (exists $data->{$tgt}{$key} && $src_val ne $tgt_val) {
push (@{$diff->{$key}}, "$src_val|$tgt_val")
}
}
foreach my $key (keys %{$data->{$tgt}})
{
my $src_val = $data->{$src}{$key};
my $tgt_val = $data->{$tgt}{$key};
next if ($src_val eq $tgt_val);
if (!exists $data->{$src}{$key}) {
push (@{$ms->{$key}},"NULL|$tgt_val");
}
}
return $result;
}
1;
Если кто-то хочет попробовать, вот пример вывода и используемый тестовый скрипт.
выход скрипта
[[email protected]:]$ perl testCOMP.pl
$VAR1 = {
'mt' => {
'Source' => [
'source|NULL'
]
},
'ms' => {
'Target' => [
'NULL|target'
]
},
'diff' => {
'Sunday_isit' => [
'Yes|No'
]
}
};
Тест-сценарий
[[email protected]:]$ cat testCOMP.pl
#!/usr/bin/env perl
use lib $ENV{PWD};
use COMP;
use strict;
use warnings;
use Data::Dumper;
my $data2 = {
f1 => {
Amitabh => 'Bacchan',
YellowSun => 'Yes',
Sunday_isit => 'Yes',
Source => 'source',
},
f2 => {
Amitabh => 'Bacchan',
YellowSun => 'Yes',
Sunday_isit => 'No',
Target => 'target',
},
};
my $result = COMP::comp ($data2,'f1','f2');
print Dumper $result;
[[email protected]:]$
Ответы
Ответ 1
Если у вас есть существующий и работающий набор инструментов, вам не нужно переписывать все это, чтобы использовать Perl6. Механизмы параллелизма тоже хорошо работают с внешними процессами. Рассмотрим
allnum.pl6
use v6;
my @processes =
[ "num1.txt", "num2.txt", "num3.txt", "num4.txt", "num5.txt" ]
.map( -> $filename {
[ $filename, run "perl", "num.pl", $filename, :out ];
})
.hyper;
say "Lazyness Here!";
my $time = time;
for @processes
{
say "<{$_[0]} : {$_[1].out.slurp}>";
}
say time - $time, "s";
num.pl
use warnings;
use strict;
my $file = shift @ARGV;
my $start = time;
my $result = 0;
open my $in, "<", $file or die $!;
while (my $thing = <$in>)
{
chomp $thing;
$thing =~ s/ //g;
$result = ($result + $thing) / 2;
}
print $result, " : ", time - $start, "s";
В моей системе
C:\Users\holli\tmp>perl6 allnum.pl6
Lazyness Here!
<num1.txt : 7684.16347578616 : 3s>
<num2.txt : 3307.36261498186 : 7s>
<num3.txt : 5834.32817942962 : 10s>
<num4.txt : 6575.55944995197 : 0s>
<num5.txt : 6157.63100049619 : 0s>
10s
Файлы были настроены так
C:\Users\holli\tmp>perl -e "for($i=0;$i<10000000;$i++) { print chr(32) ** 100, int(rand(1000)), chr(32) ** 100, qq(\n); }">num1.txt
C:\Users\holli\tmp>perl -e "for($i=0;$i<20000000;$i++) { print chr(32) ** 100, int(rand(1000)), chr(32) ** 100, qq(\n); }">num2.txt
C:\Users\holli\tmp>perl -e "for($i=0;$i<30000000;$i++) { print chr(32) ** 100, int(rand(1000)), chr(32) ** 100, qq(\n); }">num3.txt
C:\Users\holli\tmp>perl -e "for($i=0;$i<400000;$i++) { print chr(32) ** 100, int(rand(1000)), chr(32) ** 100, qq(\n); }">num4.txt
C:\Users\holli\tmp>perl -e "for($i=0;$i<5000;$i++) { print chr(32) ** 100, int(rand(1000)), chr(32) ** 100, qq(\n); }">num5.txt