Integrated: 7903: FastAccessNumberMap is slow for sparse values

Richard Startin duke at openjdk.org
Wed Sep 28 14:56:25 UTC 2022


On Tue, 27 Sep 2022 16:50:00 GMT, Richard Startin <duke at openjdk.org> wrote:

> This reworks `FastAccessNumberMap` so that a small bitset can be stored in each page of values, which can be used to skip over null values during iteration. A simple benchmark shows a modest enhancement to calls to `get`, but significant improvements to iteration speed when the map contains sparse values (iteration now scales with the number of non-null values, not the number of pages).
> 
> 
> import org.openjdk.jmc.common.collection.FastAccessNumberMap;
> import org.openjdk.jmh.annotations.Benchmark;
> import org.openjdk.jmh.annotations.BenchmarkMode;
> import org.openjdk.jmh.annotations.Fork;
> import org.openjdk.jmh.annotations.Level;
> import org.openjdk.jmh.annotations.Measurement;
> import org.openjdk.jmh.annotations.Mode;
> import org.openjdk.jmh.annotations.OutputTimeUnit;
> import org.openjdk.jmh.annotations.Param;
> import org.openjdk.jmh.annotations.Scope;
> import org.openjdk.jmh.annotations.Setup;
> import org.openjdk.jmh.annotations.State;
> import org.openjdk.jmh.annotations.Warmup;
> import org.openjdk.jmh.infra.Blackhole;
> 
> import java.util.concurrent.TimeUnit;
> 
> @OutputTimeUnit(TimeUnit.MICROSECONDS)
> @Warmup(iterations = 5, time = 1)
> @Measurement(iterations = 5, time = 1)
> @Fork(1)
> @BenchmarkMode(Mode.AverageTime)
> @State(Scope.Benchmark)
> public class FastAccessNumberMapBenchmark {
>     private FastAccessNumberMap<String> map;
>     @Param({"1", "2", "3", "4", "64"})
>     int period;
>     private int size;
>     private long[] values;
> 
>     @Setup(Level.Trial)
>     public void setup() {
>         map = new FastAccessNumberMap<>();
>         values = new long[5000];
>         for (int i = 0; i < 5000; i += period) {
>             map.put(i, String.valueOf(i));
>             values[size++] = i;
>         }
>     }
> 
>     @Benchmark
>     public void get(Blackhole bh) {
>         for (int i = 0; i < size; i++) {
>             bh.consume(map.get(values[i]));
>         }
>     }
> 
>     @Benchmark
>     public void iterate(Blackhole bh) {
>         map.forEach(bh::consume);
>     }
> }
> ``` 
> 
> Tested with 17.0.4.1 2022-08-12 on a Mac M1 
> After:
> 
> Benchmark                             (period)  Mode  Cnt   Score   Error  Units
> FastAccessNumberMapBenchmark.get             1  avgt    5  17.864 ± 0.044  us/op
> FastAccessNumberMapBenchmark.get             2  avgt    5   8.921 ± 0.124  us/op
> FastAccessNumberMapBenchmark.get             3  avgt    5   5.916 ± 0.052  us/op
> FastAccessNumberMapBenchmark.get             4  avgt    5   4.351 ± 0.011  us/op
> FastAccessNumberMapBenchmark.get            64  avgt    5   0.273 ± 0.001  us/op
> FastAccessNumberMapBenchmark.iterate         1  avgt    5  16.241 ± 0.581  us/op
> FastAccessNumberMapBenchmark.iterate         2  avgt    5   8.768 ± 0.205  us/op
> FastAccessNumberMapBenchmark.iterate         3  avgt    5   5.463 ± 0.051  us/op
> FastAccessNumberMapBenchmark.iterate         4  avgt    5   4.095 ± 0.103  us/op
> FastAccessNumberMapBenchmark.iterate        64  avgt    5   0.441 ± 0.002  us/op
> 
> 
> Before:
> 
> Benchmark                             (period)  Mode  Cnt   Score    Error  Units
> FastAccessNumberMapBenchmark.get             1  avgt    5  18.937 ±  0.212  us/op
> FastAccessNumberMapBenchmark.get             2  avgt    5   9.419 ±  0.042  us/op
> FastAccessNumberMapBenchmark.get             3  avgt    5   6.151 ±  0.108  us/op
> FastAccessNumberMapBenchmark.get             4  avgt    5   4.618 ±  0.031  us/op
> FastAccessNumberMapBenchmark.get            64  avgt    5   0.286 ±  0.001  us/op
> FastAccessNumberMapBenchmark.iterate         1  avgt    5  19.142 ±  0.611  us/op
> FastAccessNumberMapBenchmark.iterate         2  avgt    5  18.930 ±  4.756  us/op
> FastAccessNumberMapBenchmark.iterate         3  avgt    5  11.579 ±  0.082  us/op
> FastAccessNumberMapBenchmark.iterate         4  avgt    5  11.650 ±  0.500  us/op
> FastAccessNumberMapBenchmark.iterate        64  avgt    5  11.781 ±  0.541  us/op
> 
> 
> We use the jmc common library in a backend process at Datadog and see bigger improvements than the microbenchmark above would suggest.

This pull request has now been integrated.

Changeset: 7cd2f3e7
Author:    Richard Startin <richard.startin at datadoghq.com>
Committer: Marcus Hirt <hirt at openjdk.org>
URL:       https://git.openjdk.org/jmc/commit/7cd2f3e730480c833930e36951f8658b4addead4
Stats:     83 lines in 1 file changed: 65 ins; 3 del; 15 mod

7903: FastAccessNumberMap is slow for sparse values

Reviewed-by: hdafgard

-------------

PR: https://git.openjdk.org/jmc/pull/431


More information about the jmc-dev mailing list