类型良好的博客 https://well-typed.com/blog/ 因为类型良好的程序不会出错 英语 2024年5月22日星期三00:00:00 GMT 2024年5月22日星期三00:00:00 GMT 类型良好的博客生成器 https://well-typed.com/img/wtnlogoplain.svg网址 类型良好的博客 https://well-typed.com/blog/ 因为类型良好的程序不会出错 哈斯克尔展开第26集:可变整体函数 https://well-typed.com/blog/2024/05/haskell-unfolder-episode-26-可变权限功能 埃德斯科·安德烈斯 会谈 社区 训练 辅导的 youtube(youtube) 展开器 http://www.well-typed.com/blog/2024/05/haskell-unfolder-episode-26-variable-arity-functions网站 2024年5月22日星期三00:00:00 GMT 今天,2024-05-22,UTC时间1830(太平洋夏令时上午11:30,美国东部夏令时下午2:30,英国夏令时晚上7:30,欧洲夏令时20:30,…)我们正在播放《哈斯克尔展开》第26集在YouTube上直播

哈斯克尔展开第26集:可变arity函数

在这一集中,我们将了解如何使用Haskell的类系统对参数数量可变的函数进行编码,并讨论一些此类函数可能有用的示例。

关于Haskell展开

Haskell Unfolder是一个YouTube系列,讲述了Haskel主持的所有事情埃德斯科·德·弗里斯安德烈斯·洛赫,大约每两集出现一集周。所有的剧集都是现场直播的,我们尽量回应观众问题。所有剧集也可以在之后录制。

我们有一个GitHub存储库带有剧集中的代码示例。

我们有一个公共谷歌日历(也可作为ICal公司)列出计划的时间表。

现在还有一个网上商店在那里你可以买到t恤和马克杯(可能还有在未来的其他项目)带有Haskell Unfolder标志。

]]>
哈斯克尔揭秘第25集:从爪哇到哈斯克尔 https://well-typed.com/blog/2024/05/haskell-unfolder-episode-25-from-java-to-haskell 埃德斯科·安德烈斯 会谈 社区 训练 辅导的 youtube(youtube) 展开器 http://www.well-typed.com/blog/2024/05/haskell-unfolder-episode-25-from-java-to-haskell 2024年5月15日星期三00:00:00 GMT 今天,2024-05-15,UTC时间1830(太平洋夏令时上午11:30,美国东部夏令时下午2:30,英国夏令时晚上7:30,欧洲夏令时20:30,…)我们正在播放《哈斯克尔展开》第25集在YouTube上直播

哈斯克尔揭秘第25集:从爪哇到哈斯克尔

在本集中,我们将尝试将用Java编写的gRPC服务器转换为Haskell。我们将以它为例来演示这两种语言在概念上的一些差异,但也会注意到翻译的最终结果可能比人们预期的更接近Java版本。与我们的大多数剧集不同,我们希望这一集对任何软件开发人员来说都是可以理解的,即使是以前没有接触过Haskell的人。当然,我们无法解释所有内容,但所使用的示例应该有助于建立Haskell代码的外观概念,并可能进一步了解面向对象编程范式和函数编程范式之间的关系。

关于Haskell展开

Haskell Unfolder是一个YouTube系列,讲述了Haskel主持的所有事情埃德斯科·德·弗里斯安德烈斯·洛赫,大约每两集出现一集周。所有的剧集都是现场直播的,我们尽量回应观众问题。所有剧集也可以在之后录制。

我们有一个GitHub存储库带有剧集中的代码样本。

我们有一个公共谷歌日历(也可作为ICal公司)列出计划的时间表。

]]>
哈斯克尔展开第24集:普通折叠 https://well-typed.com/blog/2024/05/haskell-unfolder-episode-24-generic-unfolds 埃德斯科·安德烈斯 会谈 社区 训练 辅导的 youtube(youtube) 展开器 http://www.well-typed.com/blog/2024/05/haskell-unfolder-episode-24-generic-unfolds 2024年5月1日星期三00:00:00 GMT 今天,2024-05-01,UTC时间1830(太平洋夏令时上午11:30,美国东部夏令时下午2:30,英国夏令时晚上7:30,欧洲夏令时20:30,…)我们正在播放《哈斯克尔展开》第24集在YouTube上直播

哈斯克尔展开第24集:普通折叠

在我们的一周年纪念插曲中,我们将回到《哈斯克尔展开》的开头,谈论展开和折叠。但这一次,不仅在列表上,而且在更广泛的数据类型类上,即那些可以作为函子的固定点写入的数据类型。

关于Haskell展开

Haskell Unfolder是一个关于Haskell主持的所有事情的YouTube系列节目埃德斯科·德·弗里斯安德烈斯·洛赫,大约每两集出现一集周。所有的剧集都是现场直播的,我们尽量回应观众问题。所有剧集也可以在之后录制。

我们有一个GitHub存储库带有剧集中的代码示例。

我们有一个公共谷歌日历(也可作为ICal公司)列出计划的时间表。

]]>
ghc-debug终端接口的改进 https://well-typed.com/blog/2024/04/ghc-debug-improvements网站 马修、祖宾、汉纳斯 打字精良 温室气体 ghc调试 社区 性能 记忆 水星 剖析 http://www.well-typed.com/blog/2024/04/ghc-debug-improvements网站 2024年4月24日星期三00:00:00 GMT ghc调试是对Haskell程序执行精确堆分析的调试工具(查看我们的上一篇介绍它的帖子).工作时擦除分析,我们抓住机会赚了很多需要改进和改善生活质量ghc调试图书馆和ghc-调试块终端用户界面。

总之,

  • ghc调试现在可以无缝地处理已分析的可执行文件。
  • 这个ghc-调试块UI已经围绕可组合、基于过滤器的工作流进行了重新设计。
  • 现在可以使用库检查成本中心和其他分析元数据接口和TUI。
  • 终端界面中集成了更多分析模式,如2级配置文件。

这篇文章探讨了这些变化和检查的新可能性它们启用的Haskell进程堆。这些更改可用通过使用0.6.0.0的版本ghc-debug-子ghc-调试块

回顾:使用ghc调试

使用时通常涉及两个过程ghc调试在直播节目中。第一个是调试对象进程,这是要检查其堆的进程。调试对象进程与ghc-debug-子包裹。这个ghc-debug-子包提供包装器函数

带GhcDebug:: IO(输入输出)-> IO(输入输出)

你把你的主要的函数启用ghc调试。此包装打开unix套接字并回答有关调试对象进程堆的查询,包括传输有关调试对象的各种元数据,例如编译调试对象时使用的ghc版本,以及组成堆上各种对象的实际位。

第二个是调试器进程,它通过套接字查询调试对象机制,并解码响应以重建被调试对象的视图哈斯克尔堆。人们使用的最常见的调试器是ghc-调试块,其中提供用于与调试对象进程交互的TUI。

这是一个重要的原则ghc调试调试器和调试对象没有需要使用彼此相同的GHC版本进行编译。换句话说,一次编译的调试器可以灵活地与许多不同的调试人员一起工作。使用我们最新的更改调试器现在可以与已分析的可执行文件无缝协作。

TUI改进

探索TUI中的成本中心堆栈

为了调试已分析的可执行文件,我们添加了对解码的支持中的配置信息ghc调试库。添加解码支持后,很容易显示TUI中每个关闭的相关成本中心堆栈信息,允许您交互式地探索成本链具有导致分配特定闭包的源位置的中心。这将为您提供与调用全球总部。Stack.who创建功能对一个闭包,但对堆上的每个闭包!此外,ghc-调试块允许您搜索已经在特定成本中心下分配。

查看特定列表关闭的成本中心堆栈

正如我们在eras分析博客帖子,对象地址根据分配的年代进行着色。

擦除配置,每个时代都有不同的颜色

如果启用了其他分析模式,如固定器分析或传记分析,然后,这些模式跟踪的额外单词被用来用绿线标记使用过的闭包。

简介,使用过的封口标记为绿色

基于筛选器的工作流

典型ghc调试块工作流将包括连接到客户端流程或快照,然后运行查询(如搜索)来跟踪您感兴趣。这采用了中可用的各种搜索命令的形式用户界面:

ghc-debug-brick中现在可用的命令

然而,有时您希望组合多个搜索命令,以便更精确地缩小你感兴趣的确切对象必须通过使用ghc调试哈斯克尔API或修改ghc-调试块代码本身支持您的自定义查询。

过滤器提供了一个可组合的工作流,以便执行更高级的查询。您可以从可能的筛选器列表中选择要应用的筛选器,如构造函数名称、闭包大小、纪元等,并将其添加到当前过滤器堆栈进行自定义搜索查询。每个过滤器也可以反转。

ghc-debug-brick中可用的过滤器

在实现了对eras评测的支持后,我们有动力添加此功能因为将现有查询与按时代筛选相结合通常很有用。使用这些过滤器,可以轻松表达您自己的特定于域的查询,例如:

  • 查找在特定时代分配的施工人员。
  • 查找全部箭头大于1000字节的闭包。
  • 让我看看这个时代留下的一切,除了箭头格雷建造师。

