有没有任何理由不使用“INLINABLE”编译指示符来声明一个函数?

47

文档说明:

一个函数f上的{-# INLINABLE f #-} pragma具有以下行为:
- 虽然INLINE表示“请内联我”,但INLINABLE表示“随意内联我;自行决定”。换句话说,选择留给了GHC,它使用与无编译指示函数相同的规则。与INLINE不同,该决策在调用点处做出,并因此受到内联阈值、优化级别等的影响。 - 与INLINE一样,INLINABLE pragma保留原始RHS的副本以进行内联,并将其持久化在接口文件中,而不管RHS的大小如何。 - 使用INLINABLE的一种方法是与特殊函数inline(第7.18节,“特殊内置函数”)结合使用。调用inline f会尝试非常努力地内联f。为确保f可以内联,最好将f的定义标记为INLINABLE,以便GHC保证公开展开,而不管它有多大。此外,通过将f注释为INLINABLE,您确保f的原始RHS被内联,而不是GHC优化器生成的任意随机优化版本。 - INLINABLE pragma也适用于SPECIALISE:如果将函数f标记为INLINABLE,则可以在另一个模块中后续使用SPECIALISE(参见第7.16.8节,“SPECIALIZE pragma”)。 - 与INLINE不同,在递归函数上使用INLINABLE pragma是可以的。这样做的主要原因是允许稍后使用SPECIALISE。

它的缺点是什么?

它会让接口文件变得非常大吗?编译速度会变得更慢吗?

我是否应该在我编写的每个导出函数上放置INLINABLE pragma? GHC为什么不在我编写的每个导出函数上放置INLINABLE pragma?

1个回答

51
使用INLINABLE和不使用pragma之间有三个区别:
  1. 不使用INLINABLE,接口文件中的定义是优化后的代码,而使用INLINABLE则是你编写的代码(或者更接近你编写的代码)。特别是,在没有使用INLINABLE的情况下,GHC可能会将其他函数内联到函数定义中。
  2. 如果定义太大,不使用INLINABLE,GHC将从接口文件中省略定义。如果某些其他函数被内联到右侧,则很容易将其推到限制之外。
  3. INLINABLE还开启了一些聪明的机制,在使用重载函数时自动进行专业化,并与传递导入创建专业版本的其他模块共享。

11
我正在尝试理解实际影响。第一点的意思是,如果函数没有内联,它会使用未经优化的代码并变得更慢吗?或者如果内联了但规则没有触发,它会变得更慢吗?还是说情况不同,有时快有时慢?关于第二点,在接口文件中包含它除了磁盘空间之外还有其他缺点吗?它们会变得很大以至于成为问题(磁盘很大)吗?至于第三点,它不是一个毫无瑕疵的好处吗?它会导致代码膨胀的问题吗? - glaebhoerl
6
我故意避免谈论实际影响,因为大部分时间我不确定 :-) 我说服Simon PJ添加INLINABLE,是因为我想要一个INLINE的变体,它不会因使用GHC已经相当复杂的内联启发式法而导致太多的代码膨胀。回答你的第一个问题:不,这也将编译成函数的优化版本并嵌入原始模块中(与INLINE一样),对于没有内联的调用将使用该版本。在接口中包含大量函数定义可能会减慢速度。 - Simon Marlow
嗯,好的。有一些关于何时使用它和何时不使用它的指南会很不错,但我猜我得自己尝试一下。 :) - glaebhoerl
5
我知道这个问题有些老了,但我只是想问一个问题以确保我理解你的回答:这是否意味着使用 INLINABLE 的唯一劣势,与不使用它相比,就是会减慢编译时间或增加接口文件的大小? - Gabriella Gonzalez
21
再试一次:如果我们生活在一个每个函数自动获得INLINABLE pragma的世界里,这有什么不好的地方? - glaebhoerl

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接