• Kirill Smelkov's avatar
    [media] vivi: Optimize gen_text() · e3a8b4d2
    Kirill Smelkov authored
    I've noticed that vivi takes a lot of CPU to produce its frames.
    For example for 8 devices and 8 simple programs running, where each
    captures YUY2 640x480 and displays it to X via SDL, profile timing is as
    follows:
        # cmdline : /home/kirr/local/perf/bin/perf record -g -a sleep 20
        # Samples: 82K of event 'cycles'
        # Event count (approx.): 31551930117
        #
        # Overhead          Command         Shared Object                                                           Symbol
        # ........  ...............  ....................
        #
            49.48%           vivi-*  [vivi]                [k] gen_twopix
            10.79%           vivi-*  [kernel.kallsyms]     [k] memcpy
            10.02%             rawv  libc-2.13.so          [.] __memcpy_ssse3
             8.35%           vivi-*  [vivi]                [k] gen_text.constprop.6
             5.06%             Xorg  [unknown]             [.] 0xa73015f8
             2.32%             rawv  [vivi]                [k] gen_twopix
             1.22%             rawv  [vivi]                [k] precalculate_line
             1.20%           vivi-*  [vivi]                [k] vivi_fillbuff
        (rawv is display program, vivi-* is a combination of vivi-000 through vivi-007)
    so a lot of time is spent in gen_twopix() which as the follwing
    call-graph profile shows ...
        49.48%           vivi-*  [vivi]                [k] gen_twopix
                         |
                         --- gen_twopix
                            |
                            |--96.30%-- gen_text.constprop.6
                            |          vivi_fillbuff
                            |          vivi_thread
                            |          kthread
                            |          ret_from_kernel_thread
                            |
                             --3.70%-- vivi_fillbuff
                                       vivi_thread
                                       kthread
                                       ret_from_kernel_thread
    ... is called mostly from gen_text().
    If we'll look at gen_text(), in the inner loop, we'll see
        if (chr & (1 << (7 - i)))
                gen_twopix(dev, pos + j * dev->pixelsize, WHITE, (x+y) & 1);
        else
                gen_twopix(dev, pos + j * dev->pixelsize, TEXT_BLACK, (x+y) & 1);
    which calls gen_twopix() for every character pixel, and that is very
    expensive, because gen_twopix() branches several times.
    Now, let's note, that we operate on only two colors - WHITE and
    TEXT_BLACK, and that pixel for that colors could be precomputed and
    gen_twopix() moved out of the inner loop. Also note, that for black
    and white colors even/odd does not make a difference for all supported
    pixel formats, so we could stop doing that `odd` gen_twopix() parameter
    game.
    So the first thing we are doing here is
        1) moving gen_twopix() calls out of gen_text() into vivi_fillbuff(),
           to pregenerate black and white colors, just before printing
           starts.
    what we have next is that gen_text's font rendering loop, even with
    gen_twopix() calls moved out, was inefficient and branchy, so let's
        2) rewrite gen_text() loop so it uses less variables + unroll char
           horizontal-rendering loop + instantiate 3 code paths for pixelsizes 2,3
           and 4 so that in all inner loops we don't have to branch or make
           indirections (*).
    Done all above reworks, for gen_text() we get nice, non-branchy
    streamlined code (showing loop for pixelsize=2):
               ?       cmp    $0x2,%eax
               ?     ? jne    26
               ?       mov    -0x18(%ebp),%eax
               ?       mov    -0x20(%ebp),%edi
               ?       imul   -0x20(%ebp),%eax
               ?       movzwl 0x3ffc(%ebx),%esi
          0,08 ?       movzwl 0x4000(%ebx),%ecx
          0,04 ?       add    %edi,%edi
               ?       mov    0x0,%ebx
          0,51 ?       mov    %edi,-0x1c(%ebp)
               ?       mov    %ebx,-0x14(%ebp)
               ?       movl   $0x0,-0x10(%ebp)
               ?       lea    0x20(%edx,%eax,2),%eax
               ?       mov    %eax,-0x18(%ebp)
               ?       xchg   %ax,%ax
          0,04 ? a0:   mov    0x8(%ebp),%ebx
               ?       mov    -0x18(%ebp),%eax
          0,04 ?       movzbl (%ebx),%edx
          0,16 ?       test   %dl,%dl
          0,04 ?     ? je     128
          0,08 ?       lea    0x0(%esi),%esi
          1,61 ? b0:???shl    $0x4,%edx
          1,02 ?    ?  mov    -0x14(%ebp),%edi
          2,04 ?    ?  add    -0x10(%ebp),%edx
          2,24 ?    ?  lea    0x1(%ebx),%ebx
          0,27 ?    ?  movzbl (%edi,%edx,1),%edx
          9,92 ?    ?  mov    %esi,%edi
          0,39 ?    ?  test   %dl,%dl
          2,04 ?    ?  cmovns %ecx,%edi
          4,63 ?    ?  test   $0x40,%dl
          0,55 ?    ?  mov    %di,(%eax)
          3,76 ?    ?  mov    %esi,%edi
          0,71 ?    ?  cmove  %ecx,%edi
          3,41 ?    ?  test   $0x20,%dl
          0,75 ?    ?  mov    %di,0x2(%eax)
          2,43 ?    ?  mov    %esi,%edi
          0,59 ?    ?  cmove  %ecx,%edi
          4,59 ?    ?  test   $0x10,%dl
          0,67 ?    ?  mov    %di,0x4(%eax)
          2,55 ?    ?  mov    %esi,%edi
          0,78 ?    ?  cmove  %ecx,%edi
          4,31 ?    ?  test   $0x8,%dl
          0,67 ?    ?  mov    %di,0x6(%eax)
          5,76 ?    ?  mov    %esi,%edi
          1,80 ?    ?  cmove  %ecx,%edi
          4,20 ?    ?  test   $0x4,%dl
          0,86 ?    ?  mov    %di,0x8(%eax)
          2,98 ?    ?  mov    %esi,%edi
          1,37 ?    ?  cmove  %ecx,%edi
          4,67 ?    ?  test   $0x2,%dl
          0,20 ?    ?  mov    %di,0xa(%eax)
          2,78 ?    ?  mov    %esi,%edi
          0,75 ?    ?  cmove  %ecx,%edi
          3,92 ?    ?  and    $0x1,%edx
          0,75 ?    ?  mov    %esi,%edx
          2,59 ?    ?  mov    %di,0xc(%eax)
          0,59 ?    ?  cmove  %ecx,%edx
          3,10 ?    ?  mov    %dx,0xe(%eax)
          2,39 ?    ?  add    $0x10,%eax
          0,51 ?    ?  movzbl (%ebx),%edx
          2,86 ?    ?  test   %dl,%dl
          2,31 ?    ???jne    b0
          0,04 ?128:   addl   $0x1,-0x10(%ebp)
          4,00 ?       mov    -0x1c(%ebp),%eax
          0,04 ?       add    %eax,-0x18(%ebp)
          0,08 ?       cmpl   $0x10,-0x10(%ebp)
               ?     ? jne    a0
    which almost goes away from the profile:
        # cmdline : /home/kirr/local/perf/bin/perf record -g -a sleep 20
        # Samples: 49K of event 'cycles'
        # Event count (approx.): 16799780016
        #
        # Overhead          Command         Shared Object                                                           Symbol
        # ........  ...............  ....................
        #
            27.51%             rawv  libc-2.13.so          [.] __memcpy_ssse3
            23.77%           vivi-*  [kernel.kallsyms]     [k] memcpy
             9.96%             Xorg  [unknown]             [.] 0xa76f5e12
             4.94%           vivi-*  [vivi]                [k] gen_text.constprop.6
             4.44%             rawv  [vivi]                [k] gen_twopix
             3.17%           vivi-*  [vivi]                [k] vivi_fillbuff
             2.45%             rawv  [vivi]                [k] precalculate_line
             1.20%          swapper  [kernel.kallsyms]     [k] read_hpet
    i.e. gen_twopix() overhead dropped from 49% to 4% and gen_text() loops
    from ~8% to ~4%, and overal cycles count dropped from 31551930117 to
    16799780016 which is ~1.9x whole workload speedup.
    (*) for RGB24 rendering I've introduced x24, which could be thought as
        synthetic u24 for simplifying the code. That's done because for
        memcpy used for conditional assignment, gcc generates suboptimal code
        with more indirections.
        Fortunately, in C struct assignment is builtin and that's all we
        need from pixeltype for font rendering.
    Signed-off-by: default avatarKirill Smelkov <kirr@mns.spb.ru>
    Acked-by: default avatarHans Verkuil <hans.verkuil@cisco.com>
    Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@redhat.com>
    e3a8b4d2
vivi.c 34.2 KB