F# 与 OCaml:堆栈溢出

2024-01-09

我最近发现了一个关于适合 Python 程序员的 F# http://combiol.org/fs/FSUG_FS4PPv2.pptx,看完之后,我决定自己实现一个“蚂蚁谜题”的解决方案。

有一只蚂蚁可以在平面网格上走动。蚂蚁一次可以向左、向右、向上或向下移动一格。也就是说,蚂蚁可以从单元格 (x, y) 前往单元格 (x+1, y)、(x-1, y)、(x, y+1) 和 (x, y-1)。 x 和 y 坐标的数字之和大于 25 的点是蚂蚁无法到达的。例如,点(59,79)是不可访问的,因为5 + 9 + 7 + 9 = 30,大于25。问题是:如果从(1000, 1000)开始,蚂蚁可以访问多少个点,包括 (1000, 1000) 本身?

我用 30 行代码实现了我的解决方案首先是 OCaml http://pastebin.com/EzMt466b,并尝试了一下:

$ ocamlopt -unsafe -rectypes -inline 1000 -o puzzle ant.ml
$ time ./puzzle
Points: 148848

real    0m0.143s
user    0m0.127s
sys     0m0.013s

好吧,我的结果和leonardo 的 D 和 C++ 实现 http://leonardo-m.livejournal.com/。与 Leonardo 的 C++ 实现相比,OCaml 版本的运行速度大约比 C++ 慢 2 倍。鉴于 Leonardo 使用队列来删除递归,这没关系。

I then 将代码翻译为 F# http://pastebin.com/0Vx5GTBQ...这就是我得到的:

Thanassis@HOME /g/Tmp/ant.fsharp
$ /g/Program\ Files/FSharp-2.0.0.0/bin/fsc.exe ant.fs
Microsoft (R) F# 2.0 Compiler build 2.0.0.0
Copyright (c) Microsoft Corporation. All Rights Reserved.

Thanassis@HOME /g/Tmp/ant.fsharp
$ ./ant.exe

Process is terminated due to StackOverflowException.
Quit

Thanassis@HOME /g/Tmp/ant.fsharp
$ /g/Program\ Files/Microsoft\ F#/v4.0/Fsc.exe ant.fs
Microsoft (R) F# 2.0 Compiler build 4.0.30319.1
Copyright (c) Microsoft Corporation. All Rights Reserved.

Thanassis@HOME /g/Tmp/ant.fsharp
$ ./ant.exe

Process is terminated due to StackOverflowException

堆栈溢出...我的机器上有两个版本的 F#... 出于好奇,我然后获取生成的二进制文件(ant.exe)并在 Arch Linux/Mono 下运行它:

$ mono -V | head -1
Mono JIT compiler version 2.10.5 (tarball Fri Sep  9 06:34:36 UTC 2011)

$ time mono ./ant.exe
Points: 148848

real    1m24.298s
user    0m0.567s
sys     0m0.027s

令人惊讶的是,它在 Mono 2.10.5 下运行(即没有堆栈溢出) - 但需要 84 秒,即比 OCaml 慢 587 倍 - 哎呀。

所以这个程序...

  • 在 OCaml 下运行良好
  • 在 .NET/F# 下根本不起作用
  • 在 Mono/F# 下可以工作,但速度非常慢。

Why?

EDIT:怪异仍在继续 - 使用“--optimize+ --checked-”使问题消失,但仅限于 ArchLinux/Mono 下;在Windows XP和Windows 7/64位下,即使是优化版本的二进制堆栈也会溢出。

最终编辑:我自己找到了答案 - 见下文。


执行摘要:

  • 我编写了一个算法的简单实现......它不是尾递归的。
  • 我在Linux下用OCaml编译了它。
  • 效果很好,0.14秒就完成了。

然后是时候移植到 F# 了。

  • 我将代码(直接翻译)翻译为F#。
  • 我在 Windows 下编译并运行它 - 我遇到了堆栈溢出。
  • 我在 Linux 下获取了二进制文件,并在 Mono 下运行它。
  • 它有效,但运行速度非常慢(84 秒)。

