为什么该程序的 F# 版本比 Haskell 版本快 6 倍?

2024-03-13

Haskell版本(1.03s):

module Main where
  import qualified Data.Text as T
  import qualified Data.Text.IO as TIO
  import Control.Monad
  import Control.Applicative ((<$>))
  import Data.Vector.Unboxed (Vector,(!))
  import qualified Data.Vector.Unboxed as V

  solve :: Vector Int -> Int
  solve ar =
    V.foldl' go 0 ar' where
      ar' = V.zip ar (V.postscanr' max 0 ar)
      go sr (p,m) = sr + m - p

  main = do
    t <- fmap (read . T.unpack) TIO.getLine -- With Data.Text, the example finishes 15% faster.
    T.unlines . map (T.pack . show . solve . V.fromList . map (read . T.unpack) . T.words)
      <$> replicateM t (TIO.getLine >> TIO.getLine) >>= TIO.putStr

F#版本(0.17s):

open System

let solve (ar : uint64[]) =
    let ar' = 
        let t = Array.scanBack max ar 0UL |> fun x -> Array.take (x.Length-1) x
        Array.zip ar t

    let go sr (p,m) = sr + m - p
    Array.fold go 0UL ar'

let getIntLine() =
    Console.In.ReadLine().Split [|' '|]
    |> Array.choose (fun x -> if x <> "" then uint64 x |> Some else None)    

let getInt() = getIntLine().[0]

let t = getInt()
for i=1 to int t do
    getInt() |> ignore
    let ar = getIntLine()
    printfn "%i" (solve ar)

以上两个方案是针对这个问题的解决方案库存最大化问题 https://www.hackerrank.com/challenges/stockmax/时间是第一个测试用例的时间Run Code button.

由于某种原因,F# 版本大约快 6 倍,但我非常确定,如果我用命令式循环替换缓慢的库函数,我可以将其速度提高至少 3 倍,更有可能是 10 倍。

Haskell 版本可以进行类似的改进吗?

我这样做是出于学习目的,总的来说,我发现很难弄清楚如何编写高效的 Haskell 代码。


If you switch to ByteString http://hackage.haskell.org/package/bytestring-0.10.8.1/docs/Data-ByteString.html and stick with plain Haskell lists (instead of vectors) you will get a more efficient solution. You may also rewrite the solve function with a single left fold and bypass zip and right scan (1). Overall, on my machine, I get 20 times performance improvement compared to your Haskell solution (2).

下面的 Haskell 代码比 F# 代码执行得更快:

import Data.List (unfoldr)
import Control.Applicative ((<$>))
import Control.Monad (replicateM_)
import Data.ByteString (ByteString)
import qualified Data.ByteString as B
import qualified Data.ByteString.Char8 as C

parse :: ByteString -> [Int]
parse = unfoldr $ C.readInt . C.dropWhile (== ' ')

solve :: [Int] -> Int
solve xs = foldl go (const 0) xs minBound
    where go f x s = if s < x then f x else s - x + f s

main = do
    [n] <- parse <$> B.getLine
    replicateM_ n $ B.getLine >> B.getLine >>= print . solve . parse

1. See edits https://stackoverflow.com/posts/37527523/revisions for an earlier version of this answer which implements solve using zip and scanr.
2. HackerRank website shows even a larger performance improvement.

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

