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 字节码的替代方案,这样基于性能结果,受欢迎的 JVM JIT 编译器能够生成更快的代码。

我知道这可能是一项繁琐的工作,我绝对没有说过“Clojure 编译器产出的当前 JVM 字节码是已知的最佳”,但听起来你可能距离这个过程的开始比结束更近。

它似乎是真的,如果Clojure编译器知道表达式的类型,它可以为case语句执行更复杂的类型特定优化——我怀疑它今天从未这样做。不过,我不知道JIT对这个字节码会做什么,例如,也许它能看到Util.hash总是被Character对象调用,并创建一个内联版本的Util.hash,它检查参数是否为Character,如果是,则返回针对该类型的计算,否则调用完整的Util.hash及其所有情况。

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

所以如果你使用所有测试都是针对整数的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));
            }
        }
    }
```
...