以下是当前可用过滤器的完整列表:

姓名 输入 例子 行动
地址 结束地址 0x421c3d93c0 找到带有特定地址的闭包
信息表 信息表地址 0x1664ad70 查找具有特定信息表的所有闭包
施工单位名称 施工单位名称 箱子 查找具有给定构造函数名称的所有闭包
闭包名称 闭包名称 状态_存储J_信息 查找具有特定闭包名称的所有闭包
时代 <era>/<start-era>-<end-era> 13或9-12 查找给定时代范围内分配的所有闭包
成本中心ID 成本中心ID 107600 查找在此成本中心ID下分配的所有关闭(直接或间接)
封口尺寸 国际 1000 查找大于特定大小的所有闭包
闭合类型 闭包类型描述 箭头 查找所有ARR_WORDS闭包

所有这些查询都是保留查询,它们不仅会显示闭包有疑问,但也包括护圈堆栈,它解释了为什么要保留它们。

分析命令的改进

ghc-调试块早就提供了一个配置文件命令来执行堆遍历并提供类似于来自-小时轮廓。此查询的结果现在以交互方式显示在终端界面中。对于每个条目,标题中的左栏显示中的闭包类型问题,分配的此闭包类型的总数,此闭包占用堆上的字节数,每个的最大大小这些闭包以及每个已分配闭包的平均大小。右列显示了相同的统计信息,但接管了当前堆样本。

一个1级配置文件,显示在ghc-debug-brick中

每个条目都可以展开,每个频带中的五个采样点都会保存下来,这样您就可以检查一些影响带尺寸的封口。例如,这里我们扩展THUNK闭包,可以看到5个THUNK的样本,其中有21万个住在这堆地上的暴徒。

在一级配置文件中展开THUNK条目

TUI还增加了对2级闭合型剖面的支持。作为配置文件,2级配置文件比1级配置文件粒度更细键还包含闭包的指针参数,而不仅仅是闭包本身。钥匙:[(,), :]表示list-cons构造函数,其中head参数是一个2元组,尾部参数是另一个列表cons。

显示在ghc-debug-brick中的2级配置文件

例如,在2级配置文件中,不同类型的列表将显示为不同的带区。在上面的配置文件中,您可以看到4种不同类型的列表产生的4个不同的波段。Thunk通常也会单独出现,因为它们也根据他们的不同论点。示例功能也适用于2级配置文件所以很容易理解每个波段到底对应什么您的程序。

其他UI改进

除了上面讨论的新功能外,最近的一些其他增强功能包括:

  • 提高了显示大量数字时主视图的性能第行,共行。这显著减少了滚动时的输入延迟。
  • 搜索限制被硬编码为100个对象,这意味着只有搜索的前几个结果将在UI中可见。这个现在可以在UI中配置限制。
  • TUI中现在提供了其他分析,例如发现重复箭头闭包,这对于识别程序最终存储许多相同字节字符串的副本。

结论

我们希望ghc调试ghc-调试块将有助于任何希望对其堆进行详细检查的人的工作流Haskell过程。

这项工作是与水银Mercury长期致力于Haskell的可扩展性和健壮性生态系统,并支持开发内存评测工具帮助实现这些目标。

Well-Typed总是对项目感兴趣,并寻求资金来改进GHC和其他Haskell工具。请联系info@well-typed.com如果我们也许能和你一起工作!

]]>
与GHC专业人员一起设计舞蹈(第一部分) https://well-typed.com/blog/2024/04/choreographing-specialization-pt1 芬利 温室气体 性能 哈苏拉 展开器 辅导的 youtube(youtube) http://www.well-typed.com/blog/2024/04/choreographing-specialization-pt1 2024年4月15日星期一00:00:00 GMT 专业化是GHC用来消除ad-hoc多态性的性能开销,并支持其他强大的优化。然而,专业化并不是免费的,因为它需要更多的工作由GHC在编译期间执行,并导致更大的可执行文件。事实上,过度专业化会导致编译成本和可执行文件大小,运行时性能优势最小。因此,GHC悲观地避免了默认情况下的过度专业化,可能会离开在这样做的过程中,没有发现相对低成本的性能改进。

乐观的Haskell程序员希望利用这些错过的机会因此,机会面临着发现和制定的艰巨任务在平衡任何通过增加编译成本和可执行文件提高性能尺寸。到目前为止,这是一种笨重的舞蹈,需要拼命涉水通过GHC核心转储,只会产生不稳定、效率低下、,似乎可以提高性能的一组无动机的杂注和/或GHC标志。

在这个由两部分组成的系列文章中,我描述了我们最近所做的工作改善这种情况,使Haskell程序更加专业化这是一门科学,而不是一门黑暗的艺术。在第一篇帖子中,我将

  • 全面介绍GHC的专业化优化,
  • 探索GHC为观察和控制它提供的各种设施,以及
  • 提出一个简单的框架来思考专业化。

在本系列的下一篇文章中,我将

  • 介绍我们开发用于诊断的新工具和技术由自组织多态性引起的性能问题,
  • 演示如何系统地使用这些新工具确定有用的专业,以及
  • 从以下方面理解他们的影响本文中描述的框架。

本文的目标读者包括中级Haskell开发人员,他们想更多了解GHC中的专门化和特殊多态性,以及对系统方法感兴趣的高级Haskell开发人员以最小化编译成本和可执行文件大小,同时最大限度地提高性能。

这项工作之所以能够进行,是因为哈苏拉,谁有支持Well-Typed的许多成功举措改进商业Haskell用户的工具。

我在Haskell Unfolder上总结了这篇文章的内容:

哈斯克尔揭秘第23集:专业化

重载函数在Haskell中很常见,但也有代价。值得庆幸的是,GHC的专家非常善于消除这一成本。因此,我们可以编写高级的多态程序,并相信GHC会将它们编译成非常高效的单态化代码。在本集中,我们将揭开GHC为实现这一目标所做的看似神奇的事情的神秘面纱。

特殊多态性

在哈斯克尔特殊多态性过载函数是其类型包含类约束。例如,这个(f)是重载函数:

传真::(订单a、,号码a)=>->->
f x y轴=
    如果x个<然后
x个+
    其他的
x个-

对于某些类型这样的话命令a数字a提供了实例,(f)采用两个类型的值并对另一个进行评估

重要的是,与类型参数不同,这些类约束不会在运行时!事实上,他们会被传给(f)就像其他任何值一样参数,含义(f)在运行时更像是:

传真:: 订单-> 号码->->->
f orda数字a x y= ...

如何定义(f)改变来代表这个?这些是什么订单a数字(&a)值看起来像什么?其工作原理如下:

  • 实例被编译为记录,通常称为字典,其字段是实例中提供的定义。
  • 类函数(例如<在的身体里(f))成为记录选择器应用于字典以查找适当的定义。

因此,(f)在运行时更像是:

传真:: 订单-> 号码->->->
f orda数字a x y=
    如果(<)订单a x y然后
(+)数字a x y
    其他的
(-)数字a x y

以前的infix类运算符现在应用于前缀位置从字典中选择适当的定义,然后应用于参数。

我们可以通过编译(f)在模块中F.hs公司并发出中间表示(以GHC的核心语言):

温室气体F.hs公司-O(运行) -dno可键入索引 -dsuppress-all所有 -数据抑制-唯一 -ddump数据库

这个-O(运行)标志启用优化,并且-ddump-ds(数据转储-ds)flag告诉GHC转储程序在去糖后,优化前。其他标志使输出更具可读性。

全面介绍GHC核心和GHC接受的标志查看,查看哈斯克尔揭秘第九集:GHC核心

上述命令将为输出以下Core(f)以下为:

(f)=\@$命令$dNum x y(数字x y)->
      案例 < $dOrd x y(顺序x y)属于{
        False(错误) -> - $dNum x y(数字x y);
        真的 -> + $dNum x y(数字x y)
}

这个如果已转换为案例(核心没有如果构造)。这个$订单$dNum(数字)参数是命令a数字a实例字典,分别是。这个<运算符应用于前缀位置操作员(核心)$订单字典以获取适当的实施<,进一步应用于x个. The-+分支机构中的操作员案例都是类似的。

传递这些隐式字典参数和对它们应用选择器确实会产生可测量的开销,尽管是这样对于大多数意图和目的来说无关紧要。正如我们将看到的ad-hoc多态性来自于它所阻止的优化,而不是它引入的开销。

专业化

在这种情况下,专业化指移除特殊多态性。当我们专门化重载表达式时e::C a=>S a,我们创建一个新绑定eT::S T,其中T型是某种混凝土类型对于其中一个C T公司实例存在。在这里电子技师专业化 e(电子) (或)类型T型

例如,我们可以手动创建(f)在类型国际. The源定义保持不变,只有类型更改:

fInt:: 国际 -> 国际 -> 国际
fInt x y(积分x年)=
    如果x个<然后
x个+
    其他的
x个-

在核心级别,作为值参数传递给(f)现在直接用于fInt公司。如果我们添加fInt公司对我们的示例模块进行编译,我们得到以下输出:

(f)=\@$命令$dNum x y(数字x y)->
      案例 < $dOrd x y(顺序x y)属于{
        False(错误) -> - $dNum x y(数字x y);
        真的 -> + $dNum x y(数字x y)
}

fInt公司
  =\x年->
      案例 < $fOrdInt x y(顺序整数x y)属于{
        False(错误) -> - $fNumInt x y;
        真的 -> + $fNumInt x年
}

飞行情报不再接受字典参数,而是引用全局订单Int数字Int直接使用字典。事实上,这个定义fInt公司确切地如果GHC专家决定创造什么专门从事(f)国际。我们可以通过手动指示自行查看GHC使用专门从事杂注。我们的整个模块现在是:

模块 F类 哪里

{-#指定f::Int->Int->Int#-}

传真::(订单a、,号码a)=>->->
f x y轴=
    如果x个<然后
x个+
    其他的
x个-

传真:: 国际 -> 国际 -> 国际
fInt x y(积分x年)=
    如果x个<然后
x个+
    其他的
x个-

以及-ddump-ds(数据转储-ds)核心输出变为:

fInt公司
  =\x年->
      案例 < $fOrdInt x y(顺序整数x y)属于{
        False(错误) -> - $fNumInt x y;
        真的 -> + $fNumInt x y(数字积分x y)
}

$平方英尺
  =\x年->
      案例 < $fOrdInt x y(顺序整数x y)属于{
        False(错误) -> - $fNumInt x y;
        真的 -> + $fNumInt x y(数字积分x y)
}

(f)=\@$命令$dNum x y(数字x y)->
      案例 < $dOrd x y(顺序x y)属于{
        False(错误) -> - $dNum x y(数字x y);
        真的 -> + $dNum x y(数字x y)
}

GHC产生的专业化被命名为平方英尺美元(GHC的所有专业生成的前缀为%s美元). 请注意,我们的专业化(fInt公司)以及GHC产生的专业化(平方英尺美元)完全等价!

为什么这是一个优化?

上述转变实际上是GHC专家对我们的程序。可能还不清楚为什么这种优化是有意义的完全优化。这是因为专业化是一种有可能优化:真正的好处来自它所支持的优化稍后在管道中,例如内联。

内联将定义的(顶级或let-bound)变量替换为他们的定义。尽管(f)及其专业化平方英尺美元看起来很相似关键区别在于(f)包括对作为部分传递的“未知”函数的调用字典参数的,而平方英尺美元包括对“已知”函数的调用包含在$fOrdInt(美元)$fNumInt(美元)字典。由于GHC可以访问这些字典和包含的函数的定义可以是内联,提供更多优化机会。

通过比较我们的示例模块。为此,使用与上面的命令相同,但添加-ddump-simpl(ddump-impl)标志,它告诉GHC在Core优化管道的末端转储Core(同时添加-强制重新补偿强制重新编译,因为自上次编译以来,我们没有更改代码):

温室气体F.hs公司-强制重新补偿 -O(运行) -dno可键入索引 -dsuppress-all所有 -数据抑制-唯一 -ddump-ds(数据转储-ds) -ddump-simpl(ddump-impl)

转储输出为:

==================== 脱糖剂(优化后)====================
结果大小属于 脱糖剂(优化后)
  ={条款以下为: 57,类型以下为: 37,胁迫以下为: 0,个连接以下为: 0/0}

fInt公司
  =\x年->
      案例 < $fOrdInt x y(顺序整数x y)属于{
        False(错误) -> - $fNumInt x y;
        真的 -> + $fNumInt x y(数字积分x y)
}

$平方英尺
  =\x年->
      案例 < $fOrdInt x y(顺序整数x y)属于{
        False(错误) -> - $fNumInt x y;
        真的 -> + $fNumInt x y(数字积分x y)
}

(f)=\@$命令$dNum x y(数字x y)->
      案例 < $dOrd x y(顺序x y)属于{
        False(错误) -> - $dNum x y(数字x y);
        真的 -> + $dNum x y(数字x y)
}

==================== 整洁 核心 ====================
结果大小属于 整洁 核心
  ={术语以下为: 44,类型以下为: 29,胁迫以下为: 0,联接以下为: 0/0}

fInt公司
  =\x年->
      案例x个属于{#x1个->
      案例属于{#y1个->
      案例 <#x1 y1个属于{
__默认-> #(-#x1 y1);
        1# -> #(+#x1 y1)
}
}
}

(f)=\@$命令$dNum x y(数字x y)->
      案例 < $dOrd x y(顺序x y)属于{
        False(错误) -> - $dNum x y(数字x y);
        真的 -> + $dNum x y(数字x y)
}

------导入的ID的本地规则--------
“USPEC f@Int” 对于所有人 $数字$命令(f)$命令$数字=fInt公司

脱糖过程的输出在“脱糖(优化后)”中部分,而完全优化的输出位于“Tidy Core”部分。这个名称“去糖(优化后)”仅表示它是去糖核心输出在GHC的简单优化器运行之后。简单的优化器只做非常对Core程序的轻量级、纯转换。我们仍将提及这一阶段的核心产出为“未优化”。

