SSE4: MPSADBW — pomoc w wyszukiwaniu wzorców

Autor: Wojciech Muła
Dodany:13.04.2008

Przyznaję, że jest dość problematyczne, czy rzeczywiście użycie rozkazów SIMD może w tej klasie algorytmów pomóc, niemniej ciekawe to zastosowanie. Dopisek 7.04.2008 — owszem pomaga, testy które przeprowdziłem wykazują przyspieszenie 2, 3, 4 i więcej razy w stosunku do standardowej funckji strstr. Zobacz artykuł, tam również dostępne są źródła przetestowanych funkcji.

Skojarzyłem rozkaz MPSADBW z algorytmem Karpa-Rabina, w którym dokładne porównania wzorca i podciągu są wykonywane dopiero wtedy, gdy wartości funkcji haszującej dla wyszukiwanego wzorca i testowanego podciągu są równe.

Co prawda MPSADBW nie liczy żadnej funkcji skrótu, za to pozwala za jednym zamachem stwierdzić, na których pozycjach w zakresie 12 znaków jest zlokalizowany 4-znakowy wzorzec (SAD=0). Dopiero gdy ten fakt stwierdzimy można testować dokładniej, tzn. jeśli ów wzorzec był ledwie prefiksem dłuższego ciągu porównujemy kolejne znaki.

Oczywista wada tego rozwiązania: nie można wyszukiwać wzorców krótszych niż 4 znaki.

Szkic programu:

; edi -> wzorzec
; esi -> przeszukiwany łańcuch znaków
; ecx -> długość

        movdqa  (edi), %xmm0            ; xmm0 - prefiks wzorca
        pxor    %xmm7, %xmm7            ; xmm7 = packed_word(0)
0:
        movdqu  (esi), %xmm1            ; xmm1 - 12-bajtowy fragment łańcucha
        movdqa  %xmm1, %xmm2

        ; wyszukanie wystąpień
                                        ; xmm0 = [abcd .... .... ....] '.' - nieuwzględniane
                                        ; xmm2 = [XYab cdYa bcdX ....] '?' - liczba > 0
        mpsadbw $0, %xmm0, %xmm1        ; xmm1 = [??0? ???0] (packed word)
                                        ;        słowo = 0 -> podciągi równe (odległość między nimi zerowa)
        pcmpeqw %xmm7, %xmm1            ; xmm1 = [0010 0001]

        ; sprawdzenie wyniku

        ptest   %xmm1, %xmm1            ; ZF := 1, gdy xmm1 = 0, czyli w ogóle nie znaleziono prefiksu
        jz      1f                      ; przeskakujemy testy

        ; ... tutaj wykorzystujemy wyniki zapisane xmm1, np. tak

        ; w eax maska bitowa
        pmovmskb %xmm1, %eax

        ; interesują nas wyłącznie bity na pozycjach parzystych
        for i:=0 to 16 step 2 do
                begin
                        if %eax[i] = 1 then
                                if strcmp(%edi + 4, %esi + 4 + i/2) = 0 then
                                        ...
                end;

1:
        addl $8, %esi

        subl $8, %ecx
        jnz 0b

Można też uwzględniać prefiksy 8-bajtowe, co wymaga 2-krotnego wywołania MPSADBW i zmiany przyrostu ESI z 8 na 12, choć nie wydaje mi się to lepszym rozwiązaniem. Dla porządku jednak kawałek kodu:

; ...
movdqa  %xmm1, %xmm2

; wyszukanie wystąpień
                                ; xmm0 = [abcd .... .... ....] '.' - nieuwzględniane
                                ; xmm2 = [XYab cdYa bcde ....] '?' - liczba > 0
mpsadbw $0, %xmm0, %xmm1        ; xmm1 = [??0? ???0] (packed word)
                                ; xmm0 = [.... efgh .... ....] '.' - nieuwzględniane
                                ; xmm2 = [.... cdYa bcde fghZ] '?' - liczba > 0
mpsadbw $6, %xmm0, %xmm2        ; xmm2 = [???? ???0]
paddw   %xmm2, %xmm1            ; xmm1 = [???? ???0]
pcmpeqw %xmm7, %xmm1            ; xmm1 = [0000 0001]
; ...

addl $12, %esi
subl $12, %ecx
; ...

Firma Intel dla architektury Enhanced Core 2 podaje, że opóźnienie MPSADBW wynosi 4 cykle, zaś przepustowość 2 cykle. Zatem po 4 cyklach dostępna jest informacja, które z 8 kolejnych 4-bajtowych podciągów zawierają 4 bajtowy wzorzec — to daje pół cykla na porównanie (efektownie, ale czy efektywnie?); następnie wykonywany jest PCMPEQW (1 cykl), PTEST (1 cykl) i ewentualny skok (>= 1 cykl). W sumie 7 cykli.

Moje testy wskazują, że tego rodzaju predykcja nie zmusza do wykonywania dużej liczby dokładnych porównań — jednak kwestia, czy w praktyce będzie rzeczywiście tak różowo, jest otwarta.