当前位置: 首页 > 知识库问答 >
问题:

为什么JIT在清除绑定支票方面做得这么差?

通鸿风
2023-03-14

我正在测试热点JIT数组绑定检查消除能力。下面是相同heapsort实现的两个版本,一个使用普通数组索引,另一个sun.misc.unsafeAPI,没有绑定检查:

public class HeapSort {
    // copied from http://en.wikibooks.org/wiki/Algorithm_Implementation/Sorting/Heapsort#C
    static int heapSortSimple(int[] arr) {
        int t;
        int n = arr.length, parent = n / 2, index, childI;
        while (true) {
            if (parent > 0) {
                t = arr[--parent]; // 1, i. e. first indexing
            } else {
                if (--n == 0) break;
                t = arr[n]; // 2
                arr[n] = arr[0]; // 3, 4
            }
            index = parent;
            childI = (index << 1) + 1;
            while (childI < n) {
                int childV = arr[childI]; // 5
                int right;
                if (childI + 1 < n && (right = arr[childI + 1]) > childV) { // 6
                    childI++;
                    childV = right;
                }
                if (childV > t) {
                    arr[index] = childV; // 7
                    index = childI;
                    childI = (index << 1) + 1;
                } else {
                    break;
                }
            }
            arr[index] = t; // 8
        }
        return arr[arr.length - 1];
    }

    static int heapSortUnsafe(int[] arr) {
        int t;
        long n = arr.length * INT_SCALE, parent = (arr.length / 2) * INT_SCALE, index, childI;
        while (true) {
            if (parent > 0) {
                t = U.getInt(arr, INT_BASE + (parent -= INT_SCALE));
            } else {
                if ((n -= INT_SCALE) == 0) break;
                t = U.getInt(arr, INT_BASE + n);
                U.putInt(arr, INT_BASE + n, U.getInt(arr, INT_BASE));
            }
            index = parent;
            childI = (index << 1) + INT_SCALE;
            while (childI < n) {
                int childV = U.getInt(arr, INT_BASE + childI);
                int right;
                if (childI + INT_SCALE < n &&
                        (right = U.getInt(arr, INT_BASE + (childI + INT_SCALE))) > childV) {
                    childI += INT_SCALE;
                    childV = right;
                }
                if (childV > t) {
                    U.putInt(arr, INT_BASE + index, childV);
                    index = childI;
                    childI = (index << 1) + INT_SCALE;
                } else {
                    break;
                }
            }
            U.putInt(arr, INT_BASE + index, t);
        }
        return arr[arr.length - 1];
    }

    @OutputTimeUnit(TimeUnit.MICROSECONDS)
    @BenchmarkMode(Mode.AverageTime)
    @Warmup(iterations = 5, time = 1)
    @Measurement(iterations = 10, time = 1)
    @State(Scope.Thread)
    @Threads(1)
    @Fork(1)
    public static class Benchmarks {
        static final int N = 1024;
        int[] a = new int[N];

        @Setup(Level.Invocation)
        public void fill() {
            Random r = ThreadLocalRandom.current();
            for (int i = 0; i < N; i++) {
                a[i] = r.nextInt();
            }
        }

        @GenerateMicroBenchmark
        public static int simple(Benchmarks st) {
            int[] arr = st.a;
            return heapSortSimple(arr);
        }

        @GenerateMicroBenchmark
        public static int unsafe(Benchmarks st) {
            int[] arr = st.a;
            return heapSortUnsafe(arr);
        }
    }

    public static void main(String[] args) {
        Benchmarks bs = new Benchmarks();

        // verify simple sort
        bs.fill();
        int[] a1 = bs.a;
        int[] a2 = a1.clone();
        Arrays.sort(a2);
        heapSortSimple(a1);
        if (!Arrays.equals(a2, a1))
            throw new AssertionError();

        // let JIT to generate optimized assembly
        for (int i = 0; i < 10000; i++) {
            bs.fill();
            heapSortSimple(bs.a);
        }

        // verify unsafe sort
        bs.fill();
        a1 = bs.a;
        a2 = a1.clone();
        Arrays.sort(a2);
        heapSortUnsafe(a1);
        if (!Arrays.equals(a2, a1))
            throw new AssertionError();

        for (int i = 0; i < 10000; i++) {
            bs.fill();
            heapSortUnsafe(bs.a);
        }
    }

    static final Unsafe U;
    static final long INT_BASE;
    static final long INT_SCALE = 4;

    static {
        try {
            Field f = Unsafe.class.getDeclaredField("theUnsafe");
            f.setAccessible(true);
            U = (Unsafe) f.get(null);
        } catch (Exception e) {
            throw new IllegalStateException(e);
        }
        INT_BASE = U.arrayBaseOffset(int[].class);
    }
}

在Intel SB和AMD K10 CPU上,不安全版本的速度始终保持在13%以上。

我查看了生成的程序集:

  • 消除所有索引操作的下界检查(1-8)
  • 仅对操作5消除上限检查,合并2和3的检查
  • 是,对于每次迭代的操作4(arr[0]),检查arr.length!=0

我认为这绝对是JIT的工作优化绑定检查至少操作1,2和3,其中索引是从数组长度以下的某个值稳步递减到零。

问题就在标题中:为什么HotSpot JIT在这种情况下的绑定检查消除工作做得这么差?

共有1个答案

钱澄邈
2023-03-14

我不认为所有的乳胶都是受限制的。

传递零长度数组将导致IOOB。尝试在循环之前使用if(n==0)return

 类似资料:
  • 自从 OpenResty 1.5.8.1 版本之后,默认捆绑的 Lua 解释器就被替换成了 LuaJIT,而不再是标准 Lua。单从名字上,我们就可以直接看到这个新的解释器多了一个 JIT,接下来我们就一起来聊聊 JIT。 先看一下 LuaJIT 官方的解释:LuaJIT is a Just-In-Time Compilerfor the Lua programming language。 Lua

  • 我有一个扩展JTextField的类,我想让CTRL-Shift-O做一些事情。我一直在听 在e.isControlDown()和e.isShiftDown()的帮助下,这种方法运行良好。但我注意到字段中的文本也在从左边向右边移动。显然,这是JTextField的默认行为。所以我在So上找到了这个线程,它似乎很有帮助: 如何禁用JTextField中的默认textfield快捷方式 尽管Syste

  • 在这个React(使用JSX)代码中做什么,它叫什么?

  • 测试代码为: 测试代码为: 你知道怎么了吗?

  • 我遇到JSON解析错误。我的代码如下: 我从我的检查中得到以下错误: 由于:com,无法分析JSON。谷歌。格森。JsonSyntaxException:java。lang.IllegalStateException:应为BEGIN\u对象,但在第1行第2列为BEGIN\u数组 对于我试图读取的JSON,如果成功,我的应该返回5。 我做错了什么?

  • 关于静态和动态之间的区别,我仍然有点困惑。据我所知,动态使用对象,而静态使用类型,动态在运行时解析,而静态在编译时解析。所以this.lastName.compare(s1.last名称)不应该使用动态绑定吗? 钥匙compareTo(list[position-1])使用动态绑定 (this . last name . compare to(S1 . last name))为什么使用静态绑定?