使用Golang的协程竟然变慢了|100万个协程的归并排序耗时分析

news2024/11/13 10:52:19

前言

这篇文章将用三个版本的归并排序,为大家分析使用协程排序的时间开销(被排序的切片长度由128到1000w)

本期demo地址:https://github.com/BaiZe1998/go-learning

往期视频讲解 📺:B站:白泽talk,公众号:白泽talk

image-20240726234405804

认为并发总是更快

  • 线程:OS 调度的基本单位,用于调度到 CPU 上执行,线程的切换是一个高昂的操作,因为要求将当前 CPU 中运行态的线程上下文保存,切换到可执行态,同时调度一个可执行态的线程到 CPU 中执行。
  • 协程:线程由 OS 上下文切换 CPU 内核,而 Goroutine 则由 Go 运行时上下文切换协程。Go 协程占用内存比线程少(2KB/2MB),协程的上下文切换比线程快80~90%。

🌟 GMP 模型:

  • G:Goroutine
    • 执行态:被调度到 M 上执行
    • 可执行态:等待被调度
    • 等待态:因为一些原因被阻塞
  • M:OS thread
  • P:CPU core
    • 每个 P 有一个本地 G 队列(任务队列)
    • 所有 P 有一个公共 G 队列(任务队列)

协程调度规则:每一个 OS 线程(M)被调度到 P 上执行,然后每一个 G 运行在 M 上。

image-20240224111521402

🌟 上图中展示了一个4核 CPU 的机器调度 Go 协程的场景:

此时 P2 正在闲置因为 M3 执行完毕释放了对 P2 的占用,虽然 P2 的 Local queue 中已经空了,没有 G 可以调度执行,但是每隔一定时间,Go runtime 会去 Global queue 和其他 P 的 local queue 偷取一些 G 用于调度执行(当前存在6个可执行的G)。

特别的,在 Go1.14 之前,Go 协程的调度是合作形式的,因此 Go 协程发生切换的只会因为阻塞等待(IO/channel/mutex等),但 Go1.14 之后,运行时间超过 10ms 的协程会被标记为可抢占,可以被其他协程抢占 P 的执行。

归并案例

🌟 为了印证有时候多协程并不一定会提高性能,这里以归并排序为例举三个例子:

image-20240224232145909

主函数:

func main() {
	size := []int{128, 512, 1024, 2048, 100000, 1000000, 10000000}
	sortVersion := []struct {
		name string
		sort func([]int)
	}{
		{"Mergesort V1", SequentialMergesortV1},
		{"Mergesort V2", SequentialMergesortV2},
		{"Mergesort V3", SequentialMergesortV3},
	}
	for _, s := range size {
		fmt.Printf("Testing Size: %d\n", s)
		o := GenerateSlice(s)
		for _, v := range sortVersion {
			s := make([]int, len(o))
			copy(s, o)
			start := time.Now()
			v.sort(s)
			elapsed := time.Since(start)
			fmt.Printf("%s: %s\n", v.name, elapsed)
		}
		fmt.Println()
	}
}

v1的实现:

func sequentialMergesort(s []int) {
    if len(s) <= 1 {
    	return
    }
    middle := len(s) / 2
    sequentialMergesort(s[:middle])
    sequentialMergesort(s[middle:])
    merge(s, middle)
}

func merge(s []int, middle int) {
    // ...
}

v2的实现:

func SequentialMergesortV2(s []int) {
	if len(s) <= 1 {
		return
	}
	middle := len(s) / 2

	var wg sync.WaitGroup
	wg.Add(2)

	go func() {
		defer wg.Done()
		SequentialMergesortV2(s[:middle])
	}()
	go func() {
		defer wg.Done()
		SequentialMergesortV2(s[middle:])
	}()
	wg.Wait()
	Merge(s, middle)
}

v3的实现:

const max = 2048

func SequentialMergesortV3(s []int) {
	if len(s) <= 1 {
		return
	}
	if len(s) < max {
		SequentialMergesortV1(s)
	} else {
		middle := len(s) / 2

		var wg sync.WaitGroup
		wg.Add(2)

		go func() {
			defer wg.Done()
			SequentialMergesortV3(s[:middle])
		}()
		go func() {
			defer wg.Done()
			SequentialMergesortV3(s[middle:])
		}()

		wg.Wait()
		Merge(s, middle)
	}
}

耗时对比:

Testing Size: 128
Mergesort V1: 10.583µs
Mergesort V2: 211.25µs
Mergesort V3: 7.5µs

Testing Size: 512
Mergesort V1: 34.208µs
Mergesort V2: 500.666µs
Mergesort V3: 60.291µs

Testing Size: 1024
Mergesort V1: 48.666µs
Mergesort V2: 643.583µs
Mergesort V3: 47.084µs

Testing Size: 2048
Mergesort V1: 94.666µs
Mergesort V2: 786.125µs
Mergesort V3: 77.667µs

Testing Size: 100000
Mergesort V1: 6.810917ms
Mergesort V2: 58.148291ms
Mergesort V3: 4.219375ms

Testing Size: 1000000
Mergesort V1: 62.053875ms
Mergesort V2: 558.586458ms
Mergesort V3: 37.99375ms

Testing Size: 10000000
Mergesort V1: 632.3875ms
Mergesort V2: 5.764997041s
Mergesort V3: 388.343584ms

由于创建协程和调度协程本身也有开销,第二种情况无论多少个元素都使用协程去进行并行排序,导致归并很少的元素也需要创建协程和调度,开销比排序更多,导致性能还比不上第一种顺序归并。

特别在切片长度为1000w的时候,基于v2创建的协程数量共计百万。

而在本台电脑上,经过调试第三种方式可以获得比第一种方式更优的性能,因为它在元素大于2048个的时候,选择并行排序,而少于则使用顺序排序。但是2048是一个魔法数,不同电脑上可能不同。这里这是为了证明,完全依赖并发/并行的机制,并不一定会提高性能,需要注意协程本身的开销。

小结

可以克隆项目之后,使用协程池写一个归并排序,进一步比较内存消耗。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2094964.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

145. 利用 Redis Bitmap实践: 用户签到统计

文章目录 一、Redis Bitmap简介二、Bitmap 的主要应用三、Go使用Redis实现签到统计用户签到查询用户签到状态统计今年累计签到天数统计当月的签到情况 总结 在现代应用程序中&#xff0c;用户签到是一个常见的功能。我们通常使用 MySQL 数据库来存储用户的签到记录。然而&#…

c++11新特性-智能指针

1. 智能指针的概念及原理 1.1 什么是智能指针 智能指针RAII(Resource Acquisition Is Initialization)&#xff0c;是一种利用对象的生命周期来管理资源的技术。如果我们采用传统的new/delete来申请和释放资源&#xff0c;如果忘记调用delete&#xff0c;或者在调用delete之前…

World of Warcraft [CLASSIC][80][Grandel] Call to Arms: Arathi Basin

PVP战场阿拉希盆地15人 /i 开局队伍分配&#xff1a;圣骑士飙车光环 /i [铁匠铺]坦克、治疗3个、输出6个&#xff08;10人组&#xff09; /i [伐木场]坦克、治疗、输出2&#xff08;4个人组&#xff09; /i [农场]留一个守&#xff08;1个人组&#xff09; /i 不要恋战&#x…

如何准确物理定位EMC Unity存储的磁盘位置

上周收到一个客户的咨询&#xff0c;问题是想主动更换一个Unity存储的磁盘&#xff0c;但不知道这个盘具体在存储的什么位置&#xff0c;有没有命令或者方法准确找到这个磁盘的物理位置&#xff1f; 以前也碰到过过类似的问题&#xff0c;但大部分是来自VNX存储。在现场让客户…

ChatGPT-4o:多领域创新应用的智能助手

ChatGPT-4o&#xff1a;多领域创新应用的智能助手 前言1. 数学建模&#xff1a;ChatGPT-4o的精确计算1.1 专业术语简介1.2 代码示例&#xff1a;线性规划问题问题描述代码实现运行结果 2. AI绘画&#xff1a;ChatGPT-4o的视觉创造力2.1 角色设计示例&#xff1a;火焰魔法师角色…

Leangoo领歌敏捷管理:助力敏捷高效协作,轻松实现Scrum敏捷转型

在当今快速变化的商业环境中&#xff0c;企业面临着前所未有的挑战。如何在激烈的竞争中保持领先&#xff1f;如何快速响应市场需求&#xff1f;答案就在于敏捷转型。而在这一过程中&#xff0c;有一个高效的敏捷工具至关重要——Leangoo领歌&#xff08;Leangoo领歌 - 免费一站…

盛京银行营收、利润双降下的负重难行,症结在哪儿?

撰稿|芋圆 来源|贝多财经 盛京银行自2020开年始&#xff0c;经营业绩除了在2022年稍有回暖外&#xff0c;均处于营收、利润双降的局面。 2024年半年报显示&#xff0c;盛京银行的资产总额为10683亿元&#xff0c;规模较2023年末收缩1.1%&#xff1b;营业收入46亿元&#xff0…

【前缀和算法】--- 进阶题目赏析

Welcome to 9ilks Code World (๑•́ ₃ •̀๑) 个人主页: 9ilk (๑•́ ₃ •̀๑) 文章专栏&#xff1a; 算法Journey 本篇我们来赏析前缀和算法的进阶题目。 &#x1f3e0; 和可被K整除的子数组 &#x1f4cc; 题目解析 和可被k整除的子数组 &#x1f4cc; …

软件单元测试工程模版化

一、简介 在汽车领域混了这么多年也做了不少项目&#xff0c;发现很多公司对软件单元测试和代码覆盖率测试根本不重视&#xff0c;或者开发流程就没有单元测试这个流程。但是有的客户需要评审单元测试这个流程&#xff0c;需要有相关的单元测试报告和代码覆盖率统计的报告。如…

百度 AI Studio 脚本任务篇,它不同于notebook任务是支持免费的, 脚本任务是需要算力卡的,更好的算力 支持四张显卡,

aistudio 脚本任务是需要算力卡的&#xff0c;是收费的一个项目&#xff0c;估计是运行效率更高&#xff0c;支持4张显卡&#xff0c;同时计算。 # -*- coding: utf-8 -*- """ 空白模板 """ ###### 欢迎使用脚本任务&#xff0c;首先让我们熟悉…

计算机毕设选题推荐-基于python的豆瓣电子图书数据可视化分析

&#x1f496;&#x1f525;作者主页&#xff1a;毕设木哥 精彩专栏推荐订阅&#xff1a;在 下方专栏&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; 实战项目 文章目录 实战项目 一、基于python的豆瓣电子图书数…

插入排序代码实现(java)

简介&#xff1a; 也是一种简单的排序方法&#xff0c;其基本操作是将一条记录插入到已排好的有序表中&#xff0c;从而得到一个新的、记录数量增的有序表 说明&#xff1a; 拿一维数组来说&#xff0c;可以把第一个元素看成一个有序表&#xff0c;后面的元素看成无序表&am…

《中文Python穿云箭量化平台二次开发技术08》获取大盘涨跌家数、平均股价数据等来判断市场涨跌趋势,并在策略中自动控制多空交易

《中文Python穿云箭量化平台》是纯Python开发的量化平台&#xff0c;因此其中很多Python模块&#xff0c;我们可以自己设计新的量化工具&#xff0c;例如自己新的行情软件、新的量化平台、以及各种量化研究工具。 穿云箭自带指标公式源码运行模块&#xff0c;可以为其他量化平台…

莫比乌斯反演总结

目录 前置知识1.1 线性筛 (欧拉筛)1.2 整除分块 (数论分块)引理 1引理 2引理 3实现例 1例 2例 3例 4 1.3 数学知识积性函数莫比乌斯函数狄利克雷(Dirichlet)卷积 莫比乌斯反演2.1 公式2.2 常用~(唯一)~结论2.3 例题例 1例 2例 3例 4例 5练习 1练习 2练习 3练习 4 懵逼乌斯反演总…

配置nginx安全连接ssl(购买域名、获取ssl证书)

以前了解过ssl配置比较麻烦&#xff0c;需要弄挺多东西。 1、购买域名、获取ssl证书 2、安装nginx的ssl模块 3、配置config 1、购买域名、获取ssl证书 可以在腾讯云、阿里云购买域名&#xff0c;然后申请免费的ssl证书&#xff0c;因为免费的证书需要域名才能申请&#xff0…

docker安装配置、docker命令

一、CentOS7安装docker 1、安装 Docker CE 支持 64 位版本 CentOS 7&#xff0c;并且要求内核版本不低于 3.10&#xff0c; CentOS 7 满足最低内核的要求&#xff0c;所以我们在CentOS 7安装Docker。 卸载旧docker 如果之前安装过旧版本的Docker&#xff0c;可以使用下面命令…

Codeforces Round 964 (Div. 4) A-E Java题解

比赛地址 Dashboard - Codeforces Round 964 (Div. 4) - Codeforces A题 签到题 给一个两位数 求各位上的数字和 直接对10取余加上本来的数除以10 // 注意类名必须为 Main, 不要有任何 package xxx 信息 // package Dduo; import java.io.*; import java.math.*; import j…

22:差分线规则

1.那些线是差分对&#xff1a; ①有些特定模块就是差分线&#xff1a;USB&#xff0c;HDMI, 以太网口&#xff0c;LEDS等 设置差分对 Panel打开PCB 输入&#xfe62;和- 点击执行 对90欧姆差分对和100Ω差分对进行分类 设置差分对线宽 ①90ohm 由excel可知&a…

孩子自闭症的主要表现:探寻理解之门

自闭症&#xff0c;也称为孤独症&#xff0c;是一种复杂的神经发展障碍&#xff0c;它影响着孩子的社交互动、沟通能力以及行为模式。当家长注意到孩子出现自闭症倾向时&#xff0c;及时识别并寻求专业帮助至关重要。以下是孩子自闭症的一些主要表现&#xff0c;希望能为家长提…

西安电子科技大学研究生新生大数据

西安电子科技大学研究生新生大数据&#xff0c;来自卓越工程学院—杭州研究院 杭研不少来自双非院校&#xff0c;西电也不怎么歧视双非的