• Sergey Senozhatsky's avatar
    zsmalloc: rework compaction algorithm · 5a845e9f
    Sergey Senozhatsky authored
    The zsmalloc compaction algorithm has the potential to waste some CPU
    cycles, particularly when compacting pages within the same fullness group.
    This is due to the way it selects the head page of the fullness list for
    source and destination pages, and how it reinserts those pages during each
    iteration.  The algorithm may first use a page as a migration destination
    and then as a migration source, leading to an unnecessary back-and-forth
    movement of objects.
    
    Consider the following fullness list:
    
    PageA PageB PageC PageD PageE
    
    During the first iteration, the compaction algorithm will select PageA as
    the source and PageB as the destination.  All of PageA's objects will be
    moved to PageB, and then PageA will be released while PageB is reinserted
    into the fullness list.
    
    PageB PageC PageD PageE
    
    During the next iteration, the compaction algorithm will again select the
    head of the list as the source and destination, meaning that PageB will
    now serve as the source and PageC as the destination.  This will result in
    the objects being moved away from PageB, the same objects that were just
    moved to PageB in the previous iteration.
    
    To prevent this avalanche effect, the compaction algorithm should not
    reinsert the destination page between iterations.  By doing so, the most
    optimal page will continue to be used and its usage ratio will increase,
    reducing internal fragmentation.  The destination page should only be
    reinserted into the fullness list if:
    - It becomes full
    - No source page is available.
    
    TEST
    ====
    
    It's very challenging to reliably test this series.  I ended up developing
    my own synthetic test that has 100% reproducibility.  The test generates
    significan fragmentation (for each size class) and then performs
    compaction for each class individually and tracks the number of memcpy()
    in zs_object_copy(), so that we can compare the amount work compaction
    does on per-class basis.
    
    Total amount of work (zram mm_stat objs_moved)
    ----------------------------------------------
    
    Old fullness grouping, old compaction algorithm:
    323977 memcpy() in zs_object_copy().
    
    Old fullness grouping, new compaction algorithm:
    262944 memcpy() in zs_object_copy().
    
    New fullness grouping, new compaction algorithm:
    213978 memcpy() in zs_object_copy().
    
    Per-class compaction memcpy() comparison (T-test)
    -------------------------------------------------
    
    x Old fullness grouping, old compaction algorithm
    + Old fullness grouping, new compaction algorithm
    
        N           Min           Max        Median           Avg        Stddev
    x 140           349          3513          2461     2314.1214     806.03271
    + 140           289          2778          2006     1878.1714     641.02073
    Difference at 95.0% confidence
            -435.95 +/- 170.595
            -18.8387% +/- 7.37193%
            (Student's t, pooled s = 728.216)
    
    x Old fullness grouping, old compaction algorithm
    + New fullness grouping, new compaction algorithm
    
        N           Min           Max        Median           Avg        Stddev
    x 140           349          3513          2461     2314.1214     806.03271
    + 140           226          2279          1644     1528.4143     524.85268
    Difference at 95.0% confidence
            -785.707 +/- 159.331
            -33.9527% +/- 6.88516%
            (Student's t, pooled s = 680.132)
    
    Link: https://lkml.kernel.org/r/20230304034835.2082479-4-senozhatsky@chromium.orgSigned-off-by: default avatarSergey Senozhatsky <senozhatsky@chromium.org>
    Acked-by: default avatarMinchan Kim <minchan@kernel.org>
    Cc: Yosry Ahmed <yosryahmed@google.com>
    Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
    5a845e9f
zsmalloc.c 65.9 KB