сравнение 2 наборов данных, возможно, с параллельным/асинхронным/параллельным подходом - программирование
Подтвердить что ты не робот

сравнение 2 наборов данных, возможно, с параллельным/асинхронным/параллельным подходом

В настоящее время я пытаюсь улучшить существующий механизм (для сравнения данных из 2 источников, реализованных в perl5) и хотел бы использовать perl6 вместо этого.

Объем целевых данных составляет около 20-30 ГБ в несжатых плоских файлах. В терминах строк файл может содержать от 18 до 28 миллионов строк. Он имеет около 40-50 столбцов в строке.

Я делаю этот тип согласования данных ежедневно, и для чтения из файла и заполнения хэша может потребоваться около 10 минут. ~ 20 минут потрачено на чтение обоих файлов и заполнение хэша.

процесс сравнения занимает около 30-50 минут, включая итерацию по хэшу, сбор желаемого результата (ов) и запись в выходной файл (csv, psv).

В целом, для выполнения этого процесса может потребоваться от 30 минут до 60 минут на 32-ядерном процессоре с двумя xeon cpu с 256 ГБ ОЗУ, включая прерывистую нагрузку на сервер.

Теперь я пытаюсь еще больше сократить время обработки.

Вот мой текущий однопоточный подход с использованием perl5.

  1. извлекать данные из 2 источников (скажем, s1 и s2) один за другим и заполнять мой хэш на основе пар ключ-значение. Источником данных может быть либо плоский CSV файл, либо psv файл, либо результат запроса массива Array из массива через клиент DBI. Для начала всегда данные не сортируются.
  2. Чтобы быть конкретным, я читал файл строки за строкой, разделял поля и выбирал нужные индексы для пары ключей, значений и вставлял в хэш.
  3. После сбора данных и заполнения хэша с помощью желаемых пар ключ/значение, я начинаю сравнивать и собирать результаты (главное сравнение того, что отсутствует или отличается в s2 по s1 и наоборот).
  4. дамп-выход в файле excel (очень дорогостоящий, если нет строк больших, например, ~ 1 млн или более) или в простой CSV (дешевая операция. предпочтительный метод).

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

Какие варианты могут помочь решить эту ситуацию? Я читал о параллелизмах, асинхронных и параллельных операциях с использованием perl6, но я не настолько уверен, что могу помочь мне здесь.

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

Благодарю.

РЕДАКТИРОВАТЬ

Поскольку мое существующее выражение о проблеме было сочтено сообществом "слишком широким", позвольте мне попытаться выделить мои пункты боли ниже:

  1. Я хотел бы сделать сравнение файлов, используя все 32 ядра, если это возможно. Я просто не могу придумать стратегию или первоначальную идею.
  2. Какие новые методы доступны или применимы к perl6 для решения этой проблемы или типа проблемы.
  3. Если я создаю 2 процесса для чтения файлов и сбора данных - можно ли вернуть результат в виде массива или хэша?
  4. Можно ли параллельно сравнивать данные (хранимые в хеше)?

Моя текущая логика сравнения 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]:]$ 
4b9b3361

Ответ 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