在整个优化过程中,GHC确定了fInt公司平方英尺美元并决定删除平方英尺美元。的完全优化绑定飞行情报正在取消装箱国际s(模式匹配我#建造商)和使用有效的基本操作(<#,-#,+#),而完全的优化绑定(f)与未优化的绑定相同。优化器在这种情况下,那些晦涩难懂的字典简直什么也做不了!

输出的底部是重写规则那个专门从事创建了杂注,这将导致(f)已知处于类型国际作为应用程序重写fInt公司。这是是什么让程序的其余部分从专业化中受益。规则只需放弃字典参数$dNum::整数$dOrd::订单Int,这是安全的,因为全球类型类一致性:任何字典显式传递的必须最初来自相同的全局实例。

总之,通过将不透明字典参数替换为(f)带有参考混凝土订单Int数字Int词典fInt公司,GHC能够稍后在管道中进行更多优化。

自动专业化

在我们的示例模块中,我们手动指示GHC生成专门化属于(f)国际使用专门化实用主义。实际上,我们通常依赖GHC来确定需要哪些专业化并生成它们自动为我们服务。但GHC需要小心,因为专业化需要创建和优化更多绑定,这会增加编译成本和可执行文件大小

GHC使用了几种启发式方法,通过违约。启发式非常悲观,这意味着GHC很容易漏掉程序员可能希望手动实现的宝贵专业化机会地址。这正是我们最近工作的目的所以在我们继续之前,我们必须准确地了解GHC决定专业化应该(或不应该)发生的时间和原因。

自动专业化何时发生?

GHC仅可能尝试自动专业化确切地场景:在具体的、静态已知的类型上遇到重载调用(从现在起,我们将此类调用称为“可专门化”调用)。这意味着自动专门化只会在调用站点触发,而不会定义站点。即使在这种情况下,也需要考虑其他因素下面的示例将对此进行演示。

让我们添加一个绑定foo公司到我们的示例模块F.hs公司从上面:

foo::(整数,整数)-> 整数
foo(x,y)=f x y轴

foo公司对进行专门化调用(f)混凝土类型整数,所以我们可能期望自动专业化发生。然而,内联程序胜过这里是冲床的特工,这在-ddump-simpl(ddump-impl)输出:

$世界粮食组织
  =\网址:ww1->
      案例integerLt网址ww1属于{
        False(错误) ->integerSub ww1;
        真的 ->integer添加ww1
}

foo公司=\ds公司-> 案例ds公司属于{(ww,ww1)-> $wfoo网址ww1}

GHC没有专门化,而是决定通过内联完全消除调用(f)从而暴露出其他优化机会(例如工人/包装工)GHC利用了这一点。这是计划,因为(f)很小,GHC知道内联非常便宜,而且可能值得绩效结果。

另一种观察GHC内联决策的方法是通过-ddump插件标志,导致GHC转储它决定内联的任何绑定的名称。使用编译模块

温室气体F.hs公司-O(运行) -强制重新补偿 -ddump插件

输出结果表明GHC确实决定内联(f)以下为:

内嵌完成:F.F

内联还是专门化?

如果可能,GHC更喜欢内联而不是专门化,因为内联消除了调用,不需要创建新绑定。然而,过多的内联通常甚至更多危险的而不是过度专业化。因此,即使一个专门化呼叫被认为成本过高而无法内联,GHC也会仍然尝试将其专门化。

我们可以通过调整GHC来人为地在我们的示例中创建这样的场景称之为“展开使用阈值”。粗略地说,“展开”是一个定义GHC决定内联或专门化对该绑定的调用时使用的绑定结合。展开使用阈值控制最大有效值大小1GHC将纳入的展项使用手动调整-折叠式美国门槛标志。让我们设置将使用阈值展开为-1,本质上使GHC认为所有内联都非常价格昂贵,请检查-ddump-simpl(ddump-impl)输出:

ghc F.hs-O-force-recomp-ddump-simpl-funfolding-use-threshold=-1

正如我们所见,GHC确实专门进行了呼叫:

...
(f)_$sf1型
  =\x年->
      案例整数Lt x y属于{
        False(错误) ->integerSub x y;
        真的 ->整数相加x y
}

foo公司=\数据集-> 案例ds公司属于{(ww,ww1)->(f)_$sf1网址ww1}

------导入的ID的本地规则--------
“SPEC f@Integer” 对于所有人 $命令$数字(f)$命令$数字=(f)_$sf1型
...

专业化的名称(f_$sf1)重写规则表明GHC已成功自动将重载调用专门化为(f)

有趣的是foo公司及其专业化f_$平方英尺α-等效物GHC达成的条款内联调用并应用工人/包装工相反2,专业化也一样作为工人的角色。

跨模块自动专业化

我们现在讨论了呼叫自动专业化的两个先决条件:

  • 调用必须是可专门化的(即它必须是对重载的调用以已知类型绑定)。
  • 其他优化,如内联,可以删除调用或以其他方式破坏在专门化可以启动之前,调用的专门化不能启动发生。

实际上,对于在重载绑定(正如我们上一个示例中的情况),这些是唯一的前提条件。当重载绑定从另一个模块导入时(通常情况下),还有一些附加的先决条件,我们将现在讨论。

暴露的展开和INLINABLE公司杂注

GHC执行单独编译(与整个程序编译相反),一次编译一个Haskell模块。GHC编译模块时,会生成不仅在目标文件中编译代码,而且接口文件(带有后缀你好) . 接口文件包含有关以下模块的信息:GHC在编译其他模块时可能需要引用,例如名称和模块导出的绑定类型。如果满足某些标准,GHC将在模块的接口文件中包含一个绑定的展开,以便稍后用于跨模块内联或专门化。这样的展开是称为暴露的展开

现在,您可能会有理由怀疑:如果使用展开来实现这些强大功能优化,为什么GHC只公开符合某些标准的展开?为什么?不暴露所有展开?原因是在编制期间,GHC持有的接口每一个内存中程序中的模块。因此,为了保持GHC自己的默认性能和内存使用合理,模块接口需要如下尽可能小,同时仍能生成优化良好的程序。一种方式GHC通过限制包含在接口文件,以便默认情况下只显示小的展开。

这里还有另一个影响跨模块专业化的问题:即使GHC决定公开重载绑定的展开,以及一个可专门化的对该绑定的调用发生在另一个模块中,GHC仍将从未除非获得显式权限,否则自动专门化该调用创造专业化。这种显式权限只能在一个以下方式之一:

让我们通过继续我们的示例来探讨这个事实。移动foo公司,这使得可专门调用(f),到另一个模块食品.hs那个有-折叠式美国门槛设置为-1表示愚弄与之前一样的内嵌:

{-#OPTIONS_GHC-funfolding-use-threshold=-1#-}
模块  哪里

进口 F类

foo::(整数,整数)-> 整数
foo(x,y)=f x y轴

同时从中删除所有内容F.hs公司除了(f),为了更好地衡量:

模块 F类 哪里

传真::(订单a中,号码a)=>->->
x年=
    如果x个<然后
x个+
    其他的
x个-

(f)规模如此之小,我们可能期望GHC在F.hi公司默认情况下为模块接口。如果我们用just编译

温室气体F.hs公司

我们得到目标文件F.o公司和接口文件F.hi公司。我们可以确定GHC是否决定揭露(f)通过查看的内容使用GHC的接口文件--展示我的脸选项:

温室气体 --展示我的脸F.hi公司-全部取消

模块中每个绑定的具体信息列在底部输出。任何外露展开的GHC核心将显示在它们各自的绑定。在这种情况下(f)看起来像这个:

bcb4b04f3cb5e6aa2f776d6226a0930f::(序号a,数字a)=>a->a->a[]

它只包含类型,没有展开!这是因为GHC违约的优化级别-0号机组,的-界面上的泡沫-图形界面-图形标志是启用此选项可防止展开(以及其他内容)包含在和中从模块接口读取。启用优化后重新编译并检查再次显示模块界面:

温室气体 -O(运行)F.hs公司
温室气体 --显示iface你好-dsuppress-all所有

这一次,GHC确实揭露了事态的发展:

152dd20f273a86bea689edd6a298afe6
传真::(订单a、,号码a)=>->->
[...,
   展开以下为: 核心以下为: <香草>
\@
($命令[“很多]:: 订单a)
($数字[“很多]:: 号码a)
(x)[“很多]::a)
(年)[“很多]::a)->
              案例 < @$dOrd x y(顺序x y)属于野生的{
                False(错误) -> - @$数字x y真的 -> + @$数字x y}]

记住,我们仍然没有明确授权GHC专门调用(f)跨模块,因此我们应该期望食品.hs仍然包括对的重载调用(f)。让我们检查一下:

温室气体食品.hs-O(运行) -dno可键入索引 -dsuppress-all所有 -数据抑制-唯一 -ddump-simpl(ddump-impl)

倾倒的堆芯包括:

$世界粮食组织=\网址:ww1->(f)$fOrdInteger(顺序整数)$fNumInteger全球ww1

foo公司=\ds公司-> 案例ds公司属于{(ww,ww1)-> $wfoo网址ww1}

事实上,GHC应用了工人/包装工转型foo公司,但无法专门调用(f)尽管它满足了我们的前面讨论了自动专门化的前提条件。

GHC中有一个警告标志,可以通知我们这种情况:-Wall-missed专业化.编译食品.hs同样,包括此标志:

温室气体食品.hs-O(运行) -强制重新补偿 -Wall-missed专业化

这将输出以下警告:

Foo.hs:警告:[-Wall-missed-specialitions]无法专门化导入的函数“f”可能的修复方法:在“f”上添加INLINABLE杂注

如果我们按照警告所说的那样添加可内联的上的杂注(f),并转储食品.hs,我们会看到那个自动的专业化成功:

$平方英尺
  =\x年->
      案例整数Lt x y属于{
        False(错误) ->integerSub x y;
        真的 ->整数相加x y
}

foo公司=\ds公司-> 案例ds公司属于{(ww,ww1)-> $sf网址ww1}

------导入的ID的本地规则--------
“SPEC/Foo f@整数” 对于所有人 $命令$数字(f)$命令$数字= $平方英尺

卸下可内联的杂注打开(f)而是启用-fspecialize-aggressively专攻有相同的结果。

自动专业化决策图

我们现在已经介绍了自动专业化的所有主要先决条件。收件人总结一下,下面是一个决策图,说明了任意函数调用可以触发自动专门化:

自动专业化决策图

现在,我们完全了解了GHC专业人员是如何、为什么以及何时工作的,我们可以继续讨论其行为导致的实际问题。大多数本系列的下一篇文章将讨论此问题,但之前最后,我想介绍一些我称之为“专业化谱”的东西。

专业化谱

专业化是一种非常有价值的编译器优化,但我已经提到了许多在这篇文章中,过度专业化可能是一件坏事。这就引出了一个问题:我们如何知道自己是否适当地受益来自专业化?此处“适当”的含义取决于特定于应用程序的要求,规定了我们的可执行文件,我们多么关心编译成本,以及我们多么关心关于性能。

例如,如果我们想不惜一切代价实现性能最大化,我们应该确保我们正在生成和使用一组最大化的专门化我们感兴趣的性能指标,忽略了编译成本和可执行文件大小。

基本上,我们的目标是在专业化光谱

专业化谱
性能与代码大小和编译成本的理想图表。

这是我们的搜索空间,在一个轴上有性能,代码大小和另一方面是编译成本。绘制的点表示重要频谱中与应用程序无关的点。这些要点是:

  • 基线:性能最低、成本最低。这一点代表GHC的其启发式将导致较小代码大小和编译成本较低,但可能会导致错过专门化在重大表现中获胜。
  • 理想:作为应用程序的作者,我们可以选择此根据我们的优先事项确定。通常,我们希望此为“高左”。
  • 最大性能:这一点表示最佳的专业化设置,这将比其他任何一组专业化。
  • 最大专业化:此点是生成每一个通过启用可能的专业化-fexpose-所有褶皱-fspecialize-aggressively专攻重要的是,这是始终等同于最大性能!如果我们产生无用的专门化只会带来很少或根本没有的性能改进增加代码大小,我们最终可能会失败的更多代码带来的性能交换CPU缓存。

虚线表示近似的“最佳路径”,表示当我们按降序生成所有专业化时,我们可能会看到结果性能改进。

这个框架清楚地表明,这实际上只是一个优化问题,传统优化问题的所有正常问题都在发挥作用。不幸的是,由于缺乏探索这种光谱的良好工具特别容易让程序员迷失方向,陷入危险、不理想的境地路径如下:

导致编译成本高但性能中等的典型路径。

这样的案例是欺骗性的,让程序员认为他们已经陷入了困境当它们实际上处于表现不佳的局部最优时。幸运的是我们将在本系列的下一篇文章中讨论的工具和技术将非常有用简化专业化谱的优化搜索。

总结

