当goroutine的数量增加时,Go程序变慢

hwazgwia  于 2023-01-10  发布在  Go
关注(0)|答案(1)|浏览(133)

我正在为我的并行课程做一个小项目,我尝试了使用带缓冲通道、无缓冲通道、使用指向切片的指针的无缓冲通道等。此外,我还尝试尽可能优化它(不是当前状态),但我仍然得到相同的结果:增加goroutine的数量(甚至是1个)会降低整个程序的运行速度,有人能告诉我我做错了什么吗?在这种情况下,并行性增强是可能的吗?
下面是部分代码:

func main() {

    rand.Seed(time.Now().UnixMicro())

    numAgents := 2

    fmt.Println("Please pick a number of goroutines: ")
    fmt.Scanf("%d", &numAgents)

    numFiles := 4
    fmt.Println("How many files do you want?")
    fmt.Scanf("%d", &numFiles)
    start := time.Now()

    numAssist := numFiles
    channel := make(chan []File, numAgents)
    files := make([]File, 0)

    for i := 0; i < numAgents; i++ {
        if i == numAgents-1 {
            go generateFiles(numAssist, channel)
        } else {
            go generateFiles(numFiles/numAgents, channel)
            numAssist -= numFiles / numAgents
        }
    }

    for i := 0; i < numAgents; i++ {
        files = append(files, <-channel...)
    }

    elapsed := time.Since(start)
    fmt.Printf("Function took %s\n", elapsed)
}
func generateFiles(numFiles int, channel chan []File) {
    magicNumbersMap := getMap()
    files := make([]File, 0)

    for i := 0; i < numFiles; i++ {
        content := randElementFromMap(&magicNumbersMap)

        length := rand.Intn(400) + 100
        hexSlice := getHex()

        for j := 0; j < length; j++ {
            content = content + hexSlice[rand.Intn(len(hexSlice))]
        }

        hash := getSHA1Hash([]byte(content))

        file := File{
            content: content,
            hash:    hash,
        }

        files = append(files, file)
    }

    channel <- files

}

我们的期望是通过增加goroutine的数量程序会运行得更快,但是当goroutine的数量达到一定值时,我会得到相同的执行时间或者稍微慢一点.
编辑:使用的所有函数:

import (
    "crypto/sha1"
    "encoding/base64"
    "fmt"
    "math/rand"
    "time"
)

type File struct {
    content string
    hash    string
}

func getMap() map[string]string {
    return map[string]string{
        "D4C3B2A1": "Libcap file format",
        "EDABEEDB": "RedHat Package Manager (RPM) package",
        "4C5A4950": "lzip compressed file",
    }
}

func getHex() []string {
    return []string{
        "0", "1", "2", "3", "4", "5",
        "6", "7", "8", "9", "A", "B",
        "C", "D", "E", "F",
    }
}

func randElementFromMap(m *map[string]string) string {
    x := rand.Intn(len(*m))
    for k := range *m {
        if x == 0 {
            return k
        }
        x--
    }
    return "Error"
}

func getSHA1Hash(content []byte) string {
    h := sha1.New()
    h.Write(content)
    return base64.URLEncoding.EncodeToString(h.Sum(nil))
}
kpbwa7wx

kpbwa7wx1#

简单地说-文件生成代码并不复杂到足以证明并行执行的合理性。所有的上下文切换和通过通道移动数据吃掉了并行处理的所有好处。
如果你在generateFiles函数的循环中加入类似time.Sleep(time.Millisecond * 10)的东西,就好像它在做一些更复杂的事情一样,你会看到你所期望的结果--更多的goroutine运行得更快,但是同样,只有到了一定程度,额外的并行处理工作会超过好处。
还要注意程序最后一位的执行时间:

for i := 0; i < numAgents; i++ {
    files = append(files, <-channel...)
}

直接取决于goroutine的数量,因为所有的goroutine几乎同时完成,所以这个循环几乎不会和你的工作线程并行执行,它运行的时间只是简单地加到总时间上。
接下来,当您多次追加files切片时,它必须增长几倍并将数据复制到新位置,您可以通过最初创建一个切片来填充所有结果元素(幸运的是,您确切地知道需要多少)来避免这种情况。

相关问题