热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Perl6:处理非常大的文件的最佳方法是什么?

如何解决《Perl6:处理非常大的文件的最佳方法是什么?》经验,为你挑选了1个好方法。

上周我决定尝试Perl6并开始重新实现我的一个程序.我不得不说,Perl6对于对象编程来说非常简单,这在Perl5中对我来说非常痛苦.

我的程序必须读取和存储大文件,例如全基因组(高达3 Gb或更高,参见下面的示例1)或制表数据.

代码的第一个版本是通过逐行迭代("genome.fa".IO.lines)以Perl5方式制作的.对于正确的执行时间来说,它非常缓慢且无法确定.

my class fasta {
  has Str $.file is required;
  has %!seq;

  submethod TWEAK() {
    my $id;
    my $s;

    for $!file.IO.lines -> $line {
      if $line ~~ /^\>/ {
        say $id;
        if $id.defined {
          %!seq{$id} = sequence.new(id => $id, seq => $s);
        }
        my $l = $line;
        $l ~~ s:g/^\>//;
        $id = $l;
        $s = "";
      }
      else {
        $s ~= $line;
      }
    }
    %!seq{$id} = sequence.new(id => $id, seq => $s);
  }
}


sub MAIN()
{
    my $f = fasta.new(file => "genome.fa");
}

所以在一点点RTFM之后,我改变了文件上的一个slurp,在我用for循环解析的\n上的一个分区.这样我设法在2分钟内加载数据.好多了但还不够.作弊,我的意思是删除最多的\n(例2),我将执行时间减少到30秒.相当不错,但并非完全满意,这种fasta格式并不是最常用的.

my class fasta {
  has Str $.file is required;
  has %!seq;

  submethod TWEAK() {
    my $id;
    my $s;

    say "Slurping ...";
    my $f = $!file.IO.slurp;

    say "Spliting file ...";
    my @lines = $f.split(/\n/);

    say "Parsing lines ...";
    for @lines -> $line {
      if $line !~~ /^\>/ {
          $s ~= $line;
      }
      else {
        say $id;
        if $id.defined {
          %!seq{$id} = seq.new(id => $id, seq => $s);
        }
        $id = $line;
        $id ~~ s:g/^\>//;
        $s = "";
      }
    }
    %!seq{$id} = seq.new(id => $id, seq => $s);
  }
}

sub MAIN()
{
    my $f = fasta.new(file => "genome.fa");
}

所以RTFM又一次发现了语法的神奇之处.无论使用何种fasta格式,所以新版本和执行时间为45秒.不是最快的方式,而是更优雅和稳定.

my grammar fastaGrammar {
  token TOP { + }

  token fasta   {<.ws> }
  token header  { \n }
  token sup     { '>' }
  token id      { <[\d\w]>+ }
  token seq     { [<[ACGTNacgtn]>+\n]+ }

}

my class fastaActions {
  method TOP ($/){
    my @seqArray;

    for $ -> $f {
      @seqArray.push: seq.new(id => $f..made, seq => $f.made);
    }
    make @seqArray;
  }

  method fasta ($/) { make ~$/; }
  method id    ($/) { make ~$/; }
  method seq   ($/) { make $/.subst("\n", "", :g); }

}

my class fasta {
  has Str $.file is required;
  has %seq;

  submethod TWEAK() {

    say "=> Slurping ...";
    my $f = $!file.IO.slurp;

    say "=> Grammaring ...";
    my @seqArray = fastaGrammar.parse($f, actiOns=> fastaActions).made;

    say "=> Storing data ...";
    for @seqArray -> $s {
      %!seq{$s.id} = $s;
    }
  }
}

sub MAIN()
{
    my $f = fasta.new(file => "genome.fa");
}

我认为我找到了很好的解决方案来处理这些大文件,但性能仍然在Perl5之下.

作为Perl6的新手,我有兴趣知道是否有更好的方法来处理大数据,或者是否由于Perl6实现有一些限制?

作为Perl6的新手,我会问两个问题:

还有其他Perl6机制,我还不知道,或者还没有记录,用于存储文件中的大量数据(比如我的基因组)?

我是否达到了当前Perl6版本的最高性能?

谢谢阅读 !


Fasta示例1:

>2L
CGACAATGCACGACAGAGGAAGCAGAACAGATATTTAGATTGCCTCTCATTTTCTCTCCCATATTATAGGGAGAAATATG
ATCGCGTATGCGAGAGTAGTGCCAACATATTGTGCTCTTTGATTTTTTGGCAACCCAAAATGGTGGCGGATGAACGAGAT
...
>3R
CGACAATGCACGACAGAGGAAGCAGAACAGATATTTAGATTGCCTCTCATTTTCTCTCCCATATTATAGGGAGAAATATG
ATCGCGTATGCGAGAGTAGTGCCAACATATTGTGCTCTTTGATTTTTTGGCAACCCAAAATGGTGGCGGATGAACGAGAT
...

Fasta示例2:

>2L
GACAATGCACGACAGAGGAAGCAGAACAGATATTTAGATTGCCTCTCAT...            
>3R
TAGGGAGAAATATGATCGCGTATGCGAGAGTAGTGCCAACATATTGTGCT...

编辑 我应用@Christoph和@timotimo的建议并用代码测试:

my class fasta {
  has Str $.file is required;
  has %!seq;

  submethod TWEAK() {
    say "=> Slurping / Parsing / Storing ...";
    %!seq = slurp($!file, :enc).split('>').skip(1).map: {
  .head => seq.new(id => .head, seq => .skip(1).join) given .split("\n").cache;
    }
  }
}


sub MAIN()
{
    my $f = fasta.new(file => "genome.fa");
}

该计划以2.7秒结束,非常棒!我还在小麦基因组(10 Gb)上尝试了这个代码.它完成了35.2秒.Perl6终于不是那么慢了!

大感谢您的帮助!



1> Christoph..:

一个简单的改进是使用固定宽度编码,例如latin1加速字符解码,但我不确定这将有多大帮助.

就Rakudo的正则表达式/语法引擎而言,我发现它非常慢,所以可能确实需要采用更低级的方法.

我没有做任何基准测试,但我首先尝试的是这样的:

my %seqs = slurp('genome.fa', :enc).split('>')[1..*].map: {
    .[0] => .[1..*].join given .split("\n");
}

由于Perl6标准库是在Perl6本身中实现的,因此有时可以通过避免它来提高性能,以命令式方式编写代码,例如:

my %seqs;
my $data = slurp('genome.fa', :enc);
my $pos = 0;
loop {
    $pos = $data.index('>', $pos) // last;

    my $ks = $pos + 1;
    my $ke = $data.index("\n", $ks);

    my $ss = $ke + 1;
    my $se = $data.index('>', $ss) // $data.chars;

    my @lines;

    $pos = $ss;
    while $pos <$se {
        my $end = $data.index("\n", $pos);
        @lines.push($data.substr($pos..^$end));
        $pos = $end + 1
    }

    %seqs{$data.substr($ks..^$ke)} = @lines.join;
}

但是,如果使用的标准库的部分已经看到一些性能工作,这实际上可能会使事情变得更糟.在这种情况下,下一步采取将被加入低级类型的注解,例如strint和更换调用例程,例如.index与NQP建宏如nqp::index.

如果这仍然太慢,那么你运气不好,需要切换语言,例如使用Inline::Perl5或使用C 调用Perl5 NativeCall.


请注意,@ timotimo已经完成了一些性能测量并写了一篇关于它的文章.

如果我的短版本是基线,则命令式版本将性能提高2.4倍.

他实际上设法通过重写它来缩短短版本的3倍

my %seqs = slurp('genome.fa', :enc).split('>').skip(1).map: {
    .head => .skip(1).join given .split("\n").cache;
}

最后,使用重写内建NQP的版本必须加快东西用的17X的一个因素,但考虑到潜在的可移植性问题,写这样的代码一般不提倡,但对于可能是必要的,现在如果你真的需要的性能水平:

use nqp;

my Mu $seqs := nqp::hash();
my str $data = slurp('genome.fa', :enc);
my int $pos = 0;

my str @lines;

loop {
    $pos = nqp::index($data, '>', $pos);

    last if $pos <0;

    my int $ks = $pos + 1;
    my int $ke = nqp::index($data, "\n", $ks);

    my int $ss = $ke + 1;
    my int $se = nqp::index($data ,'>', $ss);

    if $se <0 {
        $se = nqp::chars($data);
    }

    $pos = $ss;
    my int $end;

    while $pos <$se {
        $end = nqp::index($data, "\n", $pos);
        nqp::push_s(@lines, nqp::substr($data, $pos, $end - $pos));
        $pos = $end + 1
    }

    nqp::bindkey($seqs, nqp::substr($data, $ks, $ke - $ks), nqp::join("", @lines));
    nqp::setelems(@lines, 0);
}


转换使用native int的版本来使用nqp ops(那些不是官方支持的btw,使用这些ops的代码可以在rakudo更改时自发中断)使程序在2.9s内完成,其中0.34s是根据时间的系统时间,分析师估计大约18%的时间花在"啜食"本身.听起来不是很糟糕.
通过在循环体内使用`.skip(1)`而不是`[1 ..*]`,以及`.head`和`.skip(1)`,可以大大加快你的第一个答案.另外,它要求`.split("\n")`被"增强"到`.split("\n").cache`,所以head和skip方法对它起作用.在我的机器上从47s降到了12s.我有更多的想法,在后面的评论中可能更多,或者可能是自己的答案
第二个代码的快速配置文件显示,花费的大量时间源于`.. ^`Range构造函数运算符.使用`$ pos,$ end - $ pos`而不是`$ pos .. ^ $ end`让我从16.2秒降到8.75秒,所以时间几乎减半.
moarvm是否会自动执行类似于假设Latin1编码的操作,直到输入中断该假设?换句话说,对于一个实际上是Latin1的文件,从性能角度来看,不是`:enc `大部分还是完全冗余?
@raiph它所做的是尝试存储从每个字母8位的utf8源读取的数据,直到遇到不适合的东西,此时它将转换为每个字母32位.我相信,utf8解码器已经看到了比latin1更多的优化工作,当我尝试切换编码时,它几乎没有任何区别.
@timotimo:我使用你博客文章的结果扩展了我的答案
推荐阅读
  • C# 7.0 新特性:基于Tuple的“多”返回值方法
    本文介绍了C# 7.0中基于Tuple的“多”返回值方法的使用。通过对C# 6.0及更早版本的做法进行回顾,提出了问题:如何使一个方法可返回多个返回值。然后详细介绍了C# 7.0中使用Tuple的写法,并给出了示例代码。最后,总结了该新特性的优点。 ... [详细]
  • Java中包装类的设计原因以及操作方法
    本文主要介绍了Java中设计包装类的原因以及操作方法。在Java中,除了对象类型,还有八大基本类型,为了将基本类型转换成对象,Java引入了包装类。文章通过介绍包装类的定义和实现,解答了为什么需要包装类的问题,并提供了简单易用的操作方法。通过本文的学习,读者可以更好地理解和应用Java中的包装类。 ... [详细]
  • 海马s5近光灯能否直接更换为H7?
    本文主要介绍了海马s5车型的近光灯是否可以直接更换为H7灯泡,并提供了完整的教程下载地址。此外,还详细讲解了DSP功能函数中的数据拷贝、数据填充和浮点数转换为定点数的相关内容。 ... [详细]
  • 本文介绍了在序列化时如何对SnakeYaml应用格式化,包括通过设置类和DumpSettings来实现定制输出的方法。作者提供了一个示例,展示了期望的yaml生成格式,并解释了如何使用SnakeYaml的特定设置器来实现这个目标。对于正在使用SnakeYaml进行序列化的开发者来说,本文提供了一些有用的参考和指导。摘要长度为169字。 ... [详细]
  • 如何用JNI技术调用Java接口以及提高Java性能的详解
    本文介绍了如何使用JNI技术调用Java接口,并详细解析了如何通过JNI技术提高Java的性能。同时还讨论了JNI调用Java的private方法、Java开发中使用JNI技术的情况以及使用Java的JNI技术调用C++时的运行效率问题。文章还介绍了JNIEnv类型的使用方法,包括创建Java对象、调用Java对象的方法、获取Java对象的属性等操作。 ... [详细]
  • 恶意软件分析的最佳编程语言及其应用
    本文介绍了学习恶意软件分析和逆向工程领域时最适合的编程语言,并重点讨论了Python的优点。Python是一种解释型、多用途的语言,具有可读性高、可快速开发、易于学习的特点。作者分享了在本地恶意软件分析中使用Python的经验,包括快速复制恶意软件组件以更好地理解其工作。此外,作者还提到了Python的跨平台优势,使得在不同操作系统上运行代码变得更加方便。 ... [详细]
  • Python的参数解析argparse模块的学习
    本文介绍了Python中参数解析的重要模块argparse的学习内容。包括位置参数和可选参数的定义和使用方式,以及add_argument()函数的详细参数关键字解释。同时还介绍了命令行参数的操作和可接受数量的设置,其中包括整数类型的参数。通过学习本文内容,可以更好地理解和使用argparse模块进行参数解析。 ... [详细]
  • Spring框架《一》简介
    Spring框架《一》1.Spring概述1.1简介1.2Spring模板二、IOC容器和Bean1.IOC和DI简介2.三种通过类型获取bean3.给bean的属性赋值3.1依赖 ... [详细]
  • 深入理解Java虚拟机的并发编程与性能优化
    本文主要介绍了Java内存模型与线程的相关概念,探讨了并发编程在服务端应用中的重要性。同时,介绍了Java语言和虚拟机提供的工具,帮助开发人员处理并发方面的问题,提高程序的并发能力和性能优化。文章指出,充分利用计算机处理器的能力和协调线程之间的并发操作是提高服务端程序性能的关键。 ... [详细]
  • Monkey《大话移动——Android与iOS应用测试指南》的预购信息发布啦!
    Monkey《大话移动——Android与iOS应用测试指南》的预购信息已经发布,可以在京东和当当网进行预购。感谢几位大牛给出的书评,并呼吁大家的支持。明天京东的链接也将发布。 ... [详细]
  • 云原生边缘计算之KubeEdge简介及功能特点
    本文介绍了云原生边缘计算中的KubeEdge系统,该系统是一个开源系统,用于将容器化应用程序编排功能扩展到Edge的主机。它基于Kubernetes构建,并为网络应用程序提供基础架构支持。同时,KubeEdge具有离线模式、基于Kubernetes的节点、群集、应用程序和设备管理、资源优化等特点。此外,KubeEdge还支持跨平台工作,在私有、公共和混合云中都可以运行。同时,KubeEdge还提供数据管理和数据分析管道引擎的支持。最后,本文还介绍了KubeEdge系统生成证书的方法。 ... [详细]
  • 本文讨论了在VMWARE5.1的虚拟服务器Windows Server 2008R2上安装oracle 10g客户端时出现的问题,并提供了解决方法。错误日志显示了异常访问违例,通过分析日志中的问题帧,找到了解决问题的线索。文章详细介绍了解决方法,帮助读者顺利安装oracle 10g客户端。 ... [详细]
  • 从零基础到精通的前台学习路线
    随着互联网的发展,前台开发工程师成为市场上非常抢手的人才。本文介绍了从零基础到精通前台开发的学习路线,包括学习HTML、CSS、JavaScript等基础知识和常用工具的使用。通过循序渐进的学习,可以掌握前台开发的基本技能,并有能力找到一份月薪8000以上的工作。 ... [详细]
  • 超级简单加解密工具的方案和功能
    本文介绍了一个超级简单的加解密工具的方案和功能。该工具可以读取文件头,并根据特定长度进行加密,加密后将加密部分写入源文件。同时,该工具也支持解密操作。加密和解密过程是可逆的。本文还提到了一些相关的功能和使用方法,并给出了Python代码示例。 ... [详细]
  • 本文介绍了一个Python函数same_set,用于判断两个相等长度的数组是否包含相同的元素。函数会忽略元素的顺序和重复次数,如果两个数组包含相同的元素,则返回1,否则返回0。文章还提供了函数的具体实现代码和样例输入输出。 ... [详细]
author-avatar
王文波玉龙_946
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有