然后我发布到 Stack Overflow - 但有些人决定关闭这个问题(叹气)。

  • 我尝试使用 --optimize+ --checked- 进行编译
  • Windows 下二进制仍然堆栈溢出...
  • ...但在 Linux/Mono 下运行良好(并在 0.5 秒内完成)。

是时候检查堆栈大小了:在 Windows 下,另一篇SO帖子指出它默认设置为1MB https://stackoverflow.com/questions/823724/stack-capacity-in-c。在 Linux 下,“uname -s”和测试程序的编译 http://www.linuxquestions.org/questions/linux-newbie-8/default-stack-size-on-linux-glibc-pthreads-358438/清楚地显示它是8MB。

这解释了为什么该程序在 Linux 下运行而不是在 Windows 下运行(该程序使用了超过 1MB 的堆栈)。它没有解释为什么优化版本在 Mono 下比非优化版本运行得更好:0.5 秒 vs 84 秒(尽管 --optimize+ 似乎是默认设置的,请参阅 Keith 的评论“Expert F#”)提炼)。可能与 Mono 的垃圾收集器有关,它在第一个版本中不知何故被推向了极端。

Linux/OCaml 和 Linux/Mono/F# 执行时间之间的差异(0.14 vs 0.5)是因为我测量它的简单方法:“time ./binary ...”也测量启动时间,这对于 Mono 来说很重要/.NET(嗯,对于这个简单的小问题很重要)。

无论如何,为了一劳永逸地解决这个问题,我写了一个尾递归版本 http://pastebin.com/jZy8jKRL- 函数末尾的递归调用被转换为循环(因此,不需要使用堆栈 - 至少在理论上)。

新版本在Windows下也运行良好,并在0.5秒内完成。

所以,这个故事的寓意是:

  • 请注意堆栈的使用情况,尤其是当您使用大量堆栈并在 Windows 下运行时。使用使用 /STACK 选项进行编辑 http://msdn.microsoft.com/en-us/library/d25ddyfc(v=vs.80).aspx要将二进制文件设置为更大的堆栈大小,或者更好的是,以不依赖于使用太多堆栈的方式编写代码。
  • OCaml 在尾递归消除方面可能比 F# 更好 - 或者它的垃圾收集器在这个特定问题上做得更好。
  • 不要对...粗鲁的人关闭你的 Stack Overflow 问题感到绝望,好人最终会抵消它们 - 如果问题真的很好:-)

附: Jon Harrop 博士的一些补充意见:

...你很幸运 OCaml 也没有溢出。 您已经发现实际堆栈大小因平台而异。 同一问题的另一个方面是不同的语言实现 以不同的速率占用堆栈空间并具有不同的性能 存在深堆栈时的特征。 OCaml、Mono 和 .NET 都使用不同的数据表示和 GC 算法,从而影响 这些结果... (a) OCaml 使用标记整数来区分指针, 给出紧凑的堆栈帧,并将遍历堆栈上的所有内容 寻找指点。标签基本上传达了足够的信息 让 OCaml 运行时能够遍历堆 (b) Mono 处理单词 在堆栈上保守地作为指针:如果作为指针,一个单词将指向 到堆分配的块中,则该块被认为是可访问的。 (c) 我不知道 .NET 的算法,但如果它吃掉堆栈我不会感到惊讶 space 更快,并且仍然遍历堆栈上的每个单词(当然 如果不相关的线程有一个 深栈!)...此外,您使用堆分配的元组意味着您将 快速填充苗圃一代(例如 gen0),因此, 导致 GC 经常遍历这些深栈......

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

