几年前,我通过动态编程解决了一个问题:
https://www.thanassis.space/fillupDVD.html
解决方案是用 Python 编写的。
作为拓展视野的一部分,我最近开始学习 OCaml/F#。有什么比直接将我在 Python 中编写的命令式代码移植到 F# 更好的方法来测试水域 - 然后从那里开始,逐步走向函数式编程解决方案。
第一个直接端口的结果......令人不安:
在 Python 下:
bash$ time python fitToSize.py
....
real 0m1.482s
user 0m1.413s
sys 0m0.067s
在 FSharp 下:
bash$ time mono ./fitToSize.exe
....
real 0m2.235s
user 0m2.427s
sys 0m0.063s
(如果您注意到上面的“单声道”:我也在 Windows 下进行了测试,使用 Visual Studio - 相同的速度)。
至少可以说,我……很困惑。 Python 运行代码比 F# 快?使用 .NET 运行时编译的二进制文件运行速度比 Python 的解释代码慢?!?!
我知道虚拟机的启动成本(在这种情况下是单声道)以及 JIT 如何改进 Python 等语言的性能,但仍然......我预计会加速,而不是减速!
也许我做错了什么?
我已经在这里上传了代码:
https://www.thanassis.space/fsharp.slower.than.python.tar.gz
请注意,F# 代码或多或少是 Python 代码的直接逐行翻译。
附:当然还有其他收获,例如F# 提供的静态类型安全性 - 但如果在 F# 下命令式算法的结果速度更差……至少可以这么说,我很失望。
编辑:根据评论中的要求直接访问:
Python 代码:https://gist.github.com/950697
FSharp 代码:https://gist.github.com/950699
最佳答案
我通过电子邮件联系的 Jon Harrop 博士解释了发生的情况:
The problem is simply that the program has been optimized for Python. This is common when the programmer is more familiar with one language than the other, of course. You just have to learn a different set of rules that dictate how F# programs should be optimized... Several things jumped out at me such as the use of a "for i in 1..n do" loop rather than a "for i=1 to n do" loop (which is faster in general but not significant here), repeatedly doing List.mapi on a list to mimic an array index (which allocated intermediate lists unnecessarily) and your use of the F# TryGetValue for Dictionary which allocates unnecessarily (the .NET TryGetValue that accepts a ref is faster in general but not so much here)
... but the real killer problem turned out to be your use of a hash table to implement a dense 2D matrix. Using a hash table is ideal in Python because its hash table implementation has been extremely well optimized (as evidenced by the fact that your Python code is running as fast as F# compiled to native code!) but arrays are a much better way to represent dense matrices, particularly when you want a default value of zero.
有趣的是,当我第一次编写这个算法时,我DID使用了一个表——为了清楚起见,我将实现更改为字典(避免数组边界检查使代码更简单- 而且更容易推理)。
Jon 将我的代码(返回 :-))转换为 array version ,它以 100 倍的速度运行。
故事的寓意:
谢谢你,乔恩——非常感谢。
编辑:将 Dictionary 替换为 Array 使得 F# 最终以编译语言预期运行的速度运行这一事实,并不否定修复 Dictionary 速度的需要(我希望 F#来自 MS 的人正在阅读这篇文章)。其他算法依赖于字典/哈希,不能轻易切换到使用数组;每当使用字典时,使程序遭受“解释器速度”,可以说是一个错误。如果像评论中的一些人所说的那样,问题不在于 F#,而在于 .NET 字典,那么我认为这......是 .NET 中的一个错误!
EDIT2:最清晰的解决方案,不需要算法切换到数组(有些算法根本不适合)是改变这个:
let optimalResults = new Dictionary<_,_>()
进入这个:
let optimalResults = new Dictionary<_,_>(HashIdentity.Structural)
此更改使 F# 代码的运行速度提高了 2.7 倍,从而最终击败了 Python(速度提高了 1.6 倍)。奇怪的是元组默认使用结构比较,所以原则上,字典对键所做的比较是相同的(有或没有结构)。 Harrop 博士推测速度差异可能归因于虚拟调度:“AFAIK,.NET 几乎没有优化虚拟调度,并且虚拟调度的成本在现代硬件上非常高,因为它是一个“计算的 goto”,将程序计数器跳转到不可预测的位置,从而破坏分支预测逻辑,几乎肯定会导致整个 CPU 管道被刷新和重新加载”。
简而言之,正如 Don Syme (look at the bottom 3 answers) 所建议的,“在将引用类型的键与 .NET 集合结合使用时,请明确说明结构散列的使用”。 (Harrop 博士在下面的评论中还说,我们应该始终在使用 .NET 集合时使用结构比较)。
尊敬的 MS 中的 F# 团队,如果有办法自动解决此问题,请执行。
关于python - FSharp 运行我的算法比 Python 慢,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5850243/
总的来说,我对ruby还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用
关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。
在选择我想要运行操作的频率时,唯一的选项是“每天”、“每小时”和“每10分钟”。谢谢!我想为我的Rails3.1应用程序运行调度程序。 最佳答案 这不是一个优雅的解决方案,但您可以安排它每天运行,并在实际开始工作之前检查日期是否为当月的第一天。 关于ruby-如何每月在Heroku运行一次Scheduler插件?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/8692687/
exe应该在我打开页面时运行。异步进程需要运行。有什么方法可以在ruby中使用两个参数异步运行exe吗?我已经尝试过ruby命令-system()、exec()但它正在等待过程完成。我需要用参数启动exe,无需等待进程完成是否有任何rubygems会支持我的问题? 最佳答案 您可以使用Process.spawn和Process.wait2:pid=Process.spawn'your.exe','--option'#Later...pid,status=Process.wait2pid您的程序将作为解释器的子进程执行。除
我尝试运行2.x应用程序。我使用rvm并为此应用程序设置其他版本的ruby:$rvmuseree-1.8.7-head我尝试运行服务器,然后出现很多错误:$script/serverNOTE:Gem.source_indexisdeprecated,useSpecification.Itwillberemovedonorafter2011-11-01.Gem.source_indexcalledfrom/Users/serg/rails_projects_terminal/work_proj/spohelp/config/../vendor/rails/railties/lib/r
Sinatra新手;我正在运行一些rspec测试,但在日志中收到了一堆不需要的噪音。如何消除日志中过多的噪音?我仔细检查了环境是否设置为:test,这意味着记录器级别应设置为WARN而不是DEBUG。spec_helper:require"./app"require"sinatra"require"rspec"require"rack/test"require"database_cleaner"require"factory_girl"set:environment,:testFactoryGirl.definition_file_paths=%w{./factories./test/
我是一个Rails初学者,但我想从我的RailsView(html.haml文件)中查看Ruby变量的内容。我试图在ruby中打印出变量(认为它会在终端中出现),但没有得到任何结果。有什么建议吗?我知道Rails调试器,但更喜欢使用inspect来打印我的变量。 最佳答案 您可以在View中使用puts方法将信息输出到服务器控制台。您应该能够在View中的任何位置使用Haml执行以下操作:-puts@my_variable.inspect 关于ruby-on-rails-如何在我的R
GivenIamadumbprogrammerandIamusingrspecandIamusingsporkandIwanttodebug...mmm...let'ssaaay,aspecforPhone.那么,我应该把“require'ruby-debug'”行放在哪里,以便在phone_spec.rb的特定点停止处理?(我所要求的只是一个大而粗的箭头,即使是一个有挑战性的程序员也能看到:-3)我已经尝试了很多位置,除非我没有正确测试它们,否则会发生一些奇怪的事情:在spec_helper.rb中的以下位置:require'rubygems'require'spork'
我喜欢使用Textile或Markdown为我的项目编写自述文件,但是当我生成RDoc时,自述文件被解释为RDoc并且看起来非常糟糕。有没有办法让RDoc通过RedCloth或BlueCloth而不是它自己的格式化程序运行文件?它可以配置为自动检测文件后缀的格式吗?(例如README.textile通过RedCloth运行,但README.mdown通过BlueCloth运行) 最佳答案 使用YARD直接代替RDoc将允许您包含Textile或Markdown文件,只要它们的文件后缀是合理的。我经常使用类似于以下Rake任务的东西:
rails中是否有任何规定允许站点的所有AJAXPOST请求在没有authenticity_token的情况下通过?我有一个调用Controller方法的JqueryPOSTajax调用,但我没有在其中放置任何真实性代码,但调用成功。我的ApplicationController确实有'request_forgery_protection'并且我已经改变了config.action_controller.consider_all_requests_local在我的environments/development.rb中为false我还搜索了我的代码以确保我没有重载ajaxSend来发送