使用并发/异步/并行方法比较两个数据集

11

我目前正在尝试改进一个现有机制(用于比较两个数据源中的数据,使用perl5实现),希望改用perl6。

我的目标数据量范围是约20-30 GB的未压缩平面文件。 就行数而言,一个文件可以包含从1800万到2800万行的任何位置。 每行大约有40-50列。

我每天都要进行这种类型的数据对账,从文件中读取并填充哈希表可能需要约10分钟。读取两个文件并填充哈希表需要约20分钟。

比较过程需要约30-50分钟,包括迭代哈希表、收集所需结果和写入输出文件(csv、psv)。

总体而言,在一个32核双Xeon CPU服务器上,处理整个流程可能需要30分钟到60分钟,包括间歇性的服务器负载。

现在我正试图进一步缩短总处理时间。

以下是我目前使用perl5的单线程方法:

  1. 逐个从2个来源(假设为s1和s2)获取数据,并根据键值对填充哈希表。数据源可以是扁平的csv或psv文件或数据库查询数组结果,通过DBI客户端。数据始终未排序。
  2. 具体来说,我逐行读取文件,分割字段,并选择键值对的所需索引插入哈希表。
  3. 在收集数据并填充具有所需键/值对的哈希表之后,开始比较和收集结果(主要比较s2相对于s1缺少或不同的内容,反之亦然)。
  4. 将输出转储到Excel文件(如果行数大约为~1百万或更多,则非常昂贵)或简单的CSV文件中(便宜操作,首选方法)。

我想知道是否可以并行执行第一步,即同时从两个来源收集数据并填充全局哈希表,然后进行比较和转储输出?

Perl6能提供哪些选项来处理这种情况?我已经阅读了关于使用perl6进行并发、异步和并行操作的文章,但我不确定哪个可以在这里帮助我。

我真的很感激任何一般性的指导。我希望我已经解释了我的问题,但可悲的是我没有什么可以展示的到目前为止我做了什么?,原因是我刚开始解决这个问题。我只能看到单线程方法,需要一些帮助。

谢谢。

编辑

由于社区认为我的现有问题陈述“太广泛”,请允许我尝试以下突出我的问题点:

  1. 如果可能,我想利用所有32个核心进行文件比较。我只是无法想出一个策略或初始想法。
  2. Perl6提供了哪些新技术或适用于解决此类问题的技术。
  3. 如果我生成2个进程来读取文件和收集数据,则是否可能将结果作为数组或哈希返回?
  4. 是否可能并行比较(存储在哈希表中的数据)?

您可以参考下面的代码来了解

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;

如果有人想尝试一下,这里是样例输出和使用的测试脚本。

脚本输出:

[User@Host:]$ perl testCOMP.pl 
$VAR1 = {
          'mt' => {
                    'Source' => [
                                  'source|NULL'
                                ]
                  },
          'ms' => {
                    'Target' => [
                                  'NULL|target'
                                ]
                  },
          'diff' => {
                      'Sunday_isit' => [
                                         'Yes|No'
                                       ]
                    }
        };

测试脚本

[User@Host:]$  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;
[User@Host:]$ 

1
s1和s2是否已排序或可排序?换句话说,您能否对它们进行排序,以便从s1的第一行/行开始,然后可以立即知道s2中的第一行是在其之前且与您的比较逻辑无关,还是相应的行,尽管可能缺少某些列或不匹配,或者在其之后? - raiph
1
我无法确定算法是否需要更改。您没有提供足够的细节。(哇,我看到有4个人投票关闭您的问题,因为它太广泛了。再有一个就会被关闭。投票者,请耐心等待!@User9102d82请添加更多细节!)如果您在问题中添加到gist.github.com或其他地方的链接,其中包含您的P5代码副本或至少比较逻辑,即使您的问题被关闭,这也会对我有所帮助。如果失败,请详细说明“比较s2相对于s1缺失或不同的逻辑以及反之亦然”的逻辑。 - raiph
1
这仍然非常宽泛。你有大量的数据。这不错,但是一次问4个问题仍然很多。考虑将其分解为较小的编程块,或者尝试在SoftwareEngineers.SE上提问。 - Machavity
1
@Joshua:请问为什么标签值会给我带来这么多麻烦?我无法理解那个陈述。关于我的问题过于宽泛 - 我考虑了一下,我在脑海中看到的方式是,这非常是一个Perl问题,因为我无法决定哪些perl5/6函数可以帮助我实现性能增强目标,可能还要利用这些方法。我从未想过把4个问题塞进一个问题里,如果我表现出这样的态度 - 给我一个机会改变。我只是想学习更多并继续前进!我的要求只是为了新的想法,因为我已经完全被耗尽了。 - User9102d82
1
标签维基页面指出不要使用value标签,因为它正在被删除。这是一个手动的过程,我们有很多人在筛选列表,而在50个糟糕的问题之后,他们的容忍度会降低。 - Joshua
显示剩余10条评论
1个回答

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接