多路归并排序_1600W整数排序多路归并

本文介绍了一种使用Go语言实现16M整数数组的并发多路归并排序方法,通过协程池实现分组并行排序和最小堆合并。在并发8路排序时,性能提升约3倍,但内存消耗增加128M。测试结果显示,merge过程中的append操作优化能减少约10ms的耗时。
摘要由CSDN通过智能技术生成

Merge Sort

问题描述

Go 语言实现一个16M的整数(int64)多路归并的数组排序

思路

将待排序数组分成多个组,利用多个goroutine实现各个组的并行排序;然后通过Heap(最小堆)进行多路归并排序

实现

实现一个协程池实现任务的并行处理,将待排序切片分组并封装成SortTask放入协程池 运行,待全部执行完成后ConcurrentSorter收集排序结果,并封装成MergeTask放入协程池中进行合并。

  • 协程池pool.go
    • 配置最大协程数量
    • 按需创建协程
    • 空闲超时则回收协程
  • 合并有序切片algorithm.heap_merge.go 通不采用2路循环合并,避免分配过多的内存碎片。通过堆实现多路的有序切片的合并,额外申请一倍的内存用于存放合并结果

归并算法

输入:n路待合并的有序slice

输出:有序slice

堆node定义为一个SortedSlice,实现了hasNext函数,用于迭代到当前slice的下一个元素;

type Iterator struct {
	slice []int64
	index int
}

func (i *Iterator) HasNext() bool {
	return i.index < len(i.slice)-1
}

func (i *Iterator) Next() {
	i.index++
}

func (i *Iterator) Value() int64 {
	return i.slice[i.index]
}

type SortedSlice struct {
	slice []int64
	Iterator
}

堆的定义:

type HeapMerge struct {
	nodes []*SortedSlice
}

构建一个n个元素的最小堆,从每路slice中取首个元素组成数组,调整堆;

每次从堆顶,取一个元素,放入合并后的slice中

  • 如果hasNext=true,执行当前node的Next(),重新调整当前的原因
  • 如果hasNext=false, 当前slice已经空了,因此剔除堆顶, 然后需要重建堆,原因是堆中的父子关系已经破坏。
if h.nodes[0].HasNext() {
	h.nodes[0].Next() //不需要获取值
	h.adjust(0, len(h.nodes))
} else { // 顶部的node(slice)已经为空
	if len(h.nodes) >= 1 {
		// 移除为已经合并完成的slice
		h.nodes = h.nodes[1:]
		//h.adjust(0, len(h.nodes))
		h.Build()
	} else {
		return 0, errors.New("merge complete")
	}
}

代码结构

303818ed08f240cb9b3169670ed77a1a.png

性能测试

并发8路排序的的情况下,性能大约提升三倍,主要原因是分组排序之后需要进行多路的合并。测试结果如下:

9f92255c3004719bde384e3ebb2f209c.png

内存消耗比直接排序增加了128M,是因为合并排序结果过程申请了一块内存来暂存结果128M = 16M*8B

296e0c6d12585a901c42eb36e41addc3.png

cpu的消耗大多在排序过程,merge过程5%

360d50fee9d05623daacbcfdff35ef80.png

merge过程中调用append(slice)消耗了290ms,直接改为修改slice的下标竟然减少了大约10ms

940cce01de80077da030074f3f3069eb.png

dcfbda4d37c205b8c78962dd50db3327.png

github-mergesort源码

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值