请分享您的观点,参加2024年Clojure调查!

欢迎!请查看关于页面,了解更多有关本站的信息。

0
编译器

给定

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

如果我从https://clojars.org/com.clojure-goes-fast/clj-java-decompiler 运行 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会为我处理它,但无论如何。

另外,也许可以说,既然我们知道我在 case 上操作的是 char 类型,那么为什么还需要对它执行 Util.hash 操作呢?

1 回答

0

在寻找这样详细级别的优化,即JVM字节码和JIT如何处理它时,最好阅读实际的JVM字节码,而不是反编译的Java源代码,有时它可能是误导性的。更好的做法是找到由流行的近期版本JVM JIT编译器根据性能结果生成的更快的备用JVM字节码。

我知道这可能是一份繁重的工 作,我绝对不是在说 "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

所以,如果你用 all the tests are against ints 的 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));
            }
        }
    }
```
...