performance haskell ghc core

performance - GHC generando operaciones centrales redundantes



haskell core (1)

Tengo el siguiente programa para convertir 6bit ASCII a formato binario.

ascii2bin :: Char -> B.ByteString ascii2bin = B.reverse . fst . B.unfoldrN 6 decomp . to6BitASCII -- replace to6BitASCII with ord if you want to compile this where decomp n = case quotRem n 2 of (q,r) -> Just (chr r,q) bs2bin :: B.ByteString -> B.ByteString bs2bin = B.concatMap ascii2bin

esto produce el siguiente segmento de núcleo:

Rec { $wa $wa = / ww ww1 ww2 w -> case ww2 of wild { __DEFAULT -> let { wild2 wild2 = remInt# ww1 2 } in case leWord# (int2Word# wild2) (__word 1114111) of _ { False -> (lvl2 wild2) `cast` ...; True -> case writeWord8OffAddr# ww 0 (narrow8Word# (int2Word# (ord# (chr# wild2)))) w of s2 { __DEFAULT -> $wa (plusAddr# ww 1) (quotInt# ww1 2) (+# wild 1) s2 } }; 6 -> (# w, (lvl, lvl1, Just (I# ww1)) #) } end Rec }

note que ord . chr == id ord . chr == id , y aquí hay una operación redundante aquí: narrow8Word# (int2Word# (ord# (chr# wild2)))

¿Hay alguna razón por la que GHC esté convirtiendo innecesariamente de Int -> Char -> Int, o es un ejemplo de generación de código deficiente? ¿Se puede optimizar esto?

EDITAR: Esto está usando GHC 7.4.2, no he intentado compilar con ninguna otra versión. Desde entonces he encontrado que el problema sigue en GHC 7.6.2, pero las operaciones redundantes se eliminan en la rama HEAD actual en github.


¿Hay alguna razón por la que GHC esté convirtiendo innecesariamente de Int -> Char -> Int , o es un ejemplo de generación de código deficiente? ¿Se puede optimizar esto?

En realidad no (a ambos). El núcleo que obtienes de -ddump-simpl no es el final. Hay algunas optimizaciones y transformaciones que aún se realizan después de eso en el camino hacia el código de ensamblaje. Pero eliminar las conversiones redundantes aquí no es realmente una optimización.

Pueden ser, y son, removidos entre el núcleo y el ensamblaje. El punto es que estos primops, excepto el estrechamiento, no son operaciones, solo están presentes en el núcleo porque eso está escrito. Como no son operativos, no importa si hay una cadena redundante de ellos en el núcleo.

El ensamblaje que produce 7.6.1 a partir del código [es más legible que lo que produce 7.4.2, así que lo tomo] - con ord lugar de to6BitASCII - es

ASCII.$wa_info: _cXT: addq $64,%r12 cmpq 144(%r13),%r12 ja _cXX movq %rdi,%rcx cmpq $6,%rdi jne _cXZ movq $GHC.Types.I#_con_info,-56(%r12) movq %rsi,-48(%r12) movq $Data.Maybe.Just_con_info,-40(%r12) leaq -55(%r12),%rax movq %rax,-32(%r12) movq $(,,)_con_info,-24(%r12) movq $lvl1_rVq_closure+1,-16(%r12) movq $lvl_rVp_closure+1,-8(%r12) leaq -38(%r12),%rax movq %rax,0(%r12) leaq -23(%r12),%rbx jmp *0(%rbp) _cXX: movq $64,192(%r13) _cXV: movl $ASCII.$wa_closure,%ebx jmp *-8(%r13) _cXZ: movl $2,%ebx movq %rsi,%rax cqto idivq %rbx movq %rax,%rsi cmpq $1114111,%rdx jbe _cY2 movq %rdx,%r14 addq $-64,%r12 jmp GHC.Char.chr2_info _cY2: movb %dl,(%r14) incq %r14 leaq 1(%rcx),%rdi addq $-64,%r12 jmp ASCII.$wa_info .size ASCII.$wa_info, .-ASCII.$wa_info

La parte en la que aparece en el núcleo la cmpq $1114111, %rdx narrow8Word# (int2Word# (ord# (chr# wild2))) aparece después del cmpq $1114111, %rdx . Si el cociente no está fuera de rango, el código salta a _cY2 que ya no contiene tales conversiones. Se escribe un byte en la matriz, algunos punteros / contadores se incrementan, y eso es todo, salte de nuevo a la parte superior.

Creo que sería posible generar un mejor código a partir de lo que hace GHC actualmente, pero las conversiones no operativas redundantes ya desaparecen.