2024 年 Clojure 状态调查!中分享您的想法。

欢迎!请查阅 关于 页面,了解更多关于它是如何工作的信息。

0
编译器

已知条件

(let [c (char 110)]
        (case c \n (println "lol")))

如果我从 运行 decompile
我得到以下输出

public final class json$fn__15103 extends AFunction {
public static final Object const__2;
public static final Var const__3;
public static final Var const__4;

public static Object invokeStatic() {
    final char G__15104;
    final char c = G__15104 = RT.charCast(110L);
    switch (Util.hash(G__15104)) {
        case 110: {
            if (Util.equiv((Object)G__15104, json$fn__15103.const__2)) {
                return ((IFn)json$fn__15103.const__3.getRawRoot()).invoke("lol");
            }
            break;
        }
    }
    throw new IllegalArgumentException((String)((IFn)json$fn__15103.const__4.getRawRoot()).invoke("No matching clause: ", G__15104));
}

@Override
public Object invoke() {
    return invokeStatic();
}

static {
    const__2 = 'n';
    const__3 = RT.var("clojure.core", "println");
    const__4 = RT.var("clojure.core", "str");
} 
}

我的问题是(考虑到这个反汇编是正确的),为什么编译器生成 if 测试,更有趣的是,是否有方法去掉它(通过指导编译器的方式)?

也许我认为我不应该关心这个问题,因为 JIT 会帮我处理好它,但是仍然是。

此外,有人可能会争辩说,我想,既然我们知道我正在对 "char" 做的 case,那么没有必要在它上面做 "Util.hash"?

1 个答案

0

当在这个细节级别寻找优化,例如 JVM 字节码以及 JIT 如何处理它时,最好阅读实际的 JVM 字节码,而不是反汇编的 Java 源代码,在某些情况下,这可能会导致误导。更好的方法是找到基于性能结果的 JVM 字节码,其中具有流行版本 JVM JIT 编译器的代码在速度上更快。

我知道这可能是大量的烦琐工作,我绝对没有说 "Clojure 编译器生成的当前 JVM 字节码是已知的最佳方案",但听起来你可能比预期的更接近这个过程的开端。

似乎确实如此,如果Clojure编译器知道表达式的类型,它可以对case语句进行更复杂的类型特定优化——我怀疑它今天永远不会这样做。不过,我不知道JIT会怎么处理这种字节码,例如,也许它会看到Util.hash总是使用Character对象调用,创建一个检查参数是否为Character的内联版本Util.hash,如果是,就返回针对该类型的计算,否则就调用带有所有情况的全Util.hash。

by
我在写下我的问题后深入研究了这个,答案是远高于jvm/emitter级别的,在这里
https://github.com/clojure/clojure/blob/clojure-1.10.1/src/clj/clojure/core.clj#L6743

所以如果你使用全是int的测试来做case,你会得到最快的代码

```
(decompile (let [foo (int 3)]
             (case foo
               3 "bla")))
```
给出

```
    public static Object invokeStatic() {
        final int G__16058;
        final int foo = G__16058 = RT.intCast(3L);
        switch (G__16058) {
            case 3: {
               return "bla";
            }
            default: {
               throw new IllegalArgumentException((String)((IFn)json$fn__16057.const__3.getRawRoot()).invoke("No matching clause: ", G__16058));
            }
        }
    }
```
...