F# 与 OCaml:堆栈溢出 的相关文章

  • LLVM 尾调用优化

    以下是我对事情的理解 当函数 f 调用自身是其最后一个动作时 它是尾递归的 通过形成循环而不是再次调用函数 可以显着优化尾递归 函数的参数已就地更新 并且函数体再次运行 这称为递归尾调用优化 LLVM 在使用 fastcc GHC 或 Hi
  • F# 中序列的递归函数

    这是一个相当微不足道的问题 但快速的谷歌搜索并没有给我答案 为序列编写递归函数的标准方法是什么 对于列表 您可以使用空列表和头 尾模式进行模式匹配 序列的等效项是什么 没有标准的方法可以做到这一点 因为您很少为序列编写递归函数 您应该查看各
  • OCaml 中的线性类型

    Rust http www rust lang org 有一个线性类型系统 有什么 好的 方法可以在 OCaml 中模拟这个吗 例如 当使用 ocaml lua 时 我想确保仅当 Lua 处于特定状态 堆栈顶部的表等 时才调用某些函数 Ed
  • 如何判断 F# 函数是否是纯函数?

    假设我有这两个 F 函数 let sq x x x let tm DateTime Now 显然 sq 是纯的 因为它对于给定的输入总是返回相同的值 而 tm 是不纯的 因为每次调用它时都会返回不同的值 一般来说 有没有一种方法可以确定 F
  • 如何在 FsCheck 中注册任意实例并让 xUnit 使用它?

    我有一个类型Average有一个字段count这是积极的int64 and a double字段称为sum 我做了一个任意的生成有效实例的操作 let AverageGen Gen map2 fun s c gt Average float
  • OCaml 3.12 中的一流模块:它们将使哪些事情变得更容易(或可能)?

    我听说 OCaml 3 12 中即将推出 一流模块 他们将提供什么优势 哪些孩子的事情会变得更容易 他们试图解决什么问题 一个简单的例子就足够了 这只是一个可能的应用程序 但一流的模块可以轻松地对存在类型进行编码 基本上是一个模块打包存在类
  • 使用 ocaml List.fold_left 列表中的最后一个元素

    我可以通过以下代码找到列表的最后一个元素 let last xs a list a let rec aux xs prev match xs with gt prev x ys gt aux ys x in match xs with gt
  • 通用高阶函数

    当我将泛型函数作为本地值传递时 但在作为参数传递时却不能使用具有不同类型参数的泛型函数时 是否有原因 例如 let f id let g x y f x f y g 1 2 工作正常 但如果我尝试将函数作为参数传递 let g f x y
  • `ImmutableSortedSet` 和 fsharp `Set` 有什么区别?

    BCL引入了一组Immutable Collections http blogs msdn com b bclteam archive 2012 12 18 preview of immutable collections released
  • 删除重载、递归溢出

    嘿伙计们 我写了一个快速测试 我想删除调用deleteMe 然后它会自行删除 这样做的目的是让我可以正常删除由lib分配的obj 我不希望因 crt 或 w e 导致任何崩溃 通过删除这个 我得到了一个堆栈溢出 没有它 msvc 说我泄漏了
  • 如何在Scala中实现尾递归快速排序

    我写了一个递归版本 def quickSort T xs List T p T T gt Boolean List T xs match case Nil gt Nil case gt val x xs head val left righ
  • OCaml 2 和 3 之间的差异

    我有兴趣学习这门语言 但似乎有关该主题的教程和书籍很少 我只找到一本关于这个主题的合适的书 用 Objective Caml 这绝对是完美的 但问题是它是基于 2 04 版本的 所以我唯一关心的是使用这本书 对于 OCaml 3 x 是否会
  • 什么是错误“类型实例化涉及 byref 类型。” F# 中的解决方法是什么

    我有一些代码包装 TA Lib 很多包装器非常相似 let sma timePeriod int data float let mutable outStartIndex 0 let mutable outNbElement 0 let m
  • OCaml 作为 C 库,hello world 示例

    我希望通过 C 调用 OCaml 代码 方法是将 OCaml 编译为包含 C 接口的静态或共享库 这一页 https caml inria fr pub docs manual ocaml intfc html似乎解释了如何为 OCaml
  • 使用 SqlBulkCopy 和 F# 在 SQL 中导出矩阵

    我想将大量数据从 F 传输到 SQL 表 基本上我的 F 代码创建了一个三列矩阵 UserID ProductID and price 和N行 我想将其 复制 粘贴 到数据库中 我尝试了多种选择 但最终 从 F 传输数据非常慢 10000
  • Ocaml 模块和包的区别

    我基本上是在尝试遵循这篇文章中的 stackoverflow 答案 OCaml 中 HttpRequest 的最佳模块是什么 https stackoverflow com questions 14134116 what is the be
  • 如何使用 FLinq 在 F# 中进行外连接?

    问题几乎说明了一切 我有一个如下形式的大 flinq 查询 for alias1 in table1 do for alias2 in table2 do if alias1 Id alias2 foreignId 使用这种形式 如何在这两
  • 我应该在 Prolog 和一般情况下避免尾递归吗?

    我正在阅读 立即学习 Prolog 在线书籍 以获取乐趣 我正在尝试编写一个谓词 该谓词遍历列表的每个成员并向其添加一个 使用累加器 我已经在没有尾递归的情况下轻松完成了 addone addone X Xs Y Ys Y is X 1 a
  • 使用 leftOuterJoin,不需要 .DefaultIfEmpty()

    的文档leftOuterJoin MSDN 上的查询表达式 http msdn microsoft com en us library hh225374 aspx通过样本反复暗示 当使用leftOuterJoin on into 你仍然必须
  • gfortran 支持尾调用消除吗?

    我编写了这个小程序来测试 gfortran 是否执行尾调用消除 program tailrec implicit none print tailrecsum 5 0 contains recursive function tailrecsu

