滑动对角线矢量包含16个元素,每个元素都是一个8位无符号整数。
如果没有SSE并且有点简化,它在C中会看起来像这样:
int width=1000000; // a big number
uint8_t matrix[width][16];
fill_matrix_with_interesting_values(&matrix);
for (int i=0; i < width - 16; ++i) {
uint8_t diagonal_vector[16];
for (int j=0; j<16; ++j) {
diagonal_vector[j] = matrix[i+j][j];
}
do_something(&diagonal_vector);
}
但在我的情况下,我只能使用_mm_load_si128
内在函数从矩阵中逐列(垂直)加载。滑动对角线矢量水平移动,因此我需要提前加载16个列向量,并使用每个列向量中的一个元素来创建对角矢量。
是否可以使用SSE为此进行快速低内存实现?
2016年11月14日更新:提供更多详情。在我的例子中,我从FASTA format中的文本文件中读取单字母代码。每个字母代表一定的氨基酸。每个氨基酸具有与其相关的特定柱载体。从常量表(BLOSUM矩阵)中查找该列向量。在C代码中它看起来像这样
while (uint8_t c = read_next_letter_from_file()) {
column_vector = lookup_from_const_table(c)
uint8_t diagonal_vector[16];
... rearrange the values from the latest column
vectors into the diagonal_vector ...
do_something(&diagonal_vector)
}
答案 0 :(得分:3)
我将提出的实现每次迭代只需要一个列加载。首先我们初始化一些变量
const __m128i mask1=_mm_set_epi8(0,0,0,0,0,0,0,0,255,255,255,255,255,255,255,255);
const __m128i mask2=_mm_set_epi8(0,0,0,0,255,255,255,255,0,0,0,0,255,255,255,255);
const __m128i mask3=_mm_set_epi8(0,0,255,255,0,0,255,255,0,0,255,255,0,0,255,255);
const __m128i mask4=_mm_set_epi8(0,255,0,255,0,255,0,255,0,255,0,255,0,255,0,255);
__m128i v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15;
然后,对于每个步骤,变量v_column_load
都会加载下一列。
v15 = v_column_load;
v7 = _mm_blendv_epi8(v7,v15,mask1);
v3 = _mm_blendv_epi8(v3,v7,mask2);
v1 = _mm_blendv_epi8(v1,v3,mask3);
v0 = _mm_blendv_epi8(v0,v1,mask4);
v_diagonal = v0;
在下一步中,v0
,v1
,v3
,v7
,v15
中的变量名称编号增加1并调整为范围0到15.换句话说:newnumber =(oldnumber + 1)modulo 16.
v0 = v_column_load;
v8 = _mm_blendv_epi8(v8,v0,mask1);
v4 = _mm_blendv_epi8(v4,v8,mask2);
v2 = _mm_blendv_epi8(v2,v4,mask3);
v1 = _mm_blendv_epi8(v1,v2,mask4);
v_diagonal = v1;
经过16次迭代后,v_diagonal
将开始包含正确的对角线值。
查看mask1
,mask2
,mask3
,mask4
,我们看到一种模式可用于将此算法推广到其他向量长度(2 ^ n) 。
例如,对于矢量长度为8,我们只需要3个掩码,迭代步骤如下:
v7 = a a a a a a a a
v6 =
v5 =
v4 =
v3 = a a a a
v2 =
v1 = a a
v0 = a
v0 = b b b b b b b b
v7 = a a a a a a a a
v6 =
v5 =
v4 = b b b b
v3 = a a a a
v2 = b b
v1 = a b
v1 = c c c c c c c c
v0 = b b b b b b b b
v7 = a a a a a a a a
v6 =
v5 = c c c c
v4 = b b b b
v3 = a a c c
v2 = a b c
v2 = d d d d d d d d
v1 = c c c c c c c c
v0 = b b b b b b b b
v7 = a a a a a a a a
v6 = d d d d
v5 = c c c c
v4 = b b d d
v3 = a a c d
v3 = e e e e e e e e
v2 = d d d d d d d d
v1 = c c c c c c c c
v0 = b b b b b b b b
v7 = a a a a e e e e
v6 = d d d d
v5 = a a c c e e
v4 = a b b d a
v4 = f f f f f f f f
v3 = e e e e e e e e
v2 = d d d d d d d d
v1 = c c c c c c c c
v0 = b b b b f f f f
v7 = a a a a e e e e
v6 = b b d d f f
v5 = a b c d e f
v5 = g g g g g g g g
v4 = f f f f f f f f
v3 = e e e e e e e e
v2 = d d d d d d d d
v1 = c c c c g g g g
v0 = b b b b f f f f
v7 = a a c c e e g g
v6 = a b c d e f g
v6 = h h h h h h h h
v5 = g g g g g g g g
v4 = f f f f f f f f
v3 = e e e e e e e e
v2 = d d d d h h h h
v1 = c c c c g g g g
v0 = b b d d f f h h
v7 = a b c d e f g h <-- this vector now contains the diagonal
v7 = i i i i i i i i
v6 = h h h h h h h h
v5 = g g g g g g g g
v4 = f f f f f f f f
v3 = e e e e i i i i
v2 = d d d d h h h h
v1 = c c e e g g i i
v0 = b c d e f g h i <-- this vector now contains the diagonal
v0 = j j j j j j j j
v7 = i i i i i i i i
v6 = h h h h h h h h
v5 = g g g g g g g g
v4 = f f f f j j j j
v3 = e e e e i i i i
v2 = d d f f h h j j
v1 = c d e f g h i j <-- this vector now contains the diagonal
旁注:当我处理Smith-Waterman算法的an implementation时,我发现了这种加载对角矢量的方法。可以在旧的SourceForge项目web page上找到更多信息。