【发布时间】:2018-09-17 14:26:09
【问题描述】:
我正在尝试比较 Haskell 和 Go 的一些并发示例,以下代码是 Go 中使用 Goroutines 和通道的简单 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。我的问题是如何创建一个通道数组(或列表),例如 Go,然后在 main 函数中将映射器和减速器线程连接在一起。
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 ()
【问题讨论】:
-
你能用文字描述你想计算什么吗?
-
GO 程序不打印任何东西;您答案中的haskell程序不断打印'358438400'。你能描述一下你想计算什么吗?那就是说;在我看来,正确的方法是利用 haskell 惰性(github.com/quchen/articles/blob/master/loeb-moeb.md)并在减速器中撒上
par/pseq。 -
看看
BoundedChan。 -
我正在尝试计算平方和:1^2+2^2+3^2....1024^2, (N * (N + 1) * (2 * N + 1)) / 6. 为了测试 Go 和 Haskell 的性能,我重复计算平方和 10 次 (R)。
-
@user2336525
MVar允许您最多存储一个值。因此,如果生产者碰巧比消费者快,它就会阻塞。使用BoundedChan,您可以将更多值放入通道中,在这种情况发生时提供一些缓冲空间。 |请注意,对于性能比较,您可能会使用 parallelization rather than concurrency 获得更好的数字 - 虽然非常相似,但它们服务于完全不同的用例。