Maison >développement back-end >Golang >Pourquoi les performances de « move_avg_concurrent2 » ne s'améliorent-elles pas avec une concurrence accrue, malgré la division de la liste en morceaux plus petits traités par des goroutines individuelles ?
Pourquoi les performances de moving_avg_concurrent2 ne s'améliorent-elles pas avec l'augmentation de l'exécution simultanée ?
moving_avg_concurrent2 divise la liste en morceaux plus petits et utilise une seule goroutine pour gérer chaque morceau. Pour une raison quelconque (on ne sait pas pourquoi), cette fonction utilisant un seul goroutine est plus rapide que moving_avg_serial4, mais l'utilisation de plusieurs goroutines commence à fonctionner moins bien que moving_avg_serial4.
Pourquoi moving_avg_concurrent3 est beaucoup plus lent que moving_avg_serial4 ?
Les performances de moving_avg_concurrent3 sont pires que celles de moving_avg_serial4 lors de l'utilisation d'une goroutine. Bien que l'augmentation de num_goroutines puisse améliorer les performances, c'est encore pire que moving_avg_serial4.
Même si les goroutines sont légères, elles ne sont pas totalement gratuites, est-il possible que la surcharge encourue soit si importante qu'elle soit encore plus lente que moving_avg_serial4 ?
Oui, même si les goroutines sont légères, elles ne sont pas gratuites. Lors de l'utilisation de plusieurs goroutines, les frais liés à leur lancement, leur gestion et leur planification peuvent dépasser les avantages d'un parallélisme accru.
Code
Fonction :
// 返回包含输入移动平均值的列表(已提供,即未优化) func moving_avg_serial(input []float64, window_size int) []float64 { first_time := true var output = make([]float64, len(input)) if len(input) > 0 { var buffer = make([]float64, window_size) // 初始化缓冲区为 NaN for i := range buffer { buffer[i] = math.NaN() } for i, val := range input { old_val := buffer[int((math.Mod(float64(i), float64(window_size))))] buffer[int((math.Mod(float64(i), float64(window_size))))] = val if !NaN_in_slice(buffer) && first_time { sum := 0.0 for _, entry := range buffer { sum += entry } output[i] = sum / float64(window_size) first_time = false } else if i > 0 && !math.IsNaN(output[i-1]) && !NaN_in_slice(buffer) { output[i] = output[i-1] + (val-old_val)/float64(window_size) // 无循环的解决方案 } else { output[i] = math.NaN() } } } else { // 空输入 fmt.Println("moving_avg is panicking!") panic(fmt.Sprintf("%v", input)) } return output } // 返回包含输入移动平均值的列表 // 重新排列控制结构以利用短路求值 func moving_avg_serial4(input []float64, window_size int) []float64 { first_time := true var output = make([]float64, len(input)) if len(input) > 0 { var buffer = make([]float64, window_size) // 初始化缓冲区为 NaN for i := range buffer { buffer[i] = math.NaN() } for i := range input { // fmt.Printf("in mvg_avg4: i=%v\n", i) old_val := buffer[int((math.Mod(float64(i), float64(window_size))))] buffer[int((math.Mod(float64(i), float64(window_size))))] = input[i] if first_time && !NaN_in_slice(buffer) { sum := 0.0 for j := range buffer { sum += buffer[j] } output[i] = sum / float64(window_size) first_time = false } else if i > 0 && !math.IsNaN(output[i-1]) /* && !NaN_in_slice(buffer)*/ { output[i] = output[i-1] + (input[i]-old_val)/float64(window_size) // 无循环的解决方案 } else { output[i] = math.NaN() } } } else { // 空输入 fmt.Println("moving_avg is panicking!") panic(fmt.Sprintf("%v", input)) } return output } // 返回包含输入移动平均值的列表 // 将列表拆分为较小的片段以使用 goroutine,但不使用串行版本,即我们仅在开头具有 NaN,因此希望减少一些开销 // 仍然不能扩展(随着大小和 num_goroutines 的增加,性能下降) func moving_avg_concurrent2(input []float64, window_size, num_goroutines int) []float64 { var output = make([]float64, window_size-1, len(input)) for i := 0; i < window_size-1; i++ { output[i] = math.NaN() } if len(input) > 0 { num_items := len(input) - (window_size - 1) var barrier_wg sync.WaitGroup n := num_items / num_goroutines go_avg := make([][]float64, num_goroutines) for i := 0; i < num_goroutines; i++ { go_avg[i] = make([]float64, 0, num_goroutines) } for i := 0; i < num_goroutines; i++ { barrier_wg.Add(1) go func(go_id int) { defer barrier_wg.Done() // 计算边界 var start, stop int start = go_id*int(n) + (window_size - 1) // 开始索引 // 结束索引 if go_id != (num_goroutines - 1) { stop = start + n // 结束索引 } else { stop = num_items + (window_size - 1) // 结束索引 } loc_avg := moving_avg_serial4(input[start-(window_size-1):stop], window_size) loc_avg = make([]float64, stop-start) current_sum := 0.0 for i := start - (window_size - 1); i < start+1; i++ { current_sum += input[i] } loc_avg[0] = current_sum / float64(window_size) idx := 1 for i := start + 1; i < stop; i++ { loc_avg[idx] = loc_avg[idx-1] + (input[i]-input[i-(window_size)])/float64(window_size) idx++ } go_avg[go_id] = append(go_avg[go_id], loc_avg...) }(i) } barrier_wg.Wait() for i := 0; i < num_goroutines; i++ { output = append(output, go_avg[i]...) } } else { // 空输入 fmt.Println("moving_avg is panicking!") panic(fmt.Sprintf("%v", input)) } return output } // 返回包含输入移动平均值的列表 // 模式改变,我们选择主工作者模式并生成将由 goroutine 计算的每个窗口 func compute_window_avg(input, output []float64, start, end int) { sum := 0.0 size := end - start for _, val := range input[start:end] { sum += val } output[end-1] = sum / float64(size) } func moving_avg_concurrent3(input []float64, window_size, num_goroutines int) []float64 { var output = make([]float64, window_size-1, len(input)) for i := 0; i < window_size-1; i++ { output[i] = math.NaN() } if len(input) > 0 { num_windows := len(input) - (window_size - 1) var output = make([]float64, len(input)) for i := 0; i < window_size-1; i++ {
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!