Golang并发与非并发代码性能相同的原因是什么

Golang并发与非并发代码性能相同的原因是什么

package main

import (
    "fmt"
    "time"
)

func measureTime(str string) func() {
    start := time.Now()
    return func() {
        fmt.Printf("%s took  %v time for execution\n", str, time.Since(start))
    }
}

// Sequential 使用顺序算法。
func Sequential(words []string) map[rune]int { // 字符串数组的简单解决方案
    m := make(map[rune]int)
    for _, word := range words {
        for _, c := range word {
            m[c]++
        }
    }
    return m
}

// ConcurrentUnlimited 使用基于无限扇出模式的并发算法。
func Concurrent(text []string) map[rune]int {
    ch := make(chan map[rune]int, len(text))
    for _, words := range text {
        go func(words string) {
            lm := make(map[rune]int)
            for _, r := range words {
                lm[r]++
            }
            ch <- lm
        }(words)
    }
    all := make(map[rune]int)
    for range text {
        lm := <-ch
        for r, c := range lm {
            all[r] += c
        }
    }
    return all
}

func main() {
    defer measureTime("freq")()
    var words []string = []string{"there is solution to every problems"}
    //  words :=make([]string,5)
    //  words = "there is solution to every problems"
    //ret :=Sequential(words)528.9us
    ret := Concurrent(words) //517.8us
    fmt.Println(ret)
    //  }为什么两者的性能表现完全一样
    // }
}

更多关于Golang并发与非并发代码性能相同的原因是什么的实战教程也可以访问 https://www.itying.com/category-94-b0.html

2 回复

嗨,@mohammed_yaqub

我现在才看到你的帖子,希望我的意见仍然有用。

在没有深入分析的情况下,我猜测 Concurrent() 中的最后一个循环所花费的时间与 Sequential() 中的循环大致相同。

实际上,这个循环甚至是一个嵌套循环,而顺序执行的循环则不是,这让我不禁思考为什么 Concurrent 实际上并没有更慢

另一个想法是:测试输入是一个相当短的字符串。如果你尝试使用一个长的字符串(真的,非常非常长),你可能会得到更准确的测量结果。

更多关于Golang并发与非并发代码性能相同的原因是什么的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


并发版本性能没有提升的原因主要有以下几点:

1. 数据量太小

func main() {
    // 当前只有1个字符串,数据量太小
    words := []string{"there is solution to every problems"}
    
    // 增加数据量才能体现并发优势
    words = make([]string, 10000)
    for i := range words {
        words[i] = "there is solution to every problems"
    }
}

2. goroutine创建开销

每个goroutine的创建和调度都有开销:

func Concurrent(text []string) map[rune]int {
    ch := make(chan map[rune]int, len(text))
    for _, words := range text {
        go func(words string) {  // 每个字符串启动一个goroutine
            // goroutine创建和切换的开销可能超过计算本身
            lm := make(map[rune]int)
            for _, r := range words {
                lm[r]++
            }
            ch <- lm
        }(words)
    }
    // ...
}

3. 结果合并的串行瓶颈

func Concurrent(text []string) map[rune]int {
    // ...
    all := make(map[rune]int)
    for range text {
        lm := <-ch
        for r, c := range lm {  // 这部分是串行执行的
            all[r] += c         // map合并需要加锁或串行处理
        }
    }
    return all
}

4. 正确的并发实现示例

func ConcurrentOptimized(text []string) map[rune]int {
    // 使用worker池控制goroutine数量
    numWorkers := runtime.NumCPU()
    chunkSize := len(text) / numWorkers
    
    var wg sync.WaitGroup
    results := make(chan map[rune]int, numWorkers)
    
    for i := 0; i < numWorkers; i++ {
        wg.Add(1)
        start := i * chunkSize
        end := start + chunkSize
        if i == numWorkers-1 {
            end = len(text)
        }
        
        go func(words []string) {
            defer wg.Done()
            lm := make(map[rune]int)
            for _, word := range words {
                for _, r := range word {
                    lm[r]++
                }
            }
            results <- lm
        }(text[start:end])
    }
    
    go func() {
        wg.Wait()
        close(results)
    }()
    
    all := make(map[rune]int)
    for lm := range results {
        for r, c := range lm {
            all[r] += c
        }
    }
    
    return all
}

5. 性能对比测试

func benchmark() {
    // 生成大量数据
    words := make([]string, 1000000)
    for i := range words {
        words[i] = "concurrent programming in golang"
    }
    
    // 测试顺序版本
    start := time.Now()
    Sequential(words)
    fmt.Printf("Sequential: %v\n", time.Since(start))
    
    // 测试并发版本
    start = time.Now()
    ConcurrentOptimized(words)
    fmt.Printf("Concurrent: %v\n", time.Since(start))
}

并发性能优势只有在:

  • 数据量足够大
  • 计算密集型任务
  • 合理控制goroutine数量
  • 避免过度的同步开销

时才会体现出来。

回到顶部