我们对专业化的初步探索到此结束。这就是我们已了解:

  • 对重载函数的调用是通过使用每个类型类约束的函数记录。
  • 专业化从重载的函数,并将对它们的引用替换为对混凝土的引用而不是字典。
  • 专业化的几乎所有好处都来自以下优化它通过将不透明字典参数替换为具体字典来启用其内容可以内联。
  • 只有在特定的条件集下,GHC才会自动专门化呼叫持有。请参见专业化的自动决策图表
  • 这个专业化谱方便吗专业化影响概念化框架程序的编译成本和运行时性能。

在本系列的下一篇文章中,我们将应用所学的所有知识介绍一些示例应用程序,并演示我们拥有的新工具发达国家可以帮助我们实现最佳专业化和绩效。

脚注

  1. 可以认为展开的有效大小作为展开的核心表示中的项数,加上或减去一些折扣应用的取决于GHC正在考虑将展开内联。↩︎

  2. 这暗示着汇流GHC核心和GHC优化器的减少(即优化)适用于它。↩︎

  3. 即使在电缆.项目文件:

    包装*ghc-options:-fexpose-all-unfoldings-fspecialize-aggressively

    一些过载呼叫可能仍然不要搞专业化!如果对重载函数的调用链包括对重载无法由Cabal重新安装的GHC引导库中的函数,例如。基础,其展开未暴露。唯一的方法专门化此类调用是从源代码使用-fexpose-所有褶皱-fspecialize-aggressively专攻,并包括上面的代码段电缆.项目文件。

    此外,某些特定场景可能会导致出现过载调用在优化管道的后期。要专门处理这些呼叫,-扁平专业(需要英式拼写)是必要的,在GHC的Core的末尾运行另一个专业化通行证优化管道。

    此外,即使在上述操作之后,一些重载调用也可能仍然幸存没有-fpolymorphic特异性(需要英式拼写),即已知不健全在…时正在写入。不幸的是,在复杂的应用程序中重载调用仍然是一个很难实现的目标。↩︎

]]>
Haskell与Well-Typed的开发工作 https://well-typed.com/blog/2024/04/haskell-development-job-with-well类型 埃德斯科、亚当、安德烈、本、邓肯 工作 打字精良 http://www.well-typed.com/blog/2024/04/haskell-development-job-with-well-typed 2024年4月9日星期二00:00:00 GMT tl;博士 如果你想和我们一起工作,请尽快发送你的申请。

我们正在寻找一名哈斯克尔专家加入我们在Well-Typed的团队。我们正在寻找一名强大的全面Haskell开发人员,他可以提供帮助我们有各种客户项目(而不是特定的经验在任何一个特定字段中)。这是一个很好的机会对Haskell充满热情并渴望改进和提升的人专业背景下的Haskell。

关于Well-Typed

我们是一支由顶级哈斯克尔专家组成的团队。成立于2008年,我们是第一家公司致力于推广Haskell的主流商业用途。收件人为了实现这一目标,我们通过以下方式帮助使用或迁移到哈斯克尔的公司提供一系列服务,包括咨询、开发、培训、,支持和改进Haskell开发工具。

我们与广泛的客户合作,从小型初创企业到知名企业跨国公司。对于一些公司,我们进行专有的Haskell开发和咨询。对其他人来说,大部分工作涉及开源开发和合作和Haskell社区的其他人一起。我们已经建立了技术卓越,客户满意。

我们公司有很强的工程文化。我们所有的经理和决策制造商本身就是Haskell开发商。我们大多数人都有学者背景和我们不怕将适当的计算机科学应用于客户的问题,尤其是FP和PL研究的成果。

我们是一家自筹资金的公司,因此我们不受制于外部投资者和可以专注于客户、员工和哈斯克尔的利益社区。

关于工作

该角色与单个特定项目或任务无关,是完全远程的,工作时间灵活。

一般来说,Well-Typed的工作可以涵盖任何项目和活动我们作为一家公司参与其中。该工作可能涉及:

  • Haskell应用程序开发
  • 直接与客户合作解决他们的问题
  • 致力于GHC、库和工具
  • 教授Haskell并编写培训材料

我们尽可能安排团队内的任务,以适应人们的偏好和轮换以提供多样性和兴趣。现在你是然而,更可能从事一般Haskell开发,而不是GHC或教学。

关于你

我们理想的候选人对Haskell有很好的了解,无论是来自行业,学术或个人兴趣。熟悉其他语言,低级编程和良好的软件工程实践也很有用。很好组织和管理自己的时间并可靠地满足最后期限的能力非常重要。你也应该有良好的沟通技巧。

你可能拥有计算机科学学士或更高学位,或相关字段,尽管这不是必需的。

其他(可选)额外技能:

  • 熟悉(E)DSL设计,
  • 网络、并发和/或系统编程知识,
  • 应用形式方法的知识和经验,
  • 咨询或经营企业的经验,
  • 有教授Haskell或其他技术主题的经验,
  • GHC工作经验,
  • web编程经验
  • …(你告诉我们!)

优惠详细信息

该报价最初为一年全职,旨在长期条款安排。不需要住在英国。我们也许能根据司法管辖区提供就业或分包你住的地方。年薪范围为6万至10万英镑。

如果您感兴趣,请通过电子邮件申请告诉我们您为什么感兴趣以及为什么您非常适合Well-Typed,并附上你的简历。请说明你可以多久开始。

申请截止日期为2024年4月30日星期二。

]]>
从斯威夫特给哈斯克尔打电话 https://well-typed.com/blog/2024/04/calling-haskell-from-swift 罗德里戈 敏捷的 macos公司 外部函数接口 辅导的 http://www.well-typed.com/blog/2024/04/calling-haskell-from-swift 2024年4月2日星期二00:00:00 GMT 这是关于使用Haskell和Swift/SwiftUI。这篇文章介绍了如何从Swift通过使用类似的外部函数调用约定策略由描述从天堂召唤炼狱:束缚着生锈哈斯克尔需要论证的和结果封送。你可以找到此中的其他博客帖子系列很有趣。

这一系列博客帖子还附有一个github储存库哪里每个提交都与本教程的一个步骤相匹配。如果对任何步骤有疑问,检查匹配的提交以使其更清晰。

此摘要已交叉发布到罗德里戈的博客

介绍

我们将从上一篇文章的结尾处开始——我们已经建立了一个XCode项目其中包括从Haskell模块生成的头对外出口并链接到cabal文件中声明的外部库。我们有已经能够对Swift中的整数调用非常简单的Haskell函数通过Haskell的C对外导出功能和Swift的C互操作性。

这部分涉及调用惯用Haskell函数通常会将用户定义的数据类型作为Swift的输入和输出。此外,这些功能应该透明地提供给Swift,例如Swift调用它们就像调用其他惯用函数一样,由用户定义结构和类。

对于运行示例,以下not-very-interest函数将足以展示我们将用于从Haskell公开此函数的方法到Swift,可以轻松扩展到其他复杂的数据类型和函数。

数据 用户
  = 用户{姓名:: 字符串
,年龄:: 国际
}

生日: 用户 -> 用户
生日用户=用户{年龄=用户年龄+ 1}

Swift一侧应包裹Haskell的生日以下为:

结构用户{
     名称以下为:字符串
     年龄以下为:国际
}

//birthday(user:user(name:“Anton”,age:33))=用户(name:”Anton“,age:34)
函数 生日(用户以下为: 用户)->用户 {
    //调用Haskell函数。。。
}

为了支持此工作流,我们需要一种方法将用户数据类型从哈斯克尔到斯威夫特反之亦然。我们打算序列化(大多数)输入和输出函数的。即使串行化描述可能看起来很复杂,可以使用Template Haskell和Swift实现自动化宏并打包成一个整洁的界面——我在哈斯克尔·斯威夫特

作为初步步骤,我们添加了用户数据类型和生日函数到haskell-framework/src/MyLib.hs和Swift等价物SwiftHaskell/ContentView.swift公司来自哈斯克尔-x-swift-project-steps示例项目。

编组输入和输出

从Swift的角度,整理函数的输入和输出,表示将输入值序列化为字符串,并将输出值作为然后解码为Swift值的字符串。哈斯克尔的观点是二重的。

封送/序列化是一种非常健壮的外语互操作性解决方案。虽然在函数调用时编码和解码的开销很小几乎自动扩展到并支持各种数据跨越语言边界传输,而不会受到编译器实现细节和内存表示不兼容。

我们将使用相同的封送策略从天堂召唤炼狱:在哈斯克尔身上生锈做。简而言之,惯用的Haskell函数由一个低级函数包装从参数缓冲区反序列化Haskell值,并序列化函数结果到调用方提供的缓冲区。更具体地说,

  • 对于原始函数的每个参数,我们都有一个Ptr CChar公司国际–字符串和该字符串的大小(也称为C字符串长度)

  • 对于原始函数的结果,我们有两个附加参数,Ptr CChar公司Ptr Int公司——内存中的一个空缓冲区和一个指向该缓冲区大小的指针,都是由调用方分配的。

  • 对于每个参数,我们将C字符串解析为一个Haskell值,该值用作原始函数的参数。

  • 我们调用原始函数

  • 我们用必修的缓冲区的大小以适应结果(可以小于或大于实际大小)。如果缓冲区足够大,我们将结果写入其中。

  • 从Swift端,我们读取内存中指定的字节数现在包含必修的大小。如果结果是所需尺寸大于缓冲区的大小,我们需要重试具有较大缓冲区的函数调用。

    • 这意味着如果原始缓冲区大小为不够大。一些工程工作可能允许我们重复使用结果,但为了简单起见,我们将坚持从头开始重试。

