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

欢迎!请参阅关于页面以了解有关此工作方式的一些更多信息。

0
编译器

目前,启用直接链接(DL)的方法大多是全有或全无,带有精细的拒绝选择(`^:redef`)。据我所知,这使得在生产环境中使用DL有些笨拙,您仍然想要保留调试和调查应用程序的能力。事先知道需要重新编译哪些函数是繁琐且精神负担很大的。事实上,我最终由于这个原因完全没有启用DL。

我想知道是否有更灵活的方式来调整DL是否会使其更受欢迎。以下是我对此的一些想法。

当前,是否要执行DL的决定是在调用位置根据编译时该调用位置的`*compiler-options*`的值做出的。我设想的一种替代方案是,当决定DL是在函数定义时间做出时,并且该函数未来的所有调用都尊重这个决定。

让我给你举一个例子。Clojure自身通过启用DL来编译`clojure.core`命名空间。这意味着`clojure.core`中的函数会静态地链接到其他`clojure.core`函数。然而,如果开发人员已禁用DL,那么调用`clojure.core`函数将走Var解析。我个人无法想象出一个需要这样精确行为的场景。有人会希望重新定义他们运行时仍然希望某些其他代码使用旧版本的`clojure.core`函数吗?这是否是语言设计想要鼓励的事情?

我的第二个观点是,除了对于在紧密循环中调用的特定函数外,通常DL并不带来相当大的性能提升,这些函数适用于JIT级联优化,因为这会导致内联。这些性能关键函数可以由开发人员通过分析器可靠地识别出来,然后对那些函数只启用DL是有意义的。目前,唯一能确保JIT内联的函数而不需要接受DL整体解决方案的方式是使用半隐秘的`:inline`函数,毕竟这是一种宏。

在实现方面,这可以通过`*compiler-options* :direct-linking`的特殊值(有点类似于`*unchecked-math*`可以是`true`或`:warn-on-boxed`)以及Vars上的元数据来实现(可以重用当前的no-op ^:static)。

简而言之:我希望有一种DL模式,其中所有用DL编译的函数的调用位置都自动直接链接,并且有一种方式可以显式标记函数始终使用DL。其他人觉得这种功能有用吗?我很乐意听听您的想法。

1 个答案

+1

在一些特定点上

有人想要在内容上对旧版本仍然被某些其他代码使用的情况下,重新定义他们运行的clojure.core函数吗?

人们确实这样做,不管这是不是一个好主意。 https://github.com/generateme/fastmath 是一个例子。

我的第二点是,通常,通用的直接链接只会带来相当不实质的性能改进

直接链接不仅仅在于性能改进(正如你所说,通常除了热循环代码之外并不显著)。特别是,不需要为链接加载变量可以显著减少静态初始化器和常量池的大小,从而减小类的大小并缩短加载时间。在广泛的基础上进行直接链接会使这一点变得显著(clojure.core 特别大,因此这一点被放大)。如果我们添加了延迟变量加载,那么加上 DL 将会进一步改善这一点。

尽管如此,我认为DL可能有更多针对性的使用方式,并通知函数的消费者可能会有所帮助(类似于^:redef - "请直接链接我")。但我想最好是从事“我试图做 X 但做不到”的角度来考虑,我没有在这里听到这样的请求。或者,为什么在不应该使用DL的时候不使用它?也许这只是缺乏意识,或者也许是因为它与其他功能或工作流程不兼容。

感谢你的回答,Alex!

> 特别是,不需要为链接加载变量可以显著减少静态初始化器和常量池的大小

公平的话,我没有考虑到这一点。

> "我试图做 X 但做不到"

我将重新强调我在这里有两个用例。

1. 我想确保我的某些函数始终直接链接,以便它们保持可内联(例如,使用类似于 `^:static` meta 的工作)。

2. 我想确保在我的代码或其他地方都始终直接链接所有 `clojure.core` 函数。

目前通过启用DL(延迟链接),1号和2号都可以实现,但根据我在问题中陈述的原因,我不愿意这样做。当然,我的特定需求可能过于具体,所以我创建了这个论坛,希望能够收集更多关于人们可能有的DL用例。如果有更多的话,这可能有助于找出扩展DL可用控制的最适合的方法。
...