SSE2 Smith-Waterman; unfinished

This commit is contained in:
Heng Li 2011-05-05 12:42:29 -04:00
parent 9aaabb7bea
commit e7d8f752d9
2 changed files with 177 additions and 0 deletions

158
ksw.c Normal file
View File

@ -0,0 +1,158 @@
#include <stdlib.h>
#include <stdint.h>
#include <emmintrin.h>
#include "ksw.h"
#include <stdio.h>
struct _ksw_query_t {
int slen;
uint8_t shift;
uint8_t *mem;
__m128i *qp, *H0, *H1, *E;
};
ksw_query_t *ksw_qinit(int qlen, const uint8_t *query, int p, int m, const int8_t *mat)
{
ksw_query_t *q;
uint8_t *aligned;
int8_t *t;
int qlenp, slen, a, tmp;
q = calloc(1, sizeof(ksw_query_t));
q->slen = slen = (qlen + p - 1) / p; // length of segment
qlenp = slen * p; // qlenp can be divided by p
q->mem = malloc(64 + qlenp * (m + 2));
aligned = (uint8_t*)(((size_t)q->mem + 15) & ~0xful); // align memory
q->qp = (__m128i*)aligned;
q->H0 = q->qp + qlenp * m;
q->H1 = q->H0 + qlenp;
q->E = q->H1 + qlenp;
// compute shift
tmp = m * m;
for (a = 0, q->shift = 127; a < tmp; ++a) // find the minimum score (should be negative)
if (mat[a] < (int8_t)q->shift) q->shift = mat[a];
q->shift = 256 - q->shift;
// An example: p=8, qlen=19, slen=3 and segmentation:
// {{0,3,6,9,12,15,18,-1},{1,4,7,10,13,16,-1,-1},{2,5,8,11,14,17,-1,-1}}
t = (int8_t*)q->qp;
for (a = 0; a < m; ++a) {
int i, k;
const int8_t *ma = mat + a * m;
for (i = 0; i < slen; ++i)
for (k = i; k < qlenp; k += slen) { // p iterations
*t++ = (k >= qlen? 0 : ma[query[k]]) + q->shift;
//printf("%d,%d,%d,%d\n", a, i, k, *(t-1));
}
}
return q;
}
void ksw_qdestroy(ksw_query_t *q)
{
if (!q) return;
free(q->mem); free(q);
}
int ksw_sse2_16(ksw_query_t *q, int tlen, const uint8_t *target, unsigned _o, unsigned _e) // the first gap costs -(_o+_e)
{
int slen, i, score;
__m128i zero, gapo, gape, shift, gmax, *H0, *H1, *E;
#define __set_16(ret, xx) do { \
uint16_t t16 = ((uint16_t)(xx) << 8) | ((uint16_t)(xx) & 0x00ff); \
(ret) = _mm_insert_epi16((ret), t16, 0); \
(ret) = _mm_shufflelo_epi16((ret), 0); \
(ret) = _mm_shuffle_epi32((ret), 0); \
} while (0)
#define __max_16(ret, xx) do { \
__m128i t; \
t = _mm_srli_si128((xx), 8); \
(xx) = _mm_max_epu8((xx), t); \
t = _mm_srli_si128((xx), 4); \
(xx) = _mm_max_epu8((xx), t); \
t = _mm_srli_si128((xx), 2); \
(xx) = _mm_max_epu8((xx), t); \
t = _mm_srli_si128((xx), 1); \
(xx) = _mm_max_epu8((xx), t); \
(ret) = _mm_extract_epi16((xx), 0) & 0x00ff; \
} while (0)
// initialization
zero = _mm_xor_si128(zero, zero);
gmax = _mm_xor_si128(gmax, gmax);
__set_16(gapo, _o + _e);
__set_16(gape, _e);
H0 = q->H0; H1 = q->H1; E = q->E;
slen = q->slen;
__set_16(shift, q->shift);
for (i = 0; i < slen; ++i) {
_mm_store_si128(E + i, zero);
_mm_store_si128(H0 + i, zero);
}
// the core loop
for (i = 0; i < tlen; ++i) {
int j, cmp;
__m128i e, h, f, max, t, *S = q->qp + target[i] * slen; // s is the 1st score vector
max = _mm_xor_si128(max, max);
f = _mm_xor_si128(f, f);
h = _mm_load_si128(H0 + slen - 1); // h={2,5,8,11,14,17,-1,-1} in the above example
h = _mm_slli_si128(h, 1); // h=H(i-1,-1); << instead of >> because x86 is little-endian
for (score=0;score<16;++score)printf("%d ", ((int8_t*)&S[0])[score]);printf("\n");
for (j = 0; j < slen; ++j) {
// at the beginning, h=H'(i-1,j-1)
h = _mm_adds_epu8(h, S[j]); h = _mm_subs_epu8(h, shift); // h=H'(i-1,j-1)+S(i,j)
e = _mm_load_si128(E + j); // e=E'(i,j)
h = _mm_max_epu8(h, e);
h = _mm_max_epu8(h, f); // h=H'(i,j)
max = _mm_max_epu8(max, h); // set max
for (score=0;score<16;++score)printf("%d ", ((int8_t*)&h)[score]);printf("\n");
_mm_store_si128(H1 + j, h); // save to H'(i,j)
// now compute E(i+1,j)
h = _mm_subs_epu8(h, gapo); // h=H'(i,j)-gapo
e = _mm_subs_epu8(e, gape); // e=E'(i,j)-gape
e = _mm_max_epu8(e, h); // e=E'(i+1,j)
_mm_store_si128(E + j, e); // save to E'(i+1,j)
// now compute F'(i,j+1)
f = _mm_subs_epu8(f, gape);
f = _mm_max_epu8(f, h);
// get H'(i-1,j) and prepare for the next j
h = _mm_load_si128(H0 + j); // h=H'(i-1,j)
}
gmax = _mm_max_epu8(gmax, max);
j = 0;
h = _mm_load_si128(H1); // h=H(i,0); h->{0,3,6,9,12,15,18,-1}
f = _mm_slli_si128(f, 1); // f->{-1,3,6,9,12,15,18,-1}
t = _mm_subs_epu8(h, gapo); t = _mm_subs_epu8(f, t);
t = _mm_cmpeq_epi8(t, zero); cmp = _mm_movemask_epi8(t); // test whether s>0
while (cmp != 0xffff) { // the lazy-F loop
h = _mm_max_epu8(h, f);
_mm_store_si128(H1 + j, h);
e = _mm_load_si128(E + j); // e=E'(i,j)
h = _mm_subs_epu8(h, gapo); // h=H(i,j)-gapo
e = _mm_max_epu8(e, h); // e=E(i,j)
_mm_store_si128(E + j, e); // save to E(i,j)
f = _mm_subs_epu8(f, gape); // f-=gape
if (++j >= slen) j = 0, f = _mm_slli_si128(f, 1); // restart
h = _mm_load_si128(H1 + j);
t = _mm_subs_epu8(h, gapo); t = _mm_subs_epu8(f, t);
t = _mm_cmpeq_epi8(t, zero); cmp = _mm_movemask_epi8(t);
}
S = H1; H1 = H0; H0 = S; // swap H0 and H1
}
__max_16(score, gmax);
return score;
}
int main()
{
int8_t mat[] = {1, -3, -3, 1};
uint8_t *t = (uint8_t*)"\1\0\1\1\0\0";
uint8_t *q = (uint8_t*)"\1\1";
ksw_query_t *qq = ksw_qinit(2, q, 16, 2, mat);
int s = ksw_sse2_16(qq, 6, t, 5, 2);
ksw_qdestroy(qq);
printf("%d\n", s);
return 0;
}

19
ksw.h Normal file
View File

@ -0,0 +1,19 @@
#ifndef __AC_KSW_H
#define __AC_KSW_H
struct _ksw_query_t;
typedef struct _ksw_query_t ksw_query_t;
#ifdef __cplusplus
extern "C" {
#endif
ksw_query_t *ksw_qinit(int qlen, const uint8_t *query, int p, int m, const int8_t *mat);
void ksw_qdestroy(ksw_query_t *q);
int ksw_sse2_16(ksw_query_t *q, int tlen, const uint8_t *target, unsigned o, unsigned e); // first gap costs -(o+e)
#ifdef __cplusplus
}
#endif
#endif