【发布时间】:2014-10-14 06:02:33
【问题描述】:
user=> (def r (range 1))
user=> (for [a r, b r, c r, d r, e r, f r, g r, h r :when (and (= 0 a) (not= 1 b))]
(list a b c d e f g h))
((0 0 0 0 0 0 0 0))
user=> (doseq [a r, b r, c r, d r, e r, f r, g r, h r :when (and (= 0 a) (not= 1 b))]
(println (list a b c d e f g h)))
CompilerException java.lang.RuntimeException: Method code too large!, compiling:(/tmp/form-init8346140986526777871.clj:1:1)
这似乎来自clojure.asm.MethodWriter。我用 Clojure 搜索这个错误几乎没有找到任何结果。
那么……到底发生了什么?这个兔子洞有多深?这行 Clojure 代码真的产生了 >65KB 的方法吗(值来自 MethodWriter 的源码)?
如果this answer 正在解决我遇到的问题,那么 (a) 为什么分块意味着它呈指数增长而不是线性增长? (b) 作为一名程序员,对我有什么影响?例如,这种行为是否众所周知且有意?对于超过 3 或 4 个绑定的任何情况,我是否应该避免使用 doseq?这与使用for 和doall 相比如何?
也许相关:
【问题讨论】:
-
看起来你的doseq中的每一个额外绑定都会使生成的代码的大小加倍(我用no.disassemble尝试过)。不知道为什么。
-
+1 哇!不错的收获!我真的很想知道更多。
-
我天真地期望在
for和doseq之间,for会产生更大的方法。如果doseq存在这种低效率,那么我很想知道在什么复杂度阈值下使用(def ignore (doall (for [stuff] (side-effect)))而不是更自然的(doseq [stuff] (side-effect))可以获得更好的性能。 -
@DiegoBasch,我刚刚检查了 no.disassemble。这很酷。感谢您的参考。 :)
-
您链接的第一个问题中接受的答案解释了为什么会发生这种情况。 doseq 的宏扩展有一个分支来处理每个绑定的 seq 表达式的分块 seq,这意味着每个新绑定的扩展代码大小加倍。
标签: java clojure java-bytecode-asm