【问题标题】:Using Go channels to speed up for loop使用 Go 通道加速 for 循环
【发布时间】:2016-06-07 15:15:13
【问题描述】:

我正在尝试使用 Go 的并发来加速我的代码,

这是我所拥有的:

            for i:=7; i>-1; i-- {
                go func (ch chan int32, ch2 chan int32, i int, arx int32, ary int32, dirf []int8, dirg []int8) {
                    nx := arx + int32(dirf[i])
                    ny := ary + int32(dirg[i])
                    ch <- nx
                    ch2 <- ny

                }(ch, ch2, i, arx,ary,dirf,dirg)

            }
            for i:=7; i>-1; i-- {
                nxx := <- ch
                nyx := <- ch2
                ind := nyx*w+nxx
                if imData[ind] == e[i]{
                    process[c]=nxx
                    process[c+1]=nyx
                    c+=2
                    matrix[ind]=1
                }
            }

运行此之后,我没有得到预期的矩阵切片,它充满了零。

但是如果我运行下面的代码,它给出的矩阵切片就像没有通道的代码一样,但是它太慢了。

            for i:=7; i>-1; i-- {
                go func (ch chan int32, ch2 chan int32, i int, arx int32, ary int32, dirf []int8, dirg []int8) {
                    nx := arx + int32(dirf[i])
                    ny := ary + int32(dirg[i])
                    ch <- nx
                    ch2 <- ny

                }(ch, ch2, i, arx,ary,dirf,dirg)
                nxx := <- ch
                nyx := <- ch2
                ind := nyx*w+nxx
                if imData[ind] == e[i]{
                    process[c]=nxx
                    process[c+1]=nyx
                    c+=2
                    matrix[ind]=1
                }
            }

第一个有什么问题?有任何想法吗?我对 Go 很陌生。所以,当你提出一些建议时,请清楚。

编辑: 我编辑了代码以使值按正确的顺序排列,

type data struct {
    i int
    nx int32
    ny int32
}

           for i:=7; i>-1; i-- {
                go func (ch chan data, i int, arx int32, ary int32, dirf []int8, dirg []int8) {
                    nx := arx + int32(dirf[i])
                    ny := ary + int32(dirg[i])
                    ch <- data{i,nx,ny}

                }(ch, i, arx,ary,dirf,dirg)

            }
            for i:=7; i>-1; i-- {
                d := <- ch
                nxx := d.nx
                nyx := d.ny
                j := d.i
                ind := nyx*w+nxx
                if imData[ind] == e[j]{
                    process[c]=nxx
                    process[c+1]=nyx
                    c+=2
                    matrix[ind]=1
                }
            }

现在可以了,但还是太慢了。

我正在尝试加速这个主代码:

for i:=7; i>-1; i-- {
        nx := arx + int32(dirf[i])
        ny := ary + int32(dirg[i])
        ind := ny*w+nx
        if imData[ind] == e[i]{
            process[c]=nx
            process[c+1]=ny
            c+=2
            matrix[ind]=1
        }
    }

你对此有何建议?

【问题讨论】:

    标签: go concurrency channels


    【解决方案1】:

    在第二种情况下,您确定 goroutines 以“正确的顺序”执行,因为您等待 goroutines 完成,然后再继续下一个。

    一个例子是 golang 操场上的 this minimal example。要解决此问题,您可能需要通过通道传递三个成员的结构,即您的 nxnyi 值。

    【讨论】:

    • 谢谢你的回答,它使代码工作了,但还是太慢了,我更新了问题,你也看一下吗?
    【解决方案2】:

    我怀疑您的“if imData[ind] == e[i]”条件在前一种情况下失败了,但是如果没有通道的设置代码以及有关这些不同切片的更多详细信息,就很难判断抓住。您是否尝试过使用 print 语句运行它以查看您从渠道中获得了什么?

    另外,请注意,如果有问题的通道被缓冲,则不能保证 chch2 中的值将采用相同的顺序。这很可能是你的问题。

    Goroutine 1 可以在 ch 上赋值,但 Goroutine 2 可以在 Goroutine 1 到达之前在 ch2 上赋值。如果您有 7 个 goroutine,则完全有可能在通道(或任意数量的其他通道)上看到以下顺序:

    ch: 1, 2, 3, 4, 5, 6, 7

    ch2: 1, 3, 4, 5, 6, 7, 2

    如果它们没有被缓冲,这对你的代码来说是不可能的,但它在技术上仍然是不安全的(编辑:实际上,它仍然不会与第二个循环中的i 匹配)。如果数据是一组有序对,则应将每对作为结构通过单个通道发送。

    顺便说一句,您只需要将变量传递给go func() 调用,前提是它们预计会在该调用之外发生变化。 chch2arxarydirfdirg 对于此代码块似乎都是有效的常量,因此不需要传递到 go func() .你只需要传入i,因为循环会在将外壳触发到goroutine后立即更改它。

    现在,从纯粹的速度角度来看,您最好将第一个循环移动到 go func() 调用中。您可以在主例程中循环时创建 7 个 goroutine,而不是创建一个例程,它会循环遍历这些值并将它们发送到通道上。如果通道被缓冲到至少那个大小,这将成为一个非常快速的操作。顺便说一句,这也解决了通道排序的问题(尽管在单个通道上将有序对作为结构发送更好),因为您只有一个 goroutine 尝试在通道上发送。

    【讨论】:

    • 好像是订单的问题,谢谢解答,但还是太慢了,我更新了问题,你知道如何加快速度吗?
    • 我们在这里谈论的是什么类型的速度?你做过benchmarking吗?我能看到的唯一主要加速是不将d 的值分配给其他变量,而是直接使用它们,如果有必要,通过通道而不是结构传递指向结构的指针。不过,我们在这里开始发挥微秒级优势。
    • 没有例程的原始版本需要约 150 毫秒,但有例程的工作版本需要约 13 秒。它甚至没有加速。
    • 好吧,还有其他事情发生了,因为单独的代码应该需要几微秒才能完成。例如,您的代码带有一些数据类型的填充,并且在操场上运行得非常快:play.golang.org/p/vhqEHQXwEM 我怀疑减速是由于程序的其他部分,或者代码中不明显或不存在的东西上面的示例。
    • 这次测量只是针对我提供的 for 循环。还有一个上部 for 循环,但我使用 start2 := time.Now(),elapsed2 := time.Since(start2),res += elapsed2(res 也是持续时间)进行此测量。此外,需要注意的是,切片进程和矩阵的大小分别为 44000 和 21712000。当我将尺寸设置为这些时,它甚至没有运行,说它花了太长时间。
    猜你喜欢
    • 2014-03-31
    • 2023-04-04
    • 2021-09-03
    • 2021-01-30
    • 2023-04-02
    • 1970-01-01
    • 2022-01-15
    • 2015-05-27
    • 1970-01-01
    相关资源
    最近更新 更多