&引用;“矩阵乘法”;使用goroutines和channels

&引用;“矩阵乘法”;使用goroutines和channels,go,matrix-multiplication,channel,goroutine,Go,Matrix Multiplication,Channel,Goroutine,我有一个大学项目,当我使用1个goroutine、2个goroutine、3等等时,测试矩阵乘法的时差。我必须使用频道。我的问题是,不管我添加多少go例程,编译时间几乎总是一样的。也许有人能知道问题出在哪里。也许发送的时间很长,而且一直都在发送。代码如下所示 package main import ( "fmt" "math/rand" "time" ) const length = 1000 var start time.Time var rez [length][le

我有一个大学项目,当我使用1个goroutine、2个goroutine、3等等时,测试矩阵乘法的时差。我必须使用频道。我的问题是,不管我添加多少go例程,编译时间几乎总是一样的。也许有人能知道问题出在哪里。也许发送的时间很长,而且一直都在发送。代码如下所示

package main
import (
    "fmt"
    "math/rand"
    "time"
)
const length = 1000
var start time.Time
var rez [length][length]int
func main() {
    const threadlength = 1
    toCalcRow := make(chan []int)
    toCalcColumn := make(chan []int)
    dummy1 := make(chan int)
    dummy2 := make(chan int)
    var row [length + 1]int
    var column [length + 1]int
    var a [length][length]int
    var b [length][length]int
    for i := 0; i < length; i++ {
        for j := 0; j < length; j++ {
            a[i][j] = rand.Intn(10)
            b[i][j] = rand.Intn(10)
        }
    }
    for i := 0; i < threadlength; i++ {
        go Calc(toCalcRow, toCalcColumn, dummy1, dummy2)
    }
    start = time.Now()
    for i := 0; i < length; i++ {
        for j := 0; j < length; j++ {
            row[0] = i
            column[0] = j
            for k := 0; k < length; k++ {
                row[k+1] = a[i][j]
                column[k+1] = b[i][k]
            }
            rowSlices := make([]int, len(row))
            columnSlices := make([]int, len(column))
            copy(rowSlices, row[:])
            copy(columnSlices, column[:])
            toCalcRow <- rowSlices
            toCalcColumn <- columnSlices
        }
    }
    dummy1 <- -1
    for i := 0; i < length; i++ {
        for j := 0; j < length; j++ {
            fmt.Print(rez[i][j])
            fmt.Print(" ")
        }
        fmt.Println(" ")
    }
    <-dummy2
    close(toCalcRow)
    close(toCalcColumn)
    close(dummy1)
}
func Calc(chin1 <-chan []int, chin2 <-chan []int, dummy <-chan int, dummy1 chan<- int) {
loop:
    for {
        select {
        case row := <-chin1:
            column := <-chin2
            var sum [3]int
            sum[0] = row[0]
            sum[1] = column[0]
            for i := 1; i < len(row); i++ {
                sum[2] += row[i] * column[i]
            }
            rez[sum[0]][sum[1]] = sum[2]
        case <-dummy:
            elapsed := time.Since(start)
            fmt.Println("Binomial took ", elapsed)
            dummy1 <- 0
            break loop
        }
    }
    close(dummy1)
}
主程序包
进口(
“fmt”
“数学/兰德”
“时间”
)
常数长度=1000
变量开始时间。时间
变量rez[length][length]int
func main(){
常数threadlength=1
toCalcRow:=制造(chan[]内部)
ToCalColumn:=make(chan[]int)
dummy1:=制造(成交量)
dummy2:=make(chan int)
变量行[length+1]int
变量列[length+1]int
变量a[length][length]int
变量b[length][length]int
对于i:=0;itoCalcRow您的代码很难理解(调用变量dummy1/dummy2会让人困惑,尤其是当它们在
Calc
中得到不同的名称时),添加一些注释会使代码更容易理解


首先是一个错误。在发送要计算的数据后,你看不出有什么区别,因为准备数据传递到go例程是你的瓶颈。它比执行计算慢或快

传递行和列的副本不是一个好策略。这会降低性能

go例程可以直接从只读的输入矩阵读取数据。这里没有可能的竞争条件

输出也是一样。如果go例程计算行和列的乘法,它会将结果写入一个不同的单元格。这里也没有可能的竞争条件

具体操作如下:定义一个包含两个字段的结构,一个用于行,一个用于列乘法

用行和列的所有可能组合填充缓冲通道,以从(0,0)乘至(n-1,m-1)

go例程使用通道中的结构,执行计算并将结果直接写入输出矩阵


然后还有一个done通道向主go例程发送信号,表示计算已完成。当go例程完成对结构(n-1,m-1)的处理后,它将关闭done通道

主go例程在写入所有结构后等待done通道。一旦done通道关闭,它将打印经过的时间。 我们可以使用等待组来等待所有go例程终止其计算

然后可以从一个go例程开始,增加go例程的数量,以查看处理时间的影响

参见代码:

package main

import (
    "fmt"
    "math/rand"
    "sync"
    "time"
)

type pair struct {
    row, col int
}

const length = 1000

var start time.Time
var rez [length][length]int

func main() {
    const threadlength = 1
    pairs := make(chan pair, 1000)
    var wg sync.WaitGroup
    var a [length][length]int
    var b [length][length]int
    for i := 0; i < length; i++ {
        for j := 0; j < length; j++ {
            a[i][j] = rand.Intn(10)
            b[i][j] = rand.Intn(10)
        }
    }
    wg.Add(threadlength)
    for i := 0; i < threadlength; i++ {
        go Calc(pairs, &a, &b, &rez, &wg)
    }
    start = time.Now()
    for i := 0; i < length; i++ {
        for j := 0; j < length; j++ {
            pairs <- pair{row: i, col: j}
        }
    }
    close(pairs)
    wg.Wait()
    elapsed := time.Since(start)
    fmt.Println("Binomial took ", elapsed)

    for i := 0; i < length; i++ {
        for j := 0; j < length; j++ {
            fmt.Print(rez[i][j])
            fmt.Print(" ")
        }
        fmt.Println(" ")
    }
}

func Calc(pairs chan pair, a, b, rez *[length][length]int, wg *sync.WaitGroup) {
    for {
        pair, ok := <-pairs
        if !ok {
            break
        }
        rez[pair.row][pair.col] = 0
        for i := 0; i < length; i++ {
            rez[pair.row][pair.col] += a[pair.row][i] * b[i][pair.col]
        }
    }
    wg.Done()
}
主程序包
进口(
“fmt”
“数学/兰德”
“同步”
“时间”
)
类型对结构{
行,列
}
常数长度=1000
变量开始时间。时间
变量rez[length][length]int
func main(){
常数threadlength=1
配对:=制造(成双,1000)
var wg sync.WaitGroup
变量a[length][length]int
变量b[length][length]int
对于i:=0;ipairs编译时间不受您生成的goroutine数量的影响。您可能指的是执行时间,对吗?“当go例程完成对结构(n-1,m-1)的处理时,它会关闭完成通道”;这假设goroutine将按照与接收到的数据相同的顺序芬兰语,但我认为不一定是这样的情况?(我不相信您的示例代码实际上需要这样做;您可以删除对done的所有引用,并且由于waitgroup,您的解决方案仍然可以正常运行)。除此之外,您的解决方案将比我的更快。@Brits唯一的假设是数据的读取顺序与写入通道的顺序相同。我们确实需要等待以通知go例程终止。他们可以关闭pairs通道,而不是done通道。我们可以通过这种方式摆脱done通道。正如您所说,done将始终保持不变已计算通道上最后一项的计算时关闭;但是,这并不意味着先前的计算已完成(waitgroup会这样做)。完全删除已完成的通道不会对代码产生实际影响,因为您已经关闭了对(这样做之前不需要等待所有内容都被读取;添加到缓冲通道的值在其关闭后仍然可以读取;例如@Brits您是对的。我在代码中删除了done通道。
package main

import (
    "fmt"
    "math/rand"
    "sync"
    "time"
)

type pair struct {
    row, col int
}

const length = 1000

var start time.Time
var rez [length][length]int

func main() {
    const threadlength = 1
    pairs := make(chan pair, 1000)
    var wg sync.WaitGroup
    var a [length][length]int
    var b [length][length]int
    for i := 0; i < length; i++ {
        for j := 0; j < length; j++ {
            a[i][j] = rand.Intn(10)
            b[i][j] = rand.Intn(10)
        }
    }
    wg.Add(threadlength)
    for i := 0; i < threadlength; i++ {
        go Calc(pairs, &a, &b, &rez, &wg)
    }
    start = time.Now()
    for i := 0; i < length; i++ {
        for j := 0; j < length; j++ {
            pairs <- pair{row: i, col: j}
        }
    }
    close(pairs)
    wg.Wait()
    elapsed := time.Since(start)
    fmt.Println("Binomial took ", elapsed)

    for i := 0; i < length; i++ {
        for j := 0; j < length; j++ {
            fmt.Print(rez[i][j])
            fmt.Print(" ")
        }
        fmt.Println(" ")
    }
}

func Calc(pairs chan pair, a, b, rez *[length][length]int, wg *sync.WaitGroup) {
    for {
        pair, ok := <-pairs
        if !ok {
            break
        }
        rez[pair.row][pair.col] = 0
        for i := 0; i < length; i++ {
            rez[pair.row][pair.col] += a[pair.row][i] * b[i][pair.col]
        }
    }
    wg.Done()
}