gpt4 book ai didi

list - 如何在 Haskell 中创建 channel 列表(或数组),例如 Go

转载 作者:行者123 更新时间:2023-12-04 19:46:49 25 4
gpt4 key购买 nike

我正在尝试将 Haskell 与 Go 进行一些并发示例进行比较,以下代码是 Go 中使用 Goroutines 和 channel 的简单 map-reduce 示例。以下 Go 代码计算平方和:

1^2+2^2+3^2....1024^2



为了测试 Go 和 Haskell 的性能,我重复计算 R 次 (10) 的平方和。

package main

import "fmt"

func mapper(in chan int, out chan int) {
for v := range in {out <- v*v}
}

func reducer(in1, in2 chan int, out chan int) {
for i1 := range in1 {i2 := <- in2; out <- i1 + i2}
}

func main() {
const N = 1024 // calculate sum of squares up to N; N must be power of 2
const R = 10 // number of repetitions to fill the "pipe"

var r [N*2]chan int
for i := range r {r[i] = make(chan int)}
var m [N]chan int
for i := range m {m[i] = make(chan int)}

for i := 0; i < N; i++ {go mapper(m[i], r[i + N])}
for i := 1; i < N; i++ {go reducer(r[i * 2], r[i *2 + 1], r[i])}

go func () {
for j := 0; j < R; j++ {
for i := 0; i < N; i++ {m[i] <- i + 1}
}
} ()

for j := 0; j < R; j++ {
<- r[1]
}
}

问题是如何在 Haskell 中有效地实现这个 mapreduce 示例。以下 Haskell 代码尝试在 main 函数中计算 10^2 + 7^2。我的问题是如何创建一个 channel 数组(或列表),比如 Go,然后在主函数中将映射器和化简器线程连接在一起。
import Control.Concurrent
data MRchannel = MRchannel !(MVar MRcmd)
data MRcmd = Pass !Int | Add !Int
deriving (Show)

mapper:: MRchannel -> MRchannel -> IO ()
mapper left_C@(MRchannel left) right_C@(MRchannel right) = do
v <- takeMVar left
case v of
Pass x -> do
putMVar right (Add (x*x))
mapper left_C right_C
otherwise -> do
putStrLn "Error!"
return ()

reducer:: MRchannel -> MRchannel -> MRchannel -> IO ()
reducer left_1_C@(MRchannel left_1) left_2_C@(MRchannel left_2)
right_C@(MRchannel right) = do
v1 <- takeMVar left_1
case v1 of
Add x1 -> do
v2 <- takeMVar left_2
case v2 of
Add x2 -> do
putMVar right (Add (x1+x2))
reducer left_1_C left_2_C right_C
otherwise -> do
putStrLn "Error!"
return ()
otherwise -> do
putStrLn "Error!"
return ()

main = do
m1_l <- newEmptyMVar
m2_l <- newEmptyMVar
r1_l1 <- newEmptyMVar
r1_l2 <- newEmptyMVar
r1_r <- newEmptyMVar
let m1_input = MRchannel m1_l
let m2_input = MRchannel m2_l
let r1_input1 = MRchannel r1_l1
let r1_input2 = MRchannel r1_l2
let r1_output = MRchannel r1_r
forkIO $ mapper m1_input r1_input1
forkIO $ mapper m2_input r1_input2
forkIO $ reducer r1_input1 r1_input2 r1_output

putMVar m1_l (Pass 10)
putMVar m2_l (Pass 7)

y <- takeMVar r1_r
case y of
Add kvalue -> do
putStrLn $ show kvalue
otherwise -> do
putStrLn "Error"
return ()

最佳答案

这是一个替代方案。在我看来,它不是非常优雅,但比原始代码简单一些。

主要变化:

  • 我们没有 MVars 的两个列表。并使用慢 !!
  • 我们使用 forkAll它产生整个映射器/ reducer 的二叉树(它的参数是深度:2^10 = 1024)
  • forkAll仍然不是最佳的,因为它使用 ++虽然速度较慢
  • 主要变化:做时putMVar ,我们使用 $! 强制该值,这样我们就不会在 MVar 中存储未计算的表达式.这是一个重大的性能提升。
  • 我们只打印它们的总和,而不是打印所有结果。否则,文本 IO 会减慢基准测试的速度。 (请注意,原始 repeats 执行 n+1 循环,因此我必须使用 10241 而不是下面的 10240 来匹配结果。)

  • 新代码快了 54%,在这里。
    import Control.Concurrent
    import Control.Monad

    mapper :: MVar Int -> MVar Int -> IO ()
    mapper left right = forever $ do
    v <- takeMVar left
    putMVar right $! v*v

    reducer :: MVar Int -> MVar Int -> MVar Int -> IO ()
    reducer left_1 left_2 right = forever $ do
    v1 <- takeMVar left_1
    v2 <- takeMVar left_2
    putMVar right $! v1+v2

    forkAll :: Int -> MVar Int -> IO [MVar Int]
    forkAll 0 res = do
    v <- newEmptyMVar
    _ <- forkIO $ mapper v res
    return [v]
    forkAll depth res = do
    v1 <- newEmptyMVar
    r1 <- forkAll (depth - 1) v1
    v2 <- newEmptyMVar
    r2 <- forkAll (depth - 1) v2
    _ <- forkIO $ reducer v1 v2 res
    return (r1++r2)

    main :: IO ()
    main = do
    v <- newEmptyMVar
    res <- forkAll 10 v

    nums <- replicateM 10241 $ do
    mapM_ (\ (r,i) -> putMVar r (i+1)) $ zip res [0..]
    takeMVar v
    print $ sum nums

    关于list - 如何在 Haskell 中创建 channel 列表(或数组),例如 Go,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49711757/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com