随机推荐

  • pandas 将日期时间列转换为时间戳

    我是熊猫初学者 我的数据框第一列是日期时间 例如 2016 年 9 月 19 日 10 30 00 并且许多记录都喜欢它 我正在尝试将此列转换为时间戳并将其写入另一个数据帧 我正在尝试一步完成 我正在尝试用 python 3 编写 impo
  • 设置 Apache CouchDB 屏幕在容器重新启动时重新出现

    我使用官方 Docker 镜像运行 CouchDB v2 3 我已使用 Fauxton 将数据库配置为单节点 data 目录挂载到本地目录 当我重新启动容器时 数据库仍然存在 所以卷绑定按预期工作 现在 每次我重新启动容器并导航到 设置 选
  • Spark Streaming + Kafka:SparkException:无法找到 Set 的领导者偏移量

    我正在尝试设置 Spark Streaming 以从 Kafka 队列获取消息 我收到以下错误 py4j protocol Py4JJavaError An error occurred while calling o30 createDi
  • 库中存储库的 NoSuchBeanDefinitionException

    我创建了一个用于在多个 Spring Boot 应用程序上共享代码的库 该库包含一个 Repository 类RequestRepository 将库添加到 Spring Boot 项目后 编译并成功运行单元测试 Library Reque
  • YSlow 规则“不要在 HTML 中缩放图像”背后的基本原理是什么

    我在以下地方遇到过这个规则YSlow http developer yahoo com performance rules html no scale为了提高性能 表示不应在 HTML 中调整图像大小 他们没有提到这条规则的任何具体原因 有
  • 为什么OpenGL(IOS)中有.pvr文件

    我正在 IOS 中使用 OpenGL 制作应用程序 使用 PVR 纹理来制作 3D 效果 我无法理解 pvr 文件 所以请朋友们了解一下 pvr 文件以及它在 OpenGL 中的重要性以及我该如何制作它 PVR 文件是各种纹理格式的容器 例
  • 从 condaenvironment.yaml 安装时的依赖项的 pip 依赖项

    我正在尝试为项目的用户创建一个 condaenvironment yml 文件 其中一种依赖项不是由 conda 分发的 而是通过 pip github 提供 我假设基于这个例子 https github com conda conda b
  • 使用 avro-tools 连接 Avro 文件

    我正在尝试将 avro 文件合并为一个大文件 问题是concat命令不接受通配符 hadoop jar avro tools jar concat input part output bigfile avro I get 线程 main 中
  • MYSQL INSERT 中的德语变音

    我的 mysql 插入语句有问题 我有一个将 utf 8 字符正确提交到插入文件的表单 我已经检查了 POST 变量 现在 当我查看数据库中的 INSERT 时 没有变音符号 而是问号 该错误必须位于插入语句之前 如果我从数据库输出 手动输
  • Scikit-learn Predict_proba 给出错误答案

    这是来自的后续问题如何知道 Scikit learn 中的 Predict proba 返回数组中表示哪些类 https stackoverflow com questions 16937243 how to know what class
  • 使用 Google Analytics 进行 Javascript 覆盖/对话跟踪

    使用 javascript 在我的例子中准确地说是 jQuery 我需要启用一个对话框 以便在 Google Analytics 中将其作为唯一的页面视图进行跟踪 尽管它只是一个模态叠加层 出于上下文目的 我不希望用户离开页面并且对话框内容
  • UITextField 上的 UITapGestureRecognizer 在 IOS 7.1 中不再工作

    我有一个UITapGestureRecognizer附于一个UITextField以获得类似 下拉 的效果 当 的时候UITextField被点击 我提出一个UIPopover与内容 这在 7 1 之前就像一个魅力 现在UITextFiel
  • 如何知道电子邮件地址是否无效?

    我在 www email it 上的电子邮件地址已被禁用 因为我已经很长时间没有使用它了 现在 当我访问 FB 时 我收到以下消息 Our systems have detected that email protected cdn cgi
  • 在 IntelliJ IDEA 14 中运行 JUnit 测试而不选择配置类型

    在带有 Gradle 插件的 IntelliJ IDEA 14 中 我想运行 JUnit 测试 而不询问配置类型 当我第一次运行测试时出现问题 本次运行没有配置 我从未在 IDE 中使用 Gradle 运行测试 因此禁用使用 Gradle
  • 如何根据其他列值设置唯一约束

    我编写以下脚本用于客户域维护 在我的脚本中 我想在表中进行修改 如果状态为活动 我想为端口字段设置唯一约束 否则 如果状态为非活动 我不想设置唯一约束 如何根据其他列值设置此约束 请帮我 bin bash echo Enter the Da
  • C# ui Automation [关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 我正在尝试在 C 中自动化 gui 这与浏览器自动化相同吗 我如何启动 ui 自动化 如果您使用 WPF Microsoft 有一个非
  • 如何使用 heroku CLI 连接到选定的应用程序

    我正在尝试在 Heroku 上部署我的 Java Web 应用程序 当我制作教程时 我使用创建了一个应用程序赫罗库创建命令 我们可以说它是 name app1 然后我在教程结束后删除了它并创建了一个新的来部署它 让它成为 new app 但
  • 最新的 CSS 父选择器 [重复]

    这个问题在这里已经有答案了 我能找到的关于此的最新信息是W3C 选择器 4 级编辑草稿 http dev w3 org csswg selectors 4 但是 据我所知 它不再提及父选择器 我知道有一个谷歌对此进行的调查 https do
  • 我的 TableView 中分隔线之前的空白

    我有一个关于 UITableView 的问题 我有一个 UITableViewController 并且创建了一个自定义单元格 当我可视化 tableView 时 我在分隔线之前看到了一点空白 正如您在这个屏幕截图中看到的那样 为什么 这是
  • F# 与 OCaml:堆栈溢出

    我最近发现了一个关于适合 Python 程序员的 F http combiol org fs FSUG FS4PPv2 pptx 看完之后 我决定自己实现一个 蚂蚁谜题 的解决方案 有一只蚂蚁可以在平面网格上走动 蚂蚁一次可以向左 向右 向