【问题标题】:Why sentinel search slower than linear?为什么哨兵搜索比线性搜索慢?
【发布时间】:2019-04-27 16:31:43
【问题描述】:

我决定减少在数组中查找元素所需的比较次数。在这里,我们将列表的最后一个元素替换为搜索元素本身,并运行一个 while 循环以查看列表中是否存在搜索元素的任何副本,并在找到搜索元素后立即退出循环。请参阅代码 sn-p 进行说明。

import java.util.Random;

public class Search {

    public static void main(String[] args) {
        int n = 10000000;
        int key = 10000;
        int[] arr = generateRandomSize(n);
        long start = System.nanoTime();
        int find = sentinels(arr, key);
        long end = System.nanoTime();
        System.out.println(find);
        System.out.println(end - start);

        arr = generateRandomSize(n);
        start = System.nanoTime();
        find = linear(arr, key);
        end = System.nanoTime();
        System.out.println(find);
        System.out.println(end - start);
    }

    public static int[] generateRandomSize(int n) {
        int[] arr = new int[n];
        Random rand = new Random();

        for (int i = 0; i < n; ++i) {
            arr[i] = rand.nextInt(5000);
        }

        return arr;
    }

    public static int linear(int[] a, int key) {
        for(int i = 0; i < a.length; ++i) {
            if (a[i] == key) {
                return i;
            }
        }

        return -1;
    }

    public static int sentinels(int[] a, int key) {
        int n = a.length;
        int last = a[n-1];
        a[n-1] = key;

        int i = 0;
        while (a[i] != key) {
            ++i;
        }
        a[n-1] = last;
        if ((i < n - 1) || a[n-1] == key ) {
            return i;
        }

        return -1;
    }

}

因此,使用哨兵搜索我们不会像 i

【问题讨论】:

  • i &lt; arr.length 不太可能花费太多时间。 Potentially related?
  • 您的基准测试中存在缺陷,这意味着您获得的任何结果都必须被视为可疑。请阅读:"How to write a correct micro-benchmark in Java"
  • 这两种方法也需要O(n)才能完成工作,你在我的电脑里的例子sentinels更快
  • @rustyx 不,请仔细检查哨兵实施
  • @rustyx 也是,我正在使用 0、5000 之间的值初始化数组并试图找到 10000

标签: java algorithm search optimization


【解决方案1】:

您必须查看字节码,甚至更深入地了解热点正在从中产生什么。但我很确定这个说法是不正确的:

使用哨兵搜索,我们不会像 i 那样进行 10000000 次比较

为什么?因为当您访问a[i] 时,必须检查i 的边界。另一方面,在线性情况下,优化器可以推断它可以省略边界检查,因为它“知道”i&gt;=0(因为循环结构)以及i&lt;arr.length,因为它已经在循环中进行了测试条件。

所以哨兵方法只是增加了开销。

这让我想到了我大约 20 年前所做的一种智能 C++ 优化(称为“模板元编程”和“表达式模板”),它可以缩短执行时间(以更高的编译时间为代价),之后发布了下一个编译器版本,我发现新版本能够优化原始源代码以生成完全相同的程序集 - 简而言之,我应该以不同的方式使用我的时间并保持更易读(=更易于维护)的版本的代码。

【讨论】:

  • 您需要查看本机代码。字节码对性能问题没有多大帮助。幸运的是,很容易让 JVM 转储 JIT 编译器生成的本机代码
  • 你的最后一段很有启发性。当回报太小而不值得时,太多人浪费时间优化事物。
猜你喜欢
  • 1970-01-01
  • 2020-09-17
  • 2016-01-24
  • 2015-09-23
  • 2017-07-21
  • 2013-04-11
  • 2017-12-16
  • 1970-01-01
相关资源
最近更新 更多