非抗生素DNA序列仅由核碱基腺嘌呤(A)、胞嘧啶(C)、鸟嘌呤(G)、胸腺嘧啶(T)组成。对于人类消费,碱基可以用相应的char
表示,在char
或char
中:A
、C
、G
、T
或a
、c
、g
、t
。然而,当需要存储长序列时,这种表示是低效的。由于只需要存储四个符号,每个符号都可以分配一个2位代码。2 UCSC规定的常用.2bit
正是这样做的,使用以下编码:T = 0b00
,C = 0b01
,A = 0b10
,G = 0b11
。
下面的C代码显示了为清晰起见而编写的参考实现。各种转换以char
序列表示的基因组序列的开源软件通常使用由序列中的每个char
索引的256条目查找表。这也与char
的内部表示隔离。然而,存储器访问在能量上是昂贵的,即使访问的是片上缓存,并且通用表查找难以SIMD化。因此,如果转换可以通过简单的整数运算来完成,这将是有利的。考虑到ASCII是主要的char
编码,可以限制于此。
什么是有效的计算方法来转换作为ASCII字符的核碱基到它们的.2bit
-表示?
/* Convert nucleobases A, C, G, T represented as either uppercase or lowercase
ASCII characters into UCSC .2bit-presentation. Passing any values other than
those corresponding to 'A', 'C', 'G', 'T', 'a', 'c', 'g', 't' results in an
indeterminate response.
*/
unsigned int ascii_to_2bit (unsigned int a)
{
const unsigned int UCSC_2BIT_A = 2;
const unsigned int UCSC_2BIT_C = 1;
const unsigned int UCSC_2BIT_G = 3;
const unsigned int UCSC_2BIT_T = 0;
switch (a) {
case 'A':
case 'a':
return UCSC_2BIT_A;
break;
case 'C':
case 'c':
return UCSC_2BIT_C;
break;
case 'G':
case 'g':
return UCSC_2BIT_G;
break;
case 'T':
case 't':
default:
return UCSC_2BIT_T;
break;
}
}
字符串
3条答案
按热度按时间kmynzznz1#
如果我们仔细观察碱基的ASCII字符的二进制代码,就会发现第1位和第2位提供了一个唯一的两位代码:
A
=0b01000001
->0b00
,C
=0b01000011
->0b01
,G
=0b01000111
->0b11
,T
=0b01010100
->0b10
。类似于ASCII字符,仅在第5位不同。不幸的是,这种简单的Map并不完全匹配.2bit
编码,其中A和T的代码被交换。解决这个问题的一种方法是用一个简单的四项排列表存储在一个变量中,可能在优化后分配给一个寄存器(“寄存器内查找表”):字符串
另一种方法通过观察
A
和T
的位1为0,但C
和G
为1。因此,我们可以通过对 inverse 进行XOR来交换A和T的编码。将输入的位1与初始代码的位1进行比较:型
这个版本在具有快速位域提取指令的处理器和没有桶形移位器的低端处理器上很有优势,因为只需要右移一个位位置。在乱序处理器上,这种方法提供了比置换表更高的并行度。它似乎更容易适应SIMD实现,因为所有字节通道都使用相同的移位计数。
在我仔细研究相关ASCII字符的二进制编码之前,我研究了一个简单的数学计算,对较小的乘数和除数进行简单的蛮力搜索,得到:
型
乘法器
18
对于没有快速整数乘法器的处理器来说是友好的。现代编译器可以有效地处理带有编译时常数除数的模计算,并且不需要除法。即使如此,我注意到即使是最好的编译器也很难利用非常有限的输入和输出范围,所以我不得不手动修改它来简化代码:型
然而,在64位平台上,这整个计算可以用64位、32条目的查找表来代替,如果这个64位表可以有效地放入寄存器中,则该查找表可以给予有竞争力的性能,这是x86-64的情况,其中它被作为立即数加载。
型
我添加了我的测试框架以供参考:
型
zbq4xfa02#
一种选择是:
字符串
其优点在于,它只需要8位,不会溢出,并且不包含非常数移位,因此即使没有特定的SIMD指令,例如,在64位字中输入8个字符,也可以立即用于并行化
型
在每个字节的底部留下两个输出位。
wz8daaqr3#
下面的例程将用ASCII字符串的内容填充
uint32_t
值的数组,该ASCII字符串由您指定的字符填充,并将保存状态,以便能够附加第二个,第三个,使用它的方法通过一个main()
例程来说明,该例程从命令行获取字符串参数,并将它们传递给TOTAL
元素。例程的参数在它上面的注解中描述。字符串
这个函数可以单独链接到任何程序中,但我提供了一个
main()
代码来说明函数的使用。您可以在命令行参数中使用ASCII编码的实际链来调用程序。程序将一个接一个地对它们进行编码,以演示多重转换(16个碱基适合一个32位整数,正如您在问题中发布的格式定义所述,因此,如果没有编码16的倍数,则状态反映了最后一个中覆盖了多少位。代码继续执行下面的main函数:
型