【问题标题】:GHC Generating Redundant Core OperationsGHC 产生冗余核心操作
【发布时间】:2013-02-12 07:18:28
【问题描述】:

我有以下程序用于将 6 位 ASCII 转换为二进制格式。

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

这会产生以下核心部分:

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 }

注意ord . chr == id,所以这里有一个冗余操作:narrow8Word# (int2Word# (ord# (chr# wild2)))

GHC 从 Int -> Char -> Int 进行不必要的转换是否有原因,或者这是代码生成不佳的示例?这个可以优化吗?

编辑:这是使用 GHC 7.4.2,我没有尝试使用任何其他版本进行编译。从那以后我发现问题仍然存在于 GHC 7.6.2 中,但是在 github 上的当前 HEAD 分支中删除了冗余操作。

【问题讨论】:

    标签: performance haskell ghc core


    【解决方案1】:

    GHC 从Int -> Char -> Int 进行不必要的转换是否有原因,或者这是代码生成不佳的一个例子?这个可以优化吗?

    不是真的(对两者而言)。你从-ddump-simpl 得到的核心不是终点。之后在汇编代码的路上还有一些优化和转换。但在这里删除冗余转换实际上并不是优化。

    它们可以在核心和组件之间被移除。关键是这些primops——除了变窄之外——都是无操作的,它们只存在于核心中,因为它是输入的。由于它们是无操作的,因此核心中是否存在它们的冗余链并不重要。

    7.6.1 从代码中生成的程序集 [它比 7.4.2 生成的更具可读性,所以我接受它] - 使用 ord 而不是 to6BitASCII - 是

    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
    

    narrow8Word# (int2Word# (ord# (chr# wild2))) 出现在核心中的部分在cmpq $1114111, %rdx 之后。如果商没有超出范围,则代码跳转到 _cY2,其中不再包含此类转换。一个字节写入数组,一些指针/计数器递增,就这样,跳回顶部。

    我认为它可以生成比 GHC 目前更好的代码,但是多余的无操作转换已经消失了。

    【讨论】:

    • 是的,没错。其中大多数是价值级别的 noops,仅用于更改类型。由于键入了 Core,因此这是必需的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-09-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-04-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多