【问题标题】:Guava Multimaps.filterKeys poor performance vs NavigableMap.subMap()Guava Multimaps.filterKeys 与 NavigableMap.subMap() 的性能差
【发布时间】:2018-12-08 13:18:07
【问题描述】:

我有一个 Guava TreeMultimap,它将 Date 键映射到某个值。

我希望能够过滤此map 以查找密钥在特定日期范围内的位置。

由于 map 已排序,应该可以在不评估所有键的情况下非常快速地执行此操作,但是当我第一次使用 Multimaps.filterKeys 编写此代码时,它比预期的要慢得多,这表明它正在评估每个键.当我重写它以使用 NavigableMap.subMap() 时,性能非常好,达到了我的预期。

Multimaps.filterKeys 语法要好得多,这是我想使用的,特别是因为我已经在使用 Multimap

请在下面查看我正在做的最小化示例:

import java.text.MessageFormat;
import java.util.Date;
import java.util.concurrent.TimeUnit;
import com.google.common.collect.Multimaps;
import com.google.common.collect.Range;
import com.google.common.collect.TreeMultimap;

public class MapPerformanceTest
{
    public static void main(final String[] args)
    {
        System.out.println("Populating Map...");

        final TreeMultimap<Date, Integer> map = TreeMultimap.create();

        for (int i = 0; i < 20000000; i++)
        {
            map.put(new Date(i), i);
        }

        final Date[] range = {new Date(10), new Date(20)};

        System.out.println("Map Populated");
        System.out.println();

        long tempTime = -System.nanoTime();

        System.out.println(MessageFormat.format("Multimaps.filterKeys() attempt #1 returned {0} keys in {1} milliseconds",
                Multimaps.filterKeys(map, Range.closed(range[0], range[1])).size(),
                TimeUnit.MILLISECONDS.convert(tempTime + System.nanoTime(), TimeUnit.NANOSECONDS)));

        tempTime = -System.nanoTime();

        System.out.println(MessageFormat.format("NavigableMap.subMap() attempt #1 returned {0} keys in {1} milliseconds",
                map.asMap().subMap(range[0], true, range[1], true).size(), TimeUnit.MILLISECONDS.convert(tempTime + System.nanoTime(), TimeUnit.NANOSECONDS)));

        tempTime = -System.nanoTime();

        System.out.println(MessageFormat.format("NavigableMap.subMap() attempt #2 returned {0} keys in {1} milliseconds",
                map.asMap().subMap(range[0], true, range[1], true).size(), TimeUnit.MILLISECONDS.convert(tempTime + System.nanoTime(), TimeUnit.NANOSECONDS)));

        tempTime = -System.nanoTime();

        System.out.println(MessageFormat.format("Multimaps.filterKeys() attempt #2 returned {0} keys in {1} milliseconds",
                Multimaps.filterKeys(map, Range.closed(range[0], range[1])).size(),
                TimeUnit.MILLISECONDS.convert(tempTime + System.nanoTime(), TimeUnit.NANOSECONDS)));
    }
}

输出是:

Multimaps.filterKeys() attempt #1 returned 11 keys in 1,418 milliseconds
NavigableMap.subMap() attempt #1 returned 11 keys in 1 milliseconds
NavigableMap.subMap() attempt #2 returned 11 keys in 0 milliseconds
Multimaps.filterKeys() attempt #2 returned 11 keys in 946 milliseconds

【问题讨论】:

    标签: java performance guava multimap sortedmap


    【解决方案1】:

    您认为过滤比迭代子图慢的观察是正确的。正如您所怀疑的那样,解释是过滤是否涉及评估每个键。

    这是过滤方法所固有的。 filterKeys 方法无法查看提供的过滤器内部以确定它的作用。因此有必要将过滤器应用于所有键。

    现在,如果编译器对方法和过滤器在做什么有深入的了解,理论上它就有可能将过滤转换为更有效的东西。不幸的是,它没有,因此有必要使用更麻烦的子图方法......如果您希望在具有许多键的地图上操作时获得良好的性能。

    【讨论】:

    • 在查看 Guava 的源代码时,很多方法对每种类型的 Collection 或 Map 都有内部特化。我很惊讶这个案子没有得到处理。
    • 问题是filterKeyword 中的特殊情况代码需要知道所提供的过滤器的作用。这是不可能的。
    猜你喜欢
    • 2012-09-16
    • 1970-01-01
    • 1970-01-01
    • 2015-08-03
    • 2011-12-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-02-03
    相关资源
    最近更新 更多