算法
可以使用动态规划方法计算两个序列的最佳局部对齐分数。Smith和Waterman算法的递推关系[1]经过Gotoh的修改[2]对于仿射间隙惩罚函数如下所示。
查询顺序问长度的米含有残留物问
我
.数据库序列d日长度的n个含有残留物d日
j个
.H(H)
i、 j个
是对齐的前缀的分数问和d日以残留物排列结束问
我
和d日
j个
.E类
i、 j个
和F类
i、 j个
是对齐相同前缀的分数问和d日但在查询和数据库序列中分别出现了一个缺口。P(P)[问
我,
d日
j个
]是对齐残留物的分数问
我
和d日
j个
根据替代得分矩阵P(P).问是缺口开放和延期罚款的总和,而对是缺口扩展缺口惩罚。S公司是整体最优局部对齐分数。
计算是逐列进行的。仅部分H(H),E类和F类矩阵需要保存在内存中:F类矩阵以及两个包含米元素,对应于H(H)和E类矩阵。
实施
实现的主要功能如下所述。
16个数据库序列的并行化
16个不同数据库序列的残留物并行处理,如图所示1天。这16个残留物都同时与相同的查询残留物进行比较。这些操作是使用由16个独立字节组成的向量执行的。16个残基被送入16个独立的通道。当这十六个数据库序列中的第一个结束时,下一个数据库序列的第一个剩余部分将加载到通道中。按照在原始数据库文件中找到的顺序读取数据库序列。与Rudnicki的方法相反等. [14],数据库不按序列长度排序。图2说明了这种方法。
十条指令的紧凑核心代码
计算对齐矩阵中每个单元格中的值的基础是算法部分中描述的递归关系。计算可以用十条汇编指令编写,这十条指令构成了计算内部循环的核心,如图所示三。这十条指令并行计算独立对齐矩阵中16个单元格的每个矢量的值。指令的准确选择及其顺序很重要;因此,这部分代码是在汇编程序中手工编码的,以最大限度地提高性能。在图中,H(H)表示主得分向量。这个H(H)向量保存在N个对角线上下一个单元格的向量。E类和F类分别表示查询序列和数据库序列中以间隙结束的对齐的得分向量。P(P)是数据库序列的替换分数与查询残差的向量问(见下面的临时分数档案)。问表示间隙开放加上间隙扩展惩罚的向量。对表示间隙扩展惩罚向量。S公司表示当前的最佳得分向量。所有矢量,除了N个在此代码之前初始化。
沿着数据库序列处理四个连续的单元格
在矩阵单元的计算过程中,两个数组中的值H(H)和E类每个矩阵单元的值通常必须读写一次。这些数组通常足够小,可以在接近的缓存级别进行缓存,因此内存访问时间不应成为主要问题,但仍需要为每个单元格写回和读回它们。由于有16个128位xxm寄存器可用,并且有足够的空间用于保存H(H),E类和F类寄存器中几个单元格的值,可以通过计算数据库序列中的几个连续单元格来减少运行时间,然后再进行下一个查询剩余。发现四个连续的细胞表现良好。沿着查询序列展开一次内部循环也能很好地工作。然后,基本计算块由两乘以四个单元组成,这些单元在每个内部循环迭代中进行处理,如图所示4.
更新分数和填充块
当一个新的数据库序列在其中一个通道中开始时,必须记录上一个数据库序列的分数。此外H(H),E类和S公司必须重置前一序列的分数。当要处理新列时,将检查是否有任何数据库序列在前一列中结束。如果是这样,则进行特殊处理。记录结束的序列的分数。创建一个掩码,稍后用于重置H(H),E类和S公司在处理新列之前在适当的通道中。通道被填充,并且一个或多个新序列被启动。如果前一列中没有结束的序列,则会对新列执行更简单、更快的处理步骤。在更简单的处理步骤中,无需保存分数,无需启动新序列,也无需创建或使用掩码。由于大多数列的类型都比较简单,因此总体性能主要取决于处理简单列的速度。为了简化计算,如果每个通道的长度不是4的倍数,则在序列结束后用1-3个空符号填充每个通道,以确保新的数据库序列只在新单元块的开始处开始。此填充在图中以粉红色表示2。填充会稍微增加电池的总数,但只允许对每四个残留物执行上述检查。
临时分数曲线的计算
为了使计算速度更快,很重要的一点是可以快速加载替换得分值的向量。每个得分向量对应于单个查询残差相对于不同数据库序列中16个残差的得分。如图所示,创建了一种临时分数配置文件5该得分简档对于将任何查询残基与来自16个数据库序列的4个连续残基进行匹配是有效的。对于数据库序列中的每四个残基,必须构建一个新的分数剖面。
临时分数配置文件是使用一系列压缩混洗指令(pshufb)从普通替换分数矩阵(例如BLOSUM62)和4×16数据库序列残差创建的。洗牌指令仅在含补充数据流单指令多数据扩展指令集3(SSSE3)的英特尔处理器上可用。在没有SSSE3的处理器上(即AMD处理器和较旧的Intel处理器),可以使用一系列解包指令(punpcklbw、punpckhbw),通过一种矩阵转置操作来代替计算,但速度损失不大。
分数范围和算术指令的选择
最初只使用7位的分数范围进行计算。这允许使用SSE2指令并行计算16个校准分数矩阵。加减运算是使用有符号和饱和算术进行的,而最大运算是对无符号数进行的。只使用-128到-1(有符号数字)或128到255(无符号数字)之间的7位分数范围。所有分数的偏差为128。这个值范围将确保有符号饱和加减法在下边界上很好地工作。此外,无符号最大值在这个范围内也能很好地工作。该范围允许使用压缩最大无符号字节指令(pmaxub)和压缩加减有符号饱和字节指令(paddsb和psubsb),这些指令在所有SSE2处理器上都可用,速度最高。
由于速度较慢,因此不使用8位范围,该范围允许与7位范围相同数量(16)的并行计算,同时允许更宽的分数范围。可以使用-128到127的范围,也可以使用0到255的范围。并行计算最大字节数(pmaxub,pmaxsb)和并行加减字节数(paddusb,psubusb,paddsb,psubsb)的指令都有有符号和无符号版本,但用于最大有符号字节数的pmaxsb-指令最近才在SSE4.1中可用,速度比pmaxub-慢。可以使用无符号加减法(paddusb和psubusb),但分数矩阵向量的加法需要两条指令。首先必须添加包含偏差的得分向量;然后必须减去偏差。
还实现了使用16位和63位分数范围的版本,并在分数范围较低的计算中检测到溢出时使用。16位版本允许8个并行计算。当在分数范围较窄的计算中检测到潜在溢出时,将使用下一个较宽的分数范围(首先是16位,必要时再是63位)重新计算该数据库序列的对齐分数。由于相当少的序列通常会达到无法用7位表示的分数,因此较宽分数范围的额外计算时间通常可以忽略不计。
在使用较窄的得分范围处理了数据库序列块(见下文)中的所有序列之后,对序列子集执行具有较宽得分范围的重新计算。
读取序列数据库
数据库序列以由formatdb工具生成的NCBI BLAST数据库格式存储。这是一种二进制格式,其中序列信息至少分为3个文件:索引(.pin)、标头(.phr)和序列(.psq)。文件格式允许高效地将序列读入内存。蛋白质序列使用1-24和26-27范围内的字节值存储,字节值分别表示氨基酸残基A-I、K-N、P-T、V-Z、U、O和J。序列由零字节分隔,这简化了序列结束的检查。
使用.pin和.psq文件的内存映射检索数据库序列。这是访问序列的一种有效且方便的方法,在这种方法中,操作系统管理在必要时从磁盘将数据读入内存,同时执行程序。序列数据库被划分为每个线程100个块。每个块包含大致相同数量的序列。一个组块中的序列被映射到内存中,并在映射下一个组块之前进行处理。这会导致程序占用较小的内存。
多个线程
SWIPE使用多个线程(pthreads)在序列数据库的不同部分上工作。线程数是在启动程序时指定的,通常应等于计算机的内核数。对于具有超线程的最新几代英特尔处理器,线程数等于逻辑内核数通常是最有效的。当线程准备进行更多工作时,会将数据库序列块分配给线程,因此线程可能不会处理完全相同数量的块。线程的结果在处理每个块后插入到一个常见的命中列表中。
测试
SWIPE软件在许多不同条件下根据BLAST、BLAST+、STRIPED和SWPS3进行了基准测试,以测量速度。研究了使用可变线程数的性能以及查询序列长度的影响。此外,还研究了不同评分系统、替代评分矩阵以及差距开放和延期处罚的影响。
线程
图6A级表示以1到24个线程、375个剩余长度的P07327查询序列、BLOSUM62矩阵以及间隙打开和扩展惩罚分别为11和1的情况下运行的程序的性能。SWIPE使用单个线程以9.1 GCUPS的速度运行,并以106.2 GCUPS的19个线程达到其最大性能,但超过12个线程的额外线程几乎没有任何收益。它的伸缩性很好,最大加速比(最大速度与单线程速度之比)为11.6。SWPS3使用单个线程以3.4 GCUPS的速度运行,在12个线程和16.4 GCUPS的情况下达到了最佳性能,但在9个线程之后几乎没有什么收益。最大加速比为4.8。令人惊讶的是,使用两个线程的速度不如使用单个线程的速度。用GNU编译器编译的STRIPED在3.1 GCUPS下运行,只有一个线程,在14.7 GCUPS下有23个线程达到了最大性能,但在超过12个线程的情况下几乎没有收获。最大加速比为4.8。使用英特尔编译器编译STRIPED后,在单线程上运行时,速度提高了21%,达到3.7 GCUPS,但在23个线程上运行的GCUPS速度仅提高了2%,达到15.0 GCUPS。这对应于4.0的最大加速。BLAST和BLAST+分别以14.7和15.5 GCUPS的速度运行,当24个线程分别以208.4和178.9 GCUPS的速率运行时,单个线程的伸缩性非常好,并达到其最大性能。BLAST和BLAST+的最大加速比分别为14.2和11.5。
查询长度
图6亿和图6摄氏度说明了使用24个线程(B)或单个线程(C)执行不同长度的查询时的性能。32个不同的序列被用作查询,长度从24到5478个氨基酸残基。
SWIPE的性能曲线相当平坦。对于少于大约100个剩余数的查询,性能会逐渐下降,尤其是在运行多个线程时。当使用多个线程时,对于非常长的查询,速度也略有降低。24个线程的性能从23.1到110.1 GCUPS不等,单线程的性能则从3.9到9.8 GCUPS不等。
SWPS3程序非常依赖于查询长度,24个线程的速度在0.94到49.0 GCUPS之间,单个线程的速度在1.1到4.6 GCUPS之间。
用英特尔编译器编译的STRIPED程序也非常依赖于查询长度,尤其是在24个线程上运行时,速度从1.2到46.6 GCUPS不等。在单个线程上,STRIPED的速度在0.8到5.7 GCUPS之间变化。用GNU编译器编译的STRIPED通常稍慢,尤其是对于较长的查询。
BLAST程序的速度似乎更快,查询序列更长,BLAST和BLAST+的速度分别为52.8到374.1和30.6到360.2 GCUPS,但性能因序列而异。当查询长度小于大约100个残数时,性能明显下降。
评分系统
图7显示了不同评分系统下的表现。对BLAST允许的矩阵和间隙惩罚的所有组合进行了测试。使用375个残基长的P07327查询序列和24个线程。SWIPE的性能几乎恒定在102-106 GCUPS左右。STRIPED和SWPS3的性能也几乎恒定在14-15和15 GCUPS左右。BLAST的性能高度依赖于所使用的评分矩阵。使用BLOSUM50矩阵,SWIPE的速度几乎是普通BLAST的两倍。这两个程序的速度与PAM250矩阵非常相似,而其他矩阵的BLAST速度更快。总的来说,BLAST+比普通BLAST慢10-20%。差距惩罚对总体表现影响不大,但在少数情况下,相对较低的差距惩罚似乎会降低BLAST、BLAST+和STRIPED的速度(例如BLOSUM62,差距惩罚为9和1),而对SWIPE和SWPS3的影响可以忽略不计。