Haskell(GHC)专业巡回演唱会&高效的TypeFamilies

时间:2017-10-13 16:50:31

标签: performance haskell ghc type-families

Haskell就是抽象。但是抽象会花费额外的CPU周期和额外的内存使用量,因为所有抽象(多态)数据的常见表示 - 堆上的指针。有一些方法可以使抽象代码更好地满足高性能需求。据我了解,它的一种方式是专业化 - 基本上是额外的代码生成(手动或编译器),对吗?

让我们假设下面的所有代码都是严格(这有助于编译器执行更多优化?)

如果我们有一个函数sum

sum :: (Num a) => a -> a -> a

我们可以使用specialize pragma:

生成它的专用版本
{-#SPECIALIZE sum :: Float -> Float -> Float#-}

现在,如果haskell编译器可以在编译时确定我们在两个sum上调用Float,那么它将使用它的专用版本。没有堆分配,对吧?

功能 - 完成。相同的pragma可以应用于类实例。逻辑在这里不会改变,是吗?

但是数据类型呢? 我怀疑TypeFamilies在这里负责?

让我们尝试专门化依赖长度索引的列表。

--UVec for unboxed vector
class UVec a where
   data Vec (n :: Nat) a :: *

instance UVec Float where
   data Vec n Float where
     VNilFloat :: Vec 0 Float
     VConsFloat :: {-#UNPACK#-}Float ->
                   Vec n Float -> 
                   Vec (N :+ 1) Float

Vec有问题。我们不能在其构造函数上进行模式匹配 UVec的每个实例都不必为Vec提供相同的构造函数。这迫使我们为Vec的每个实例在Vec上实现每个函数(因为缺少模式匹配意味着它不能在Vec上具有多态性)。在这种情况下,最佳做法是什么?

2 个答案:

答案 0 :(得分:1)

正如您所说,我们无法在UVec a上模式匹配,而不知道a是什么。 一种选择是使用另一个使用自定义函数扩展vector类的类型类。

class UVec a => UVecSum a where
   sum :: UVec a -> a

instance UVecSum Float where
   sum = ... -- use pattern match here

如果稍后我们使用sum v v :: UVec Float,则会调用我们在实例中定义的Float特定代码。

答案 1 :(得分:0)

部分答案,但也许可能有所帮助。

  

据我了解,其中一种方法是专业化 - 基本上是额外的代码生成(手动或编译器),对吗?

是的,这类似于C ++模板中的代码实例化。

  

现在如果haskell编译器可以在编译时确定我们在两个Floats上调用sum,那么它将使用它的专用版本。没有堆分配,对吧?

是的,编译器会尽可能调用专用版本。不确定你对堆分配的意思。

关于依赖类型向量:通常(我从Idris知道这个),编译器尽可能消除向量的长度。它旨在进行更强大的类型检查。在运行时,长度信息是无用的,可以删除。