Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/xamarin/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Performance GHC中的跨模块优化_Performance_Haskell_Optimization_Criterion - Fatal编程技术网

Performance GHC中的跨模块优化

Performance GHC中的跨模块优化,performance,haskell,optimization,criterion,Performance,Haskell,Optimization,Criterion,我有一个非递归函数来计算最长的公共子序列,如果我在同一个模块中使用标准对其进行测量,该子序列似乎表现良好(ghc 7.6.1,使用-O2-fllvm标志编译)。另一方面,如果我将函数转换为一个模块,只导出该函数(按照建议),然后使用Criteria再次测量,我会得到~2x的减速(如果我将Criteria测试移回定义函数的模块,则会消失)。我尝试用INLINEpragma标记函数,这在跨模块性能度量中没有任何区别 在我看来,GHC可能正在进行严格性分析,当函数和main(可以从中访问函数)位于同一

我有一个非递归函数来计算最长的公共子序列,如果我在同一个模块中使用
标准
对其进行测量,该子序列似乎表现良好(
ghc 7.6.1
,使用
-O2-fllvm
标志编译)。另一方面,如果我将函数转换为一个模块,只导出该函数(按照建议),然后使用Criteria再次测量,我会得到~2x的减速(如果我将Criteria测试移回定义函数的模块,则会消失)。我尝试用
INLINE
pragma标记函数,这在跨模块性能度量中没有任何区别

在我看来,GHC可能正在进行严格性分析,当函数和main(可以从中访问函数)位于同一个模块中时,这种分析效果很好,但当它们被拆分时,这种分析就不起作用了。我希望您能给我一些关于如何模块化函数的建议,以便在从其他模块调用函数时能够很好地执行。所讨论的代码太大,无法粘贴到此处-如果您想尝试,可以查看它。下面是我尝试做的一个小示例(带有代码片段):

,重要的问题是编译器可以在查看代码的同时查看
lcs
使用的类型,因此它可以将代码专门化为该特定类型

如果编译器不知道使用代码的类型,它只能生成多态代码。这对性能很不利——我很惊讶这里只有2倍的差异。多态代码意味着,对于许多操作,都需要类型类查找,这至少使得无法内联查找的函数或常量折叠大小[例如,对于非固定数组/向量访问]

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

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

我尝试用
INLINE
pragma标记函数,这在跨模块性能度量中没有任何区别

仅标记

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
INLINE
inlineable
没有什么区别。当然,这个函数很简单,而且编译器公开了它的展开,因为它太小了。即使它的展开没有暴露出来,差异也无法测量

您还需要公开执行实际工作的函数的展开,至少是多态函数的展开,
lcsh
findSnakes
gridWalk
cmp
cmp
是这里最关键的一个,但是其他的都是必要的1.看到
cmp
是需要的,2.从他们那里调用专门的
cmp

使这些
可插入
,独立模块外壳之间的差异

$。/diffBench
热身
估计时钟分辨率。。。
平均值为1.573571 us(320001次迭代)
在319999个样本中发现2846个异常值(0.9%)
2182人(0.7%)高度严重
估计时钟呼叫的成本。。。
平均值为40.54233纳秒(12次迭代)
基准lcs 10
平均值:1.628523 us,磅1.618721 us,ub 1.638985 us,置信区间0.950
标准偏差:51.75533纳秒,磅47.04237纳秒,ub 58.45611纳秒,置信区间0.950
异常值引入的方差:26.787%
异常值适度夸大了方差
以及单模块机箱

$。/oneModule
热身
估计时钟分辨率。。。
平均值为1.726459 us(320001次迭代)
在319999个样本中发现2092个异常值(0.7%)
1608人(0.5%)高度严重
估计时钟呼叫的成本。。。
平均值为39.98567纳秒(14次迭代)
基准lcs 10
平均值:1.523183 us,磅1.514157 us,ub 1.533071 us,置信区间0.950
标准偏差:48.48541纳秒,磅44.43230纳秒,ub 55.04251纳秒,置信区间0.950
异常值引入的方差:26.791%
异常值适度夸大了方差

小得可以忍受。

试试内联函数。它可能会更好。@Carl,在lcs函数中尝试过它。还是一样。我怀疑问题是,当它都在一个模块中时,GHC可以将类型变量
a
专门化为
Char
,因为它从未与任何其他类型一起使用,从而消除了类型类开销。您可以我在玩
SPECIALIZE
pragma(或者手动将其更改为
Char
),看看是否有效果。我自己也曾多次看到过同样的事情,每次我都认为“应该有一个程序将多模块Haskell程序转换为单模块程序,并对两个模块进行基准测试”这将使我们更容易知道是否应该篡改<代码> >代码> >代码>内联< <代码> >代码>可链接< /代码>。是否存在这样的事情?它本质上应该归结为名称的修饰,对吗?我仍然希望有一个语用学告诉GHC在特定的参数的值的基础上专门化一个函数,如果它是已知的。在编译时,这对高阶函数的高性能使用是一个巨大的帮助。很好的一点。我在试图分析这个问题时忘记了专门化。
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