gpt4 book ai didi

performance - GHC 中的跨模块优化

转载 作者:行者123 更新时间:2023-12-03 15:29:32 24 4
gpt4 key购买 nike

如果我用 ghc 7.6.1 测量它,我有一个非递归函数来计算似乎表现良好的最长公共(public)子序列( -O2 -fllvm ,用 Criterion 标志编译)在同一个模块中。另一方面,如果我将函数转换为模块,只导出该函数(如推荐的 here ),然后使用 Criterion 再次测量,我会得到 ~2x 减速(如果我将标准测试移回定义函数的模块)。我尝试用 INLINE 标记函数pragma 对跨模块性能测量没有任何影响。

在我看来,GHC 可能会进行严格性分析,当函数和主函数(从该函数可以访问)在同一个模块中时效果很好,但当它们被拆分时则不行。我会很感激有关如何模块化函数的指针,以便在从其他模块调用时它可以很好地执行。有问题的代码太大,无法粘贴到这里 - 你可以看到它here如果你想尝试一下。下面是我正在尝试做的一个小例子(带有代码片段):

-- Function to find longest common subsequence given unboxed vectors a and b
-- It returns indices of LCS in a and b
lcs :: (U.Unbox a, Eq a) => Vector a -> Vector a -> (Vector Int,Vector Int)
lcs a b | (U.length a > U.length b) = lcsh b a True
| otherwise = lcsh a b False

-- This section below measures performance of lcs function - if I move it to
-- a different module, performance degrades ~2x - mean goes from ~1.25us to ~2.4us
-- on my test machine
{--
config :: Config
config = defaultConfig { cfgSamples = ljust 100 }

a = U.fromList ['a'..'j'] :: Vector Char
b = U.fromList ['a'..'k'] :: Vector Char

suite :: [Benchmark]
suite = [
bench "lcs 10" $ whnf (lcs a) b
]

main :: IO()
main = defaultMainWith config (return ()) suite
--}

最佳答案

hammar is right ,重要的问题是编译器可以看到 使用 lcs 的类型,同时它可以看到代码 ,因此它可以将代码专门用于该特定类型。

如果编译器不知道代码应该使用的类型,它就只能产生多态代码。这对性能不利 - 我很惊讶这里只有 ~2 倍的差异。多态代码意味着对于许多操作都需要类型类查找,这至少使得内联查找的函数或常量折叠大小变得不可能[例如用于未装箱的数组/向量访问]。

如果不使需要专门化的代码在使用站点可见(或者,如果您在实现站点知道所需的类型,则在那里专门化,{-# SPECIALISE foo :: Char -> Int, foo :: Bool -> Integer #-} 等),您无法获得与具有实现和在单独模块中使用的单模块情况相当的性能。 )。

使代码在使用站点可见通常是通过标记函数 {-# INLINABLE #-} 在接口(interface)文件中公开展开来完成的。

I tried marking the function with INLINE pragma which didn't make any difference in cross-module performance measurements.



仅标记
lcs :: (U.Unbox a, Eq a) => Vector a -> Vector a -> (Vector Int,Vector Int)
lcs a b | (U.length a > U.length b) = lcsh b a True
| otherwise = lcsh a b False
INLINEINLINABLE 当然没有什么区别,这个函数是微不足道的,编译器无论如何都会暴露它的展开,因为它太小了。即使它的展开没有暴露出来,差异也无法测量。

您还需要公开执行实际工作的函数的展开,至少是多态函数 lcshfindSnakesgridWalkcmp 的展开( cmp 是这里至关重要的,但其他的对于 1. 看到 cmp需要,2.从他们那里调用专门的 cmp)。

制作那些 INLINABLE ,分离模块案例之间的区别

$ ./diffBench 
warming up
estimating clock resolution...
mean is 1.573571 us (320001 iterations)
found 2846 outliers among 319999 samples (0.9%)
2182 (0.7%) high severe
estimating cost of a clock call...
mean is 40.54233 ns (12 iterations)

benchmarking lcs 10
mean: 1.628523 us, lb 1.618721 us, ub 1.638985 us, ci 0.950
std dev: 51.75533 ns, lb 47.04237 ns, ub 58.45611 ns, ci 0.950
variance introduced by outliers: 26.787%
variance is moderately inflated by outliers

和单模块案例

$ ./oneModule 
warming up
estimating clock resolution...
mean is 1.726459 us (320001 iterations)
found 2092 outliers among 319999 samples (0.7%)
1608 (0.5%) high severe
estimating cost of a clock call...
mean is 39.98567 ns (14 iterations)

benchmarking lcs 10
mean: 1.523183 us, lb 1.514157 us, ub 1.533071 us, ci 0.950
std dev: 48.48541 ns, lb 44.43230 ns, ub 55.04251 ns, ci 0.950
variance introduced by outliers: 26.791%
variance is moderately inflated by outliers

小到可以忍受。

关于performance - GHC 中的跨模块优化,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16908539/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com