1

要求是:

事实 1:我们有一些由遗留系统生成的数据文件

事实 2:我们有一些由新系统生成的数据文件,最终应该会取代旧系统

事实 3:

  1. 这两个文件都是文本/ASCII 文件,记录由多行组成。
  2. 记录中的每一行都由字段名和字段值组成。
  3. 1 和 2 中行的显示格式不同,但是可以通过使用正则表达式从每行中提取字段名和字段值
  4. 字段名称可以在 1 和 2 之间变化,但我们有一个关联它们的映射
  5. 每条记录都有一个唯一标识符,可帮助我们将旧记录与新记录相关联,因为输出文件中的记录顺序在两个系统中不必相同。
  6. 每个要比较的文件至少为 10 MB,平均为 30 - 35 MB

事实 4:当我们迭代构建新系统时,我们需要比较两个系统在完全相同的条件下生成的文件并协调差异。

事实 5:这种比较是使用昂贵的视觉差异工具手动完成的。为了帮助解决这个问题,我编写了一个工具,将两个不同的字段名合并为一个通用名称,然后对每个记录、每个文件中的字段名进行排序,以便它们按顺序同步(新文件可以有额外的字段,在视觉差异)

事实 6:由于人工比较是由人工进行的,而且人工会犯错误,我们会得到错误的正面和负面,这会极大地影响我们的时间线。

显然问题是,“ALG”和“DS”应该是什么?

我必须解决的场景:

人们继续在视觉上检查差异 - 在这种情况下,现有脚本的性能令人沮丧 - 大部分处理似乎是按字典顺序对行数组进行排序(读取/获取数组元素:Tie::File:: FETCH,Tie::File::Cache::lookup 并将其放在正确的位置以便对其进行排序:Tie::File::Cache::insert, Tie::File::Heap::insert)

use strict;
use warnings;

use Tie::File;

use Data::Dumper;

use Digest::MD5 qw(md5_hex);

# open an existing file in read-only mode
use Fcntl 'O_RDONLY';

die "Usage: $0 <unsorted input filename> <sorted output filename>" if ($#ARGV < 1);

our $recordsWrittenCount = 0;
our $fieldsSorted = 0;

our @array;

tie @array, 'Tie::File', $ARGV[0], memory => 50_000_000, mode => O_RDONLY or die "Cannot open $ARGV[0]: $!";

open(OUTFILE, ">" .  $ARGV[1]) or die "Cannot open $ARGV[1]: $!";

our @tempRecordStorage = ();

our $dx = 0;

# Now read in the EL6 file

our $numberOfLines = @array; # accessing @array in a loop might be expensive as it is tied?? 

for($dx = 0; $dx < $numberOfLines; ++$dx)
{
    if($array[$dx] eq 'RECORD')
    {
        ++$recordsWrittenCount;

        my $endOfRecord = $dx;

        until($array[++$endOfRecord] eq '.')
        {
            push @tempRecordStorage, $array[$endOfRecord];
            ++$fieldsSorted;
        }

        print OUTFILE "RECORD\n";

        local $, = "\n";
        print OUTFILE sort @tempRecordStorage;
        @tempRecordStorage = ();

        print OUTFILE "\n.\n"; # PERL does not postfix trailing separator after the last array element, so we need to do this ourselves)

        $dx = $endOfRecord;     
    }
}

close(OUTFILE);

# Display results to user

print "\n[*] Done: " . $fieldsSorted . " fields sorted from " . $recordsWrittenCount . " records written.\n";

所以我想了想,我相信,如果是 trie,可能是后缀 trie/PATRICIA trie,这样在插入时,每条记录中的字段都会被排序。因此,我不必一次性对最终数组进行排序,成本将被摊销(我的猜测)

在这种情况下会出现另一个问题 - Tie::File 使用数组来抽象文件中的行 - 将行读入树然后将它们序列化回数组将需要额外的内存和处理/

问题是 - 这会比当前对绑定数组进行排序的成本更高吗?

4

1 回答 1

2

Tie::File 非常慢。这有两个原因:首先,绑定变量比标准变量慢得多。另一个原因是,在 Tie::File 的情况下,数组中的数据位于磁盘上而不是内存中。这大大减慢了访问速度。Tie::File 的缓存在某些情况下可以提高性能,但当您像在此处那样一次循环遍历数组一个元素时却不能。(缓存仅在您重新访问相同的索引时才有帮助。)使用 Tie::File 的时间是当您有一个算法需要一次将所有数据保存在内存中但您没有足够的内存来执行此操作时。由于您使用 Tie::File 一次只处理一行文件不仅没有意义,而且是有害的。

我不认为 trie 在这里是正确的选择。我会改用普通的 HoH(哈希散列)。您的文件足够小,您应该能够一次将所有内容都保存在内存中。我建议解析每个文件并构建一个如下所示的哈希:

%data = (
  id1 => {
    field1 => value1,
    field2 => value2,
  },
  id2 => {
    field1 => value1,
    field2 => value2,
  },
);

如果您在构建数据结构时使用映射来规范化字段名称,这将使比较更容易。

要比较数据,请执行以下操作:

  1. 对两个哈希的键进行一组比较。这应该生成三个列表:仅存在于旧数据中的 ID、仅存在于新数据中的 ID 以及同时存在于两者中的 ID。
  2. 报告仅出现在一个数据集中的 ID 列表。这些是在其他数据集中没有对应记录的记录。
  3. 对于两个数据集中的 ID,逐个字段比较每个 ID 字段的数据并报告任何差异。
于 2009-05-21T02:59:19.800 回答