我们将使用JSON格式作为序列化格式:这个选择是有动机的主要是为了方便,因为Swift可以为数据类型派生JSON实例开箱即用(不产生额外依赖),在Haskell中我们可以使用伊森达到同样的效果。实际上,最好使用一种格式例如CBOR或Borsh,它们是针对紧凑性进行优化的二进制格式序列化性能。

哈斯克尔的观点

扩展用户示例要求用户可解码,可以通过添加到用户宣言:

衍生股票通用
衍生任何类别(ToJSON(ToJSON),来自JSON)

使用适当的扩展并在中导入必要的模块我的图书馆以下为:

{-#LANGUAGE派生策略,DeriveAnyClass#-}

-- ...

进口 全球总部。泛型
进口 数据。埃宋

这个我的外国图书馆模块还必须导入

进口 国外。Ptr公司
进口 国外。可存储的
进口 国外。元帅
进口 数据。埃宋
进口 数据。字节字符串
进口 数据。ByteString.不安全

现在,让我们(国外)导出一个函数生日(_B)包裹起来的生日中的以上haskell-framework/flib/MyForeignLib.hs,使用描述的方法。

首先,函数的类型定义用用户参数,以及缓冲区以写入用户无法使用元组,因为它们不是国外出口申报支持,但直觉是两个参数代表原始参数用户输入,以及后两个参数代表返回的用户

生日(_B): Ptr公司 CChar公司 -> 国际 -> Ptr公司 CChar公司 -> Ptr公司 国际 -> IO(输入输出)()

然后,实现——解码参数,编码结果,写入如果它很合适。

c_birthday cstr clen结果大小_ptr= 

我们改变了(Ptr CChar,国际)配对为字节字符串使用unsafePackCStringLen,并解码用户来自字节字符串使用decodeStrict(解码严格)以下为:

  --(1)解码C字符串
  只是用户<-decodeStrict(解码严格)<$>unsafePackCStringLen(cstr,clen)

我们应用原始生日解码后的函数用户。在我们的示例中,这是一个非常无聊的函数,但实际上这可能是一个复杂的函数我们要公开的惯用Haskell函数。

  --(2)申请生日`
  用户_新建=生日用户

我们对new_user::用户作为一个字节字符串、和使用未安全使用为CStringLen获取指向字节字符串数据及其长度的指针。最后,我们得到结果缓冲区的大小,写下结果返回给给定的内存位置,如果实际大小适合缓冲区,将字节从字节测试复制到给定的缓冲区。

  --(3)编码结果
unsafeUseAsCStringLen(严格$编码user_new)$\(ptr,len)-> 

    --(3.2)结果缓冲区的大小是多少?
size_avail(大小可用)<-查看size_ptr

    --(3.3)将实际大小写入int ptr。
戳尺寸_ptr长度

    --(3.4)如果足够,我们将结果字节复制到给定的结果缓冲区
    如果size_avail(大小可用)<伦恩
       然后 
         --我们需要可用的@len@字节
         --呼叫方必须重试
         返回()
       其他的 
moveBytes结果ptr长度

如果写入必修的大小大于给定的缓冲区,则调用方将重试。

当然,我们必须将其导出为C函数。

国外出口商品分类生日(_B): Ptr公司 CChar公司 -> 国际 -> Ptr公司 CChar公司 -> Ptr公司 国际 -> IO(输入输出)()

这使得生日(_B)生成的动态库中的标头和链接时。

斯威夫特的观点

在Swift中,我们希望能够通过他们的C包装器来自一个在Swift中感觉很地道的包装器。在我们的示例中,这意味着结束对的呼叫生日(_B)在一个新的雨燕生日功能。

ContentView.swift目录视图,我们制造用户JSON可编码/可解码,符合这个可编码的协议:

结构用户以下为: 可编码的 {
    // ...
}

然后,我们实现生日哪一个简单地电话生日(_B)–整个逻辑生日由Haskell方处理功能(回忆一下生日可能非常复杂,以及其他Haskell公开的函数确实会是)。

函数 生日(用户以下为: 用户)->用户 {
    // ...
}

注意:在实现中,必须用执行{…}捕获X{…{但我在本文中省略了它们。你可以看到提交与回购中的Swift函数包装器实现相关这些细节包括在内。

首先,我们使用数据类型(加上其长度),其将用作外部C函数的参数。

 电子控制 =JSON编码器()
 12月 =JSON解码器()

无功功率,无功功率 数据以下为:数据= 尝试电子控制编码(用户)
 data_len(数据_伦) =国际64(数据计数)

然而,斯威夫特数据值将JSON表示为二进制数据,它不能作为指针直接传递给C。为此,我们必须使用带有不安全的可变字节以获得不安全的MutableRawBufferPointer从中退出数据–我们可以传递给C外部函数。带有不安全的可变字节接收使用不安全的MutableRawBufferPointer在其范围内,以及返回闭包返回的值。因此,我们可以返回对用户调用数据我们立即编码:

返回数据带有不安全的可变字节{ (原始Ptr以下为:不安全的MutableRawBufferPointer) 在里面
    //下面是可以使用原始指针的闭包,
    //下面描述的代码
}

我们为C外部函数分配一个缓冲区,以插入调用Haskell函数,并分配内存来存储缓冲区。我们使用带有不安全的临时分配分配一个可以在C外部函数调用中使用。至于带有不安全的可变字节,这个函数还接受闭包并返回闭包返回的值:

//数据缓冲区大小
 buf_size(大小) = 1024048 //1024KB

//存储结果缓冲区长度的size=1缓冲区
返回带非安全临时分配(属于以下为:国际自己以下为: 1) {尺寸_ptr在里面
    //将缓冲区大小存储在此内存位置
尺寸_ptr基本地址?.指针对象=buf_size(大小)

    //为结果分配缓冲区(我们需要将其包装在do{…}catch中,原因如下所述)
     {
        返回带有不安全的临时分配(字节计数以下为:buf_size(大小),对齐以下为:1) {资源_ptr在里面

            //从这里继续。。。
        }
    } 抓住 //我们会在适当的时候继续。。。
}

我们现在深深地嵌套在3个闭包中:一个将指针绑定到参数的数据,另一个是指向缓冲区大小的指针,而另一个则是结果缓冲区指针。这意味着我们现在可以调用C外部函数包装Haskell函数:

生日(_B)(原始Ptr基本地址,data_len(数据_伦),资源_ptr基本地址,尺寸_ptr基本地址)

回顾Haskell端将更新尺寸_ptr序列化编码结果所需的大小,我们需要检查所需的大小超过了我们分配的缓冲区,否则将读取数据:

如果  所需大小 =尺寸_ptr基本地址?.指针对象{
    如果所需大小>buf_size(大小){
        //需要重试
        HsFFI错误所需尺寸(所需大小)
    }
}

返回12月解码(用户自己,以下为:数据(字节无副本以下为:资源_ptr基本地址!,
计数以下为:尺寸_ptr基本地址?.指针对象?? 0,解除定位器以下为: 没有人))

哪里HsFFI错误自定义错误定义为

枚举HsFFI错误以下为: 错误 {
    案例必需的大小(国际)
}

我们现在必须填写抓住块重试外部函数调用大小合适的缓冲区:

} 抓住HsFFI错误必需的大小( 所需大小) {
    返回带有不安全的临时分配(字节计数以下为:所需大小,对齐以下为:1)
    {资源_ptr在里面
尺寸_ptr基本地址?.指针对象=所需大小
生日(_B)(原始Ptr基本地址,data_len(数据_伦),资源_ptr基本地址,尺寸_ptr基本地址)

        返回12月解码(用户自己,以下为:数据(字节无副本以下为:资源处理程序基本地址!,
计数以下为:尺寸_ptr基本地址?.指针对象?? 0,解除定位器以下为: 没有人))
    }
}

从Haskell调用函数似乎需要很多工作!然而,尽管这是大量代码,而不是全部代码:我们只需序列化参数,为结果分配缓冲区,并将结果反序列化到其中。在最坏的情况下,如果序列化结果不适合(序列化数据超过100万个字符),那么我们天真地计算函数a第二次(通过缓存避免此工作应该不会太复杂结果并以某种方式使用新缓冲区恢复序列化)。此外,在获取要发送的原始指针方面存在很多官僚作风前往哈斯克尔地区——好消息是,所有这些都可以自动取消支持使用Template Haskell和Swift宏自动生成代码。

展开以获得完整的功能
函数 生日 (用户 以下为: 用户)->用户 {
     电子控制 =JSON编码器()
     12月 =JSON解码器()
     {
        无功功率,无功功率 数据 以下为:数据= 尝试电子控制编码(用户)
         data_len(数据_伦) =国际64(数据计数)
        返回 尝试数据带有不安全的可变字节{ (原始Ptr以下为:不安全的MutableRawBufferPointer) 在里面

            //为结果分配缓冲区
             buf_size(大小) = 1024000

            返回 尝试带有不安全的临时分配(属于以下为:国际自己,容量以下为: 1) {尺寸_ptr在里面
尺寸_ptr基本地址?.指针对象=buf_size(大小)

                 {
                    返回 尝试带有不安全的临时分配(字节计数以下为:buf_size(大小),对齐以下为: 1) {
资源处理程序在里面

生日(_B)(原始Ptr基本地址,data_len(数据_伦),资源_ptr基本地址,尺寸_ptr基本地址)

                        如果  所需大小 =尺寸_ptr基本地址?.指针对象{
                            如果所需大小>buf_size(大小){
                                HsFFI错误必需的大小(必需的大小)
                            }
                        }
                        返回 尝试12月解码(用户自己,以下为:数据(字节无副本以下为:资源_ptr基本地址!,计数以下为:尺寸_ptr基本地址?.指针对象?? 0,解除定位器以下为: 没有人))
                    }
                } 抓住HsFFI错误必需的大小( 所需大小) {
打印(“使用所需大小重试:\(required_size)“)
                    返回 尝试带有不安全的临时分配(字节计数以下为:所需大小,对齐以下为:
 1) {资源_ptr在里面
尺寸_ptr基本地址?.尖头物=必需的大小

生日(_B)(原始Ptr基本地址,data_len(数据_伦),资源_ptr基本地址,尺寸_ptr基本地址)

                        返回 尝试12月解码(用户自己,以下为:数据(字节无副本以下为:资源_ptr基本地址!,计数以下为:尺寸_ptr基本地址?.指针对象?? 0,解除定位器以下为: 没有人))
                    }
                }
            }
        }
    } 抓住 {
打印(“解码JSON时可能出错:\(错误)“)
        返回用户(名称以下为: "",年龄以下为: 0)
    }
}

我们可以通过替换内容视图具有:

结构内容视图以下为: 查看 {
    无功功率,无功功率 身体以下为:一些视图{
V堆栈{
             用户 =生日(用户以下为:用户(名称以下为: “埃莉”,年龄以下为: 24))
文本(“出生后,\(user.name)是:\(user.age)!")
        }
        衬垫()
    }
}

你应该看到:

Swift应用程序通过惯用Swift包装器显示调用惯用Haskell函数的结果

边界上的元编程

我想快速预览一下使用编译时间可以实现什么代码生成功能(Haskell中的Template Haskel,Swift中的Swift Macros)。此外部函数代码生成API由哈斯克尔·斯威夫特项目,即快速飞行Haskell库和哈斯克尔·菲伊Swift套餐(因为它不在在本教程的范围内,我不会详细介绍编译时间代码生成代码是有效的,但使用这些代码提供的API库)。

有了这些顶级的外国交互设施,再加上构建工具由提供哈斯克尔·斯威夫特,一个可以轻松引导和开发混合Haskell和Swift的程序。

让我们考虑定义惯用语的相同示例生日::用户->用户函数,并希望能够从Swift将其调用为生日(用户:user)->用户

哈斯克尔的观点

要暴露生日函数,我们只需使用国外出口Swift模板Haskell函数。整个模块可以如下所示:

{-#LANGUAGE模板Haskell#-}
模块 我的图书馆 哪里

--。。。
进口 斯威夫特。外国金融机构

数据 用户
 = 用户{姓名:: 字符串
,年龄:: 国际
}
        衍生股票通用
        衍生任何类别来自JSON
        衍生任何类别ToJSON(ToJSON)

生日: 用户 -> 用户
生日用户{年龄=x、 姓名=是}= 用户{年龄=x个+1,名称=是}

$(foreignExportSwift生日)

关键位是最后一位国外出口Swift将公开C函数的调用使用我们上面概述的基于编组的调用约定。

斯威夫特的观点

在Swift方面,我们想使用双@国外进口Swift宏,其中生成Swift函数包装器,该包装器反过来调用公开的C函数Haskell采用上述编组策略。Swift文件可以看起来像:

进口 哈斯克尔FFI

结构用户以下为: 可编码的 {
     名称以下为:字符串
     年龄以下为:国际
}

@国外进口哈斯克尔
函数 生日(cconv公司以下为: Hs调用JSON, 用户以下为: 用户)->用户 {短截线() }

哪里生日可以称为:

生日(用户以下为:用户(名称以下为: “皮埃尔”,年龄以下为: 55))

评论

跨外语边界的编组策略非常稳健并且仍然具有表演性,非常适合混合语言我们想要稳健开发的应用程序。

尽管要有力地通过外语需要封送在随后的一篇帖子中,我还将探讨边界,从斯威夫特给哈斯克尔打电话而是将Haskell值的内存表示强制转换为Swift第一,这主要是一种(非常不安全的)不完全是强烈的好奇心,但是这将给我一个在Haskell中写一些低级细节的借口!

在另一篇帖子中,我还打算介绍hxs公司引导工具Haskell x Swift项目和使其更容易导出的库Haskell函数并从Swift导入它们。

这个哈斯克尔-x-swift-project-stepsgit存储库有一个与本指南步骤相匹配的提交,因此如果有不清楚在检查提交时,您可以让代码自己说话。

]]>
哈斯克尔展开第22集:折叠融合 https://well-typed.com/blog/2024/03/haskell-unfolder-episode-22-foldr-build-fusion 埃德斯科·安德烈斯 会谈 社区 训练 辅导的 youtube(youtube) 展开器 http://www.well-typed.com/blog/2024/03/haskell-unfolder-episode-22-foldr-build-fusion 2024年3月20日星期三00:00:00 GMT 今天,2024-03-20,UTC时间1930(太平洋夏令时下午12:30,美国东部时间下午3:30,格林尼治时间下午7:30,欧洲中部时间20:30,…)我们正在播放《哈斯克尔展开》第22集的流媒体在YouTube上直播

哈斯克尔展开第22集:折叠融合

在组合多个列表处理函数时,GHC使用了一种称为fold-builded fusion的优化。Fusion以这样一种方式组合函数,任何中间列表通常都可以完全消除。在这一集中,我们将看看这种优化是如何工作的,以及它是如何在GHC中实现的:不是作为内置的编译器魔法,而是通过用户定义的重写规则。

关于Haskell展开

Haskell Unfolder是一个YouTube系列,讲述了Haskel主持的所有事情埃德斯科·德·弗里斯安德烈斯·洛赫,大约每两集出现一集周。所有的剧集都是现场直播的,我们尽量回应观众问题。所有剧集也可以在之后录制。

我们有一个GitHub存储库带有剧集中的代码示例。

我们有一个公共谷歌日历(也可作为ICal公司)列出计划的时间表。

]]>
GHC活动报告:2023年12月至2024年2月 https://well-typed.com/blog/2024/03/ghc-activities-report-december-2023-february-2024 亚当、安德烈亚克、本、芬利、汉纳斯、马修、罗德里戈、萨姆、祖宾 打字精良 温室气体 社区 ghc活动报告 http://www.well-typed.com/blog/2024/03/ghc-activities-report-december-2023-february-2024 2024年3月8日,星期五00:00:00 GMT 这是我们GHC活动报告的第二十二版,其中描述了我们正在Well-Typed进行的GHC、Cabal和相关项目的工作。当前版本大致涵盖2023年12月至2024年2月的几个月。您可以在ghc-activities-report标签

非常感谢我们的赞助商,他们使这项工作成为可能:安督瑞尔,哈苏拉Juspay公司此外,我们感谢水银为大型代码库上的开发人员工具改进性能的具体工作提供资金,并发送至主权科技基金供资在Cabal上工作

然而,我们需要更多的赞助支持团队!如果贵公司能够出资支持这项工作,请阅读有关如何提供帮助的信息联系

当然,Haskell工具是一项大型社区工作,Well-Typed的捐款只是其中的一小部分。本报告的目的不是提供详尽的图片全部的GHC工作正在进行中这里省略了目前正在开发的一些很棒的功能,原因很简单目前我们都没有参与其中。此外,我们提到的仍然是许多人的工作。在许多情况下,我们只是在帮助解决最后几个问题整合步骤。我们非常感谢为GHC做出贡献的每一个人!

团队

Well-Typed的GHC团队目前包括本·加马里,安德烈亚斯·克莱宾格,马修·皮克林,祖宾·达加尔,萨姆·德比郡罗德里戈·梅斯基塔,带有汉内斯·西本汉德尔1月加入团队芬利·麦克尔瓦恩转移到另一个客户端项目。此外,Well-Typed中的许多其他公司也在更偶尔地为GHC做出贡献。

发布

祖宾被释放通用条款9.6.41月份通用条款9.8.22月。我们现在正在努力争取在今年晚些时候发布GHC 9.10。结账这个GHC状态页对于有关发布计划的更多信息。

擦除分析

Matthew和Zubin最近实现了一种新的评测模式,即擦除评测可以深入了解什么时候分配特定对象。这对于诊断长时间运行的程序中的内存泄漏非常有用。

查看我们的介绍eras分析的博客帖子有关此新功能的更多信息,以及我们如何使用这种新的评测模式用于诊断GHCi中的内存泄漏。Matthew还使用eras分析来诊断太空泄漏在GHC的简化程序中(11914).

eras分析和ghc调试特别适用于分析内存泄漏,所以Zubin一直在对ghc调试(MR 32型),包括改进它处理已分析可执行文件的方式(MR 35型,MR 36型).

GHC内部构件的新家

GHC的基础图书馆长期以来有双重目的:一方面是面向用户的标准库界面,但同时它包含许多用于实现标准库的内部详细信息。这种双重目的给实现者和用户都带来了问题内部接口与预期的长稳定接口自由穿插用于一般消费。更糟糕的是基础经常提供对于哪些界面属于哪一类,用户几乎没有什么指导。

今年早些时候,核心图书馆委员会和GHC团队同意了改善这种情况的途径通过拆分基础三个库:基础,ghc内部,ghc实验。我们希望这种方法能够解决同时出现几个问题:

  • 基础为用户提供了一组清晰的稳定界面,由核心图书馆委员会监督。
  • ghc实验为新语言和库功能的开发人员提供专门用于迭代他们的设计,同时仍然允许使用用户愿意接受稍低程度的稳定性。
  • ghc内部为内部提供了一个家不打算由用户消费的实现细节,并且可能从一个版本到另一个版本发生变化。

Ben一直致力于通过分离定义来实现这种分离属于ghc内部包装(11400).这种分裂导致了整个生态系统的许多改进,包括Haddock改进(参见Haddock问题1629,1630)到编译器错误文件(#24436)和实施清理(#24472).

异常回溯

本一直在努力实现他期待已久的梦想例外回溯建议(8869)遵循广泛的与核心图书馆委员会的讨论预计这将成为GHC 9.10的一部分,并将是朝着异常诊断对用户来说更容易。

GHC指导委员会和GHC2024型

Adam现在担任GHC转向委员会,以下是约阿希姆·布雷特纳下台经过多年的专注服务在这个角色上。他的第一项主要任务是秘书一直在寻找新的志愿者加入委员会。如果你有兴趣,请阅读更多内容并联系

委员会更新了推荐的语言扩展集合引入GHC2024型GHC 9.10将随附GHC2024型可用(12084),但尚不清楚何时将成为默认值(请参阅ghc提案先生632).

STM正确性和性能

Andreas一直在使用STM诊断进度和性能问题用户报告STM饥饿问题(#24142). 特别地:

  • STM事务性能随着TVar公司涉及的(#24410),因为当前实现使用链接列表来跟踪所有的TVar公司事务使用的。Ben探索了一种改进方法这种情况下,使用哈希映射进行这些查找(12030).

  • 具有大量TVar公司s可能表现不佳(#24427)由于每次Haskell线程返回到调度程序。此检查可识别潜在的非终止STM事务通过对照内存验证STM内存的事务视图当前状态。虽然这项检查非常有用,但执行起来有些费钱,并且在当前的实施中,当多个验证并行进行。最好的解决方案可能是因为这个问题是要减少执行验证的频率,特别是在长时间正在运行事务。

  • 在病理情况下,两个并行运行的事务可能无法进度(#24446),即使所有事务都是只读的。这应该是通过返工可以解决TVar公司s在验证过程中被锁定。

不幸的是,解决这些问题需要进一步的工作。

专业化和后期插件

芬利一直在探索技术,以使诊断问题更容易大型应用程序的专业化,例如由于超载呼叫未被专门处理。解决此类问题的一个解决方法是暴露所有展开并使用攻击性专业化,但这往往会反而会导致编译时性能不佳。

出于这些调查的动机,他添加了“后期插件”,这些插件是在添加后期成本中心后,核心管道结束(11765). 这允许插件,用于分析和修改编译为STG的Core,无需更改在接口文件中结束。

卡瓦尔

马修、罗德里戈和萨姆一直致力于解决长期存在的建筑问题和中的维护问题卡瓦尔图书馆和电缆安装建造工具。这项工作得到了主权科技基金如我们的上一篇博客文章

一些变化包括:

  • 设计和实施新的建筑类型:挂钩功能以提供反对的途径build-type:自定义根据社区反馈,Sam反复强调了设计,特别关注预构建规则,设计灵感来源于Cloud Haskell公司,使用静态指针。请参阅详细HF技术建议书以深入解释该设计及其优点。这个目前正在准备审查实施情况(公关9551).

  • 将隐式全局状态从卡瓦尔库,允许它以工作目录作为参数,而不是使用工作当前进程的目录(公关9718).这是为了允许直接调用卡瓦尔要构建的库并发设置中的包。

  • 为私有依赖项进行设计和原型实现(发行号4035),允许包来表示它们不公开来自API中的依赖项。这为构建构建提供了更大的灵活性计划,可能使库版本升级更容易,并允许测试和基准来比较同一库的不同版本。

  • 使测试套件更加健壮,包括对其进行重构以在单独的临时目录,使其不受外部用户系统的配置(9717年4月).

  • 允许使用Haskell Program Coverage(HPC)信息进行每个组件的构建(公关9464).

  • 重构以消除长期存在的代码重复构建组件逻辑中的错误源(公关9602)和全球支持(公关9673).

  • 使用install命令修复几个长期存在的错误,通常忽略CLI标志(公关9697).

  • 稳健地处理从源代码编译的相同GHC版本多次(公关9618),因为GHC版本号不足以确保ABI兼容性。

  • 更多错误修复和重构,以提高可维护性和代码库的健壮性(例如公关9524 公关9554).

GHC错误修复

  • Ben使用ThreadSanitizer调查了内存排序问题并修复了许多数据竞赛(9372,11795,11768).

  • Ben解决了GHC使用C导致的线程安全问题斯特雷罗公用事业(#24344).

  • Sam修复了涉及记录字段的隐藏错误消息中的9.8回归没有字段选择器(11981).

  • Hannes修正了Haddock如何解析限定引用的9.8回归(11920).

  • Zubin修复了GHC报告错误消息不佳的回归存在包括hs-boot文件的模块周期(11718,11792).

  • Zubin使用不正确的成本中心修复了跨模块模块断点(11892).

  • Sam和Andreas修复了fused-multiply-add处理中的各种错误GHC 9.8.1中添加的首字母(11587,11893,11902,11987).

  • Ben修复了在32位上实现唯一生成时的一个微妙错误平台(11802).

  • Andreas修复了引入的C外部函数接口中的一个错误通过使用子单词大小的参数(11989).

  • 祖宾集合-数字编程使用评测编译C++源代码时(11871).

  • Matthew修复了处理来源信息表条目时出现的一个离群错误(11873).

  • 祖宾修复了一个生成ghcup-metadata的错误(11791).

  • 祖宾更新了用户指南,以考虑到无限制超载标签GHC提案,降落在GHC 9.6(11774).

  • Hannes修复了在包含以下内容的文件路径中安装GHC时产生的错误Windows上的空间(11938).

构建系统、CI和分销改进

  • Ben为GHC 9.10进行了许多子模块碰撞准备释放。

  • Rodrigo允许配置脚本使用自动配置文件2.72 (11942).

  • Matthew修复了配置中的一个错误hsc2小时建造GHC时,这是链接器错误的来源(#24050,11384).

  • Matthew更新了CI图像,特别关注改进在CI上测试LLVM后端(#24369,11976).

  • Matthew确保文档建立在CI中的更多配置之上(例如阿尔卑斯山、岩石、Windows、达尔文)(12134).

  • Ben将GHC改编为LLVM的新通行证管理器CLI(8999).

]]>
哈斯克尔揭秘第21集:没有参考的测试 https://well-typed.com/blog/2024/03/haskell-unfolder-episode-21-testing-无参考 埃德斯科·安德烈斯 会谈 社区 训练 辅导的 youtube(youtube) 展开器 http://www.well-typed.com/blog/2024/03/haskell-unfolder-episode-21-testing,无参考 2024年3月6日星期三00:00:00 GMT 今天,2024-03-06,UTC时间1930(太平洋标准时间上午11:30,美国东部时间下午2:30,格林尼治时间下午7:30,欧洲中部时间20:30,…)我们正在播放《哈斯克尔展开》第21集在YouTube上直播

哈斯克尔揭秘第21集:没有参考的测试

测试软件时,最好的情况是我们有一个参考实现进行比较。然而,通常这样的引用是不可用的,这就提出了一个问题:如果我们无法验证函数准确计算的内容,那么如何测试该函数。在本集中,我们将考虑如何定义属性来验证我们在第20集中讨论的Dijkstra最短路径算法的实现;您可能希望先看这一集,但这不是必需的:为了测试它,我们主要将算法视为一个黑盒。

关于Haskell展开器

Haskell Unfolder是一个关于Haskell主持的所有事情的YouTube系列节目埃德斯科·德·弗里斯安德烈斯·洛赫,大约每两集出现一集周。所有的剧集都是现场直播的,我们尽量回应观众问题。所有剧集也可以在之后录制。

我们有一个GitHub存储库带有剧集中的代码示例。

我们有一个公共谷歌日历(也可作为ICal公司)列出计划的时间表。

]]>