为什么该程序的 F# 版本比 Haskell 版本快 6 倍? 的相关文章

  • 表长度运算符的性能

    卢阿有 运算符来计算用作数组的表的 长度 在诸如 C 之类的语言中 计算出某个内容的长度后 通常不会再次计算它 例如int len strlen string 这在 Lua 中有什么不同吗 其中一个的效率是否比另一个低 显然这可能不会显示显
  • 模式匹配需要圆括号来表示非空列表而不是方括号

    在 Haskell 中 为什么模式匹配期望列表在不为空时有圆括号而不是方括号 当它尝试与空列表 方括号 进行模式匹配时 为什么它不遵循相同的约定 圆括号不应该专门为元组保留吗 例如 下面不起作用 third Integral a gt a
  • 为什么删除 else 会减慢我的代码速度?

    考虑以下函数 def fact1 n if n lt 2 return 1 else return n fact1 n 1 def fact2 n if n lt 2 return 1 return n fact2 n 1 它们应该是等价的
  • 专家 f# 脚本编译奇怪

    第 209 210 页有一个扩展示例 见下文 我使用的是 F 4 5 总之 我不明白的是 如果我单独键入每个语句 则会有一个声明引发错误 如果我立即提交整个脚本 以及引发错误的声明之后的函数 则一切正常 那么 当我批量提交所有语句时 交互中
  • 在字节数组上进行右位旋转/循环移位的最快方法是什么

    如果我有数组 01101111 11110000 00001111 111 240 15 移位 1 位的结果是 10110111 11111000 00000111 183 248 7 数组大小不固定 移位范围为 1 到 7 含 目前我有以
  • 为什么 LinkedList 通常比 List 慢?

    我开始在我的一些 C 算法中使用一些 LinkedList 而不是列表 希望能够加快速度 然而 我注意到他们只是感觉更慢 像任何优秀的开发人员一样 我认为我应该尽职调查并验证我的感受 所以我决定对一些简单的循环进行基准测试 我认为用一些随机
  • .NET 程序集大小会影响性能吗?

    net 程序集的大小是否会影响性能 您的 Windows 窗体 Web 窗体项目中的程序集数量如何 来自微软的模式和实践提高 NET 应用程序性能和可扩展性 http msdn microsoft com en us library ms9
  • 将数据添加到闪存中的段如何会扰乱程序的时序?

    我有一个实时嵌入式应用程序 其主周期以 10KHz 运行 它在配置为从闪存启动的 TI TMS320C 上运行 我最近在源文件中添加了一个初始化的数组 突然间时间就搞砸了 以一种太复杂的方式无法很好地解释 本质上串行端口写入不再按时完成 这
  • 测试列表是否已排序

    在 haskell 中找到最小列表确实很容易 foldl1 min 9 5 7 3 7 4 6 10 给我3 我更换了min with lt 测试列表是否已排序 foldl1 lt 9 5 7 3 7 4 6 10 我收到此错误消息 No
  • 什么比 std::pow 更快?

    我的程序 90 的 CPU 时间都花在std pow double int 功能 准确性不是这里的主要问题 所以我想知道是否有更快的替代方案 我正在考虑尝试的一件事是强制转换为浮动 执行操作然后返回到双精度 尚未尝试过 我担心这不是一种提高
  • 简单的Java程序插入USB热点后速度慢100倍

    我有以下Java程序 class Main public static void main String args throws java io IOException long start System nanoTime java io
  • AngularJS 与(Angular JS + jQuery)

    我有一个关于仅使用 AngularJS 和纯 JavaScript 以及使用 AngularJS 和 jQuery 时的性能问题 ex app directive fitHeight function window return restr
  • 绑定变量时 Haskell 中的无限循环

    下面的 Haskell 代码不会终止 有人可以解释一下为什么吗 谢谢 f let x 10 in let x x x in x 我认为解释器首先绑定 x 10 然后将 x x 计算为 100 并绑定 x 100 环境变为 x 100 那么整
  • 为什么这会导致 Haskell Conduit 库内存泄漏?

    我有一个conduit https hackage haskell org package conduit管道处理长文件 我想每 1000 条记录为用户打印一份进度报告 所以我这样写 Every n records perform the
  • 为什么any (True for ... if cond) 比any (cond for ...) 快得多?

    检查列表是否包含奇数的两种类似方法 any x 2 for x in a any True for x in a if x 2 计时结果与a 0 10000000 每次尝试五次 次数以秒为单位 0 60 0 60 0 60 0 61 0 6
  • 对相当大的整数的大集合的操作的快速实现

    描述 我实现了以下类 LabSetInt64 参见下面的代码 这里的目标是尽可能快地操作大量大整数 最多 10M 的值 我的主要要求集中在 至关重要 尽快获取集合的大小 基数 重要 能够非常快速地迭代一组集合 所以 从下面的实现开始 我还有
  • 经常访问 NSUserDefaults

    在我的应用程序的逻辑处理过程中 我需要频繁访问用户首选项 并多次访问 10 15 次 以确定需要处理什么以及如何处理 也许这个问题不是关于性能的问题 而是关于正确执行的问题 目前我正在做一个 NSUserDefaults standardU
  • 如何限制 Android 设备网络速度以进行测试

    我正在测试一个 Android 应用程序 该应用程序在低质量网络上管理其内容时遇到一些问题 我无法验证问题是否仍然存在 因为以我家的网络速度 120mb s 在我设法开始复制路线之前 所有内容都已经下载完毕 在这种情况下 不能选择使用 An
  • 在生成此 SOP 函数时,如何修复类型错误,包括“无法对 Traversable 进行量化”?

    我只是说我什至不确定这是否可能 这是迄今为止我在 Haskell 中尝试过的最通用的事情 我正在尝试制作一个更通用的版本applyFunc在发现https stackoverflow com a 58890226 3096687 https
  • 为什么 foreach 这么慢?

    PHPBench com http www phpbench com 在每个页面加载上运行快速基准测试脚本 在 foreach 测试中 当我加载它时 foreach 的运行时间是第三个示例的 4 到 10 倍 为什么本机语言构造明显比执行逻

随机推荐