gpt4 book ai didi

haskell - 为什么这个函数在命名时会慢很多?

转载 作者:行者123 更新时间:2023-12-05 05:53:12 24 4
gpt4 key购买 nike

我有一个计算整数除数的函数:

numDivisor = ( \n -> length $ filter (\x -> n `mod` x == 0) [1..n] )

当我在 GHCI 中运行这个函数时,无论是否给它命名,都会有一个常量 ~1.4 倍的性能损失。可复制到 ghci 的示例:

:set +s
x = 20000000
( \n -> length $ filter (\x -> n `mod` x == 0) [1..n] ) x
numDivisor = ( \n -> length $ filter (\x -> n `mod` x == 0) [1..n] )
numDivisor x


结果:

Prelude> ( \n -> length $ filter (\x -> n `mod` x == 0) [1..n] ) x
72
(8.67 secs, 3,040,063,784 bytes)
Prelude> numDivisor x
72
(12.68 secs, 4,000,064,048 bytes)

无论 x 是 20000000、200000 还是低于 10000,命中率都是 ~1.4,当我在“where”中命名我的函数时也会发生这种情况。在下一个示例中,我计算从 1 到 x 的所有数字的除数,然后将它们删除:

:set +s
x = 10000
divisorListAnon = map ( \n -> length $ filter (\x -> n `mod` x == 0) [1..n] ) [1..]
filter odd $ filter even $ take x divisorListAnon
:{
divisorListNamed = map numDivisor [1..]
where numDivisor = ( \n -> length $ filter (\x -> n `mod` x == 0) [1..n] )
:}
filter odd $ filter even $ take x divisorListNamed


结果:

Prelude> filter odd $ filter even $ take x divisorListAnon
[]
(21.17 secs, 7,613,246,464 bytes)
Prelude> filter odd $ filter even $ take x divisorListNamed
[]
(29.84 secs, 9,213,726,504 bytes)

谁能指点一下这是怎么回事?另外,如何在不影响性能的情况下使用和命名该子例程?我宁愿给它一个名字,也不要匿名使用它。

谢谢,


编辑:

ubuntu 20.04,来自 ubuntu 分布式 ghc 包的 ghc 8.6.5,不是堆栈

最佳答案

(\n -> length $ filter (\x -> n `mod` x == 0) [1..n] ) x 的类型为 Num a =>诠释。 “隐藏”类型变量a(x 的类型)默认为Integer。我想,这发生在 GHCi 生成其字节码之前,并且它足够聪明,可以在已知数字类型时使用 Integer 特化的操作而不是通用的 Num 方法为整数

let-绑定(bind)函数将其概括为forall a。 Num a => a -> Int。在 Core 中间语言中,该函数将 Num a 字典作为参数,所有对数值的操作都通过字典。 GHCi 在不知道 a 类型的情况下将函数编译为字节码。

在 GHCi 中,您可以通过为 numDivisor 指定显式类型 Integer -> Int 来解决这个问题。 GHC(编译器)很可能会自动将其专门化为 Integer,因此不会有性能差异。

您也可以通过在 GHCi 中启用单态限制来解决这个问题,这将导致 xnumDivisor 立即默认为IntegerInteger -> Int

关于haskell - 为什么这个函数在命名时会慢很多?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69943874/

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