草庐IT

python, perl 和julia的性能对比

小光amateur 2023-10-11 原文

2023/3/20更新

Codon是一个高性能的Python编译器,它将Python代码编译为本地机器代码,而不需要任何运行时开销。Python上的典型加速在单个线程上大约为10-100x或更多。Codon的性能通常与C/C++不相上下。与Python不同,Codon支持本机多线程,这会导致速度提高很多倍。Codon可通过插件基础设施进行扩展,使您能够合并新的库、编译器优化甚至关键字。

现在,让我们测试codon是否能给python提速,在此之前,我们需要修改以下python的代码

import sys
def calculateGC(sequence:str)->Tuple[int,int]:
    """Calculate the GC content of a DNA sequence"""
    gc = 0
    allnumber = 0
    for i in sequence:
        if i != 'N' and i != 'n':
            allnumber += 1
            if i == 'G' or i == 'C' or i == 'g' or i == 'c':
                gc += 1
    return gc, allnumber


def main(file:str):
    """Main function"""
    gcNum = 0
    allNum = 0
    with open(file,'r') as f:
        for line in f:
            line=line.strip()
            if line.startswith('>'):
                continue
            else:
                gc, allnumber = calculateGC(line)
                gcNum += gc
                allNum += allnumber
    print(f'GC content is: {gcNum/allNum:.3f}')

main(sys.argv[1])

然后运行

codon build --release -o calGC calGC.py

最后速度为:

time ./calGC ../../Project/DataBase/hg38.fa     
GC content is: 0.410
./calGC ../../Project/DataBase/hg38.fa  22.30s user 2.75s system 111% cpu 22.421 total

速度还不错,已经可以超越不用Biojulia包的julia函数了。

2022/11/14更新

最近python3.11出来了,据说性能有很大的提升, 一位国外的小哥(Dennis Bakhuis)采用简单的蒙特卡洛预测圆周率的方式测试循环的性能,发现Python3.11性能确实是突飞猛进,同一时间,Julia也已经更新到V1.8了,于是我在他的github下贡献了julia版本的代码,希望继续比较多个语言的计算特性。

总而言之,虽然python性能进一步优化,但和julia相比,速度依旧不够打,1000000个循环,python3.11用时6秒,而julia仅需要0.033秒。具体可以看github
https://github.com/dennisbakhuis/python3.11_speedtest

在生物信息学中经常用到的脚本语言主要是pythonperl,他们被用来处理文本大量统计流程控制等等,其自身也是各有优势。比如说perl天生就为了处理文本而生,但是python确是有名的胶水语言,特别在整合C代码时显示出巨大的优势,其语法简洁易懂,易于维护更让其成为仅次于CJAVA的第三大语言,但其糟糕的性能在处理大量循环时会让人忍不住抓狂。因此,Julia语言应运而生,其控制了python中没必要的动态性,加之使用JIT技术让其能够保有高性能的同时具备简洁的语法。

说了那么多,在生物信息上我们经常需要处理大量的文本文件,例如Fasta格式的序列文件,那么三者又是谁快呢?

版本控制

  • python3 = 3.8.3
  • perl = 5.26.2
  • julia = 1.5.0-beta
  • system = centos 8

计算内容

从UCSC上下载人类参考基因组 hg38.fa.gz 并解压,计算基因组GC含量,N碱基不算在总长中

代码

perl

#!/usr/bin/perl -w

use strict;

if(@ARGV < 1){
    die "Usage : perl $0 <genome.fa>\n";
}

my $input = shift @ARGV;

my ($sum,$G_num,$C_num,$N_num)=(0,0,0,0);
my $id;
open IN, "< $input" or die $!;
while(my $line = <IN>){
    chomp $line;
    if($line =~ />([^\s]+)/){
        $id = $1;
    }else{
        $sum += length($line);
        $G_num += ($line =~ tr/Gg/Gg/);
        $C_num += ($line =~ tr/Cc/Cc/);
        $N_num += ($line =~ tr/Nn/Nn/);
    }
}
close IN;

my $GC_rate = ($G_num+$C_num)/($sum-$N_num);
printf "GC content: %.3f \n",$GC_rate;

julia

function lineGC(seq::String)
    GCnumber=count(x->(x=='G'||x=='C'||x=='g'||x=='c'),seq)
    lineNum=count(x->(x!='N' && x!='n'),seq)
    (GCnumber,lineNum)
end

function calGC(fs)
    GCnumber=zero(Int)
    lineNum=zero(Int)
    open(fs,"r") do IOstream
        for line in eachline(IOstream)
            if startswith(line,">")
                continue
            else
                GC,all=lineGC(line)
                GCnumber+=GC
                lineNum+=all
            end
        end
    end
    round(GCnumber/lineNum;digits=3)
end

println("GC content: ",calGC(ARGS[1]))

python

import sys

def lineGC(seq):
   tmp=[base for base in seq if base =="G" or base =="g" or base == "C" or base == "c"]
   gcNumber=len(tmp)
   tmp2=[base for base in seq if base !="N" and base !="n"]
   allNumber=len(tmp2)
   return (gcNumber,allNumber)


with open(sys.argv[1],'r') as f:
    gcNum=0
    allNum=0
    for line in f:
       if line.startswith(">"):
           continue
       else:
           gc,alln=lineGC(line.strip("\n"))
           gcNum=gcNum+gc
           allNum=allNum+alln

print("GC content: {:.3f}".format(gcNum/allNum))

运行时间测试

python

python

julia


julia

perl

perl

总结

结果令人咋舌,可以从sys时间看出来python和perl都是立马启动,而julia在函数的即时编辑上花了一点时间(一半时间)。 总体用时上,julia仅比perl快了1秒,而python却用了惊人的9分钟,?

后记

python 也不是这么不堪,想要提速还是可以有很多办法的,比如切换pypy, 或者也用正则表达式,例如:

import sys
import re


def lineGC(seq):
    pattern_1 = re.compile(r"G|C",re.I)
    pattern_2 = re.compile(r"N",re.I)
    gcNumber=len(pattern_1.findall(seq))
    allNumber=len(seq)-len(pattern_2.findall(seq))
    return (gcNumber,allNumber)


with open(sys.argv[1],'r') as f:
    gcNum=0
    allNum=0
    for line in f:
       if line.startswith(">"):
           continue
       else:
           gc,alln=lineGC(line.strip("\n"))
           gcNum=gcNum+gc
           allNum=allNum+alln

print("GC content: {:.3f}".format(gcNum/allNum))

这样计算下来,大概需要6分20秒,提速很多

另外,我们也可以使用NumPy的向量化运算来提速

import sys
from pyfaidx import Fasta
import numpy as np

def lineGC(seq):
    gc_number = np.where((seq==b'G')|(seq==b'C')|(seq==b'g')|(seq==b'c'))[0].shape[0]
    n_number = np.where((seq==b'N')|(seq==b'n'))[0].shape[0]
    allnumber = seq.shape[0] - n_number
    return (gc_number,allnumber)

def calGC(fs):
    GC = 0
    all = 0
    hg38 = Fasta(fs)
    for record in hg38:
        seq = np.asarray(record)
        gc_number,all_number=lineGC(seq)
        GC = GC + gc_number
        all = all + all_number
    return (GC, all)

if __name__ == "__main__":
    gcNum, allNum = calGC(sys.argv[1])
    print("GC content: {:.3f}".format(gcNum/allNum))

这样的话,就只需要2分22秒了,已经是非常快的了,但是和perl还是有差距的。

最后,难道julia真的速度和perl就相差无几吗?

答案是否定的,因为julia设计是为了科学计算的,但是其字符串的性能并算不上优秀,我们可以调用BioSequence来处理生物序列

using BioSequences
using FASTX

function lineGC(seq)
    GCnumber=count(x->(x==DNA_G||x==DNA_C),seq)
    lineNum=length(seq)-count(isambiguous,seq)
    GCnumber,lineNum
end

function calGC(fs)
    GCnumber=zero(Int)
    lineNum=zero(Int)
    reader=open(FASTA.Reader,fs)
    for record in reader
        GC,all=lineGC(FASTA.sequence(record))
        GCnumber+=GC
        lineNum+=all
    end
    close(reader)
    round(GCnumber/lineNum;digits=3)
end

println("GC content: ",calGC(ARGS[1]))

这样就只需要11秒就可以计算出答案了

有关python, perl 和julia的性能对比的更多相关文章

  1. python - 如何使用 Ruby 或 Python 创建一系列高音调和低音调的蜂鸣声? - 2

    关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。

  2. ruby - 如何将脚本文件的末尾读取为数据文件(Perl 或任何其他语言) - 2

    我正在寻找执行以下操作的正确语法(在Perl、Shell或Ruby中):#variabletoaccessthedatalinesappendedasafileEND_OF_SCRIPT_MARKERrawdatastartshereanditcontinues. 最佳答案 Perl用__DATA__做这个:#!/usr/bin/perlusestrict;usewarnings;while(){print;}__DATA__Texttoprintgoeshere 关于ruby-如何将脚

  3. Python 相当于 Perl/Ruby ||= - 2

    这个问题在这里已经有了答案:关闭10年前。PossibleDuplicate:Pythonconditionalassignmentoperator对于这样一个简单的问题表示歉意,但是谷歌搜索||=并不是很有帮助;)Python中是否有与Ruby和Perl中的||=语句等效的语句?例如:foo="hey"foo||="what"#assignfooifit'sundefined#fooisstill"hey"bar||="yeah"#baris"yeah"另外,类似这样的东西的通用术语是什么?条件分配是我的第一个猜测,但Wikipediapage跟我想的不太一样。

  4. java - 什么相当于 ruby​​ 的 rack 或 python 的 Java wsgi? - 2

    什么是ruby​​的rack或python的Java的wsgi?还有一个路由库。 最佳答案 来自Python标准PEP333:Bycontrast,althoughJavahasjustasmanywebapplicationframeworksavailable,Java's"servlet"APImakesitpossibleforapplicationswrittenwithanyJavawebapplicationframeworktoruninanywebserverthatsupportstheservletAPI.ht

  5. 华为OD机试用Python实现 -【明明的随机数】 2023Q1A - 2

    华为OD机试题本篇题目:明明的随机数题目输入描述输出描述:示例1输入输出说明代码编写思路最近更新的博客华为od2023|什么是华为od,od薪资待遇,od机试题清单华为OD机试真题大全,用Python解华为机试题|机试宝典【华为OD机试】全流程解析+经验分享,题型分享,防作弊指南华为o

  6. python - 如何读取 MIDI 文件、更改其乐器并将其写回? - 2

    我想解析一个已经存在的.mid文件,改变它的乐器,例如从“acousticgrandpiano”到“violin”,然后将它保存回去或作为另一个.mid文件。根据我在文档中看到的内容,该乐器通过program_change或patch_change指令进行了更改,但我找不到任何在已经存在的MIDI文件中执行此操作的库.他们似乎都只支持从头开始创建的MIDI文件。 最佳答案 MIDIpackage会为您完成此操作,但具体方法取决于midi文件的原始内容。一个MIDI文件由一个或多个音轨组成,每个音轨是十六个channel中任何一个上的

  7. 「Python|Selenium|场景案例」如何定位iframe中的元素? - 2

    本文主要介绍在使用Selenium进行自动化测试或者任务时,对于使用了iframe的页面,如何定位iframe中的元素文章目录场景描述解决方案具体代码场景描述当我们在使用Selenium进行自动化测试的时候,可能会遇到一些界面或者窗体是使用HTML的iframe标签进行承载的。对于iframe中的标签,如果直接查找是无法找到的,会抛出没有找到元素的异常。比如近在咫尺的例子就是,CSDN的登录窗体就是使用的iframe,大家可以尝试通过F12开发者模式查看到的tag_name,class_name,id或者xpath来定位中的页面元素,会抛出NoSuchElementException异常。解决

  8. python ffmpeg 使用 pyav 转换 一组图像 到 视频 - 2

    2022/8/4更新支持加入水印水印必须包含透明图像,并且水印图像大小要等于原图像的大小pythonconvert_image_to_video.py-f30-mwatermark.pngim_dirout.mkv2022/6/21更新让命令行参数更加易用新的命令行使用方法pythonconvert_image_to_video.py-f30im_dirout.mkvFFMPEG命令行转换一组JPG图像到视频时,是将这组图像视为MJPG流。我需要转换一组PNG图像到视频,FFMPEG就不认了。pyav内置了ffmpeg库,不需要系统带有ffmpeg工具因此我使用ffmpeg的python包装p

  9. Python 刷Leetcode题库,顺带学英语单词(31) - 2

    ValidPalindromeGivenastring,determineifitisapalindrome,consideringonlyalphanumericcharactersandignoringcases. [#125]Example:"Aman,aplan,acanal:Panama"isapalindrome."raceacar"isnotapalindrome.Haveyouconsiderthatthestringmightbeempty?Thisisagoodquestiontoaskduringaninterview.Forthepurposeofthisproblem

  10. python - 是否可以使用 Ruby 或 Python 禁用 anchor /引用来发出有效的 YAML? - 2

    是否可以在PyYAML或Ruby的Psych引擎中禁用创建anchor和引用(并有效地显式列出冗余数据)?也许我在网上搜索时遗漏了一些东西,但在Psych中似乎没有太多可用的选项,而且我也无法确定PyYAML是否允许这样做.基本原理是我必须序列化一些数据并将其以可读的形式传递给一个不是真正的技术同事进行手动验证。有些数据是多余的,但我需要以最明确的方式列出它们以提高可读性(anchor和引用是提高效率的好概念,但不是人类可读性)。Ruby和Python是我选择的工具,但如果有其他一些相当简单的方法来“展开”YAML文档,它可能就可以了。 最佳答案

随机推荐