Nadal pracuję nad moją implementacją SHA1 w Haskell. Ja teraz mam wdrożenie pracę i to jest wewnętrzna pętla:Optymalizacja pętli wewnętrznych Haskell
iterateBlock' :: Int -> [Word32] -> Word32 -> Word32 -> Word32 -> Word32 -> Word32 -> [Word32]
iterateBlock' 80 ws a b c d e = [a, b, c, d, e]
iterateBlock' t (w:ws) a b c d e = iterateBlock' (t+1) ws a' b' c' d' e'
where
a' = rotate a 5 + f t b c d + e + w + k t
b' = a
c' = rotate b 30
d' = c
e' = d
Profiler mi mówi, że funkcja ta zajmuje 1/3 wykonywania mojej realizacji. Nie mogę wymyślić sposobu, aby zoptymalizować go w inny sposób niż może wstawić zmienne tymczasowe, ale wierzę, że -O2 zrobi to za mnie.
Czy ktokolwiek widzi znaczącą optymalizację, którą można zastosować?
FYI Połączenia k i f znajdują się poniżej. Są tak proste, że nie sądzę, że istnieje sposób na zoptymalizowanie tych innych. O ile moduł Data.Bits nie działa wolno?
f :: Int -> Word32 -> Word32 -> Word32 -> Word32
f t b c d
| t <= 19 = (b .&. c) .|. ((complement b) .&. d)
| t <= 39 = b `xor` c `xor` d
| t <= 59 = (b .&. c) .|. (b .&. d) .|. (c .&. d)
| otherwise = b `xor` c `xor` d
k :: Int -> Word32
k t
| t <= 19 = 0x5A827999
| t <= 39 = 0x6ED9EBA1
| t <= 59 = 0x8F1BBCDC
| otherwise = 0xCA62C1D6
Bez prób, zgaduję, że problemem jest utrzymanie danych blokowych na liście (zbyt duży ruch w punkcie/pamięci). Starałbym się mocno przenieść do unboxed wektory 'Word32' i ręcznie rozwinąć pętlę. Poza tym spróbuj z surową/rozpakowaną strukturą zawierającą 'a',' b', 'c',' d' i 'e'; wtedy masz tylko jedną zmienną, która musi zostać przekazana (i na pewno nałożysz na nią wzór, prawda?). –
Chciałbym również spróbować zastąpić wszystkie '(<=)', ale nie jestem pewien, czy to pomoże. –
Inna sprawa: Często dobrym pomysłem jest napisanie ciasnych funkcji arytmetycznych w C i wywołać to za pomocą FFI. Jeśli nie wprowadzisz żadnych efektów ubocznych, środowisko wykonawcze może użyć szybkiego wywołania do C, które zapewnia dobrą wydajność. – fuz