【高并发系列】26、并行排序

CUDA(六). 从并行排序方法理解并行化思维——冒泡、归并、双调排序的GPU实现 在第五讲中我们学习了GPU三个重要的基础并行算法: Reduce, Scan 和 Histogram,分析了 其作用与串并行实现方法。 在第六讲中,本文以冒泡排序 Bubble Sort、归并排序 Merge Sort 和排序网络中的双调排序 Bitonic Sort 为例, 讲解如何从数据结构课上学的串行并行排序方法转换到并行排序,并附GPU实现代码。 在并行方法中,我们将考虑到并行方法需要注意 阅读详情

1、先回顾一下冒泡排序

public class BubbleSort {
	public static void bubbleSort(int[] arr) {
		for (int i = arr.length - 1; i > 0; i--) {
			for (int j = 0; j < i; j++) {
				if (arr[j] > arr[j + 1]) {
					int tmp = arr[j + 1];
					arr[j + 1] = arr[j];
					arr[j] = tmp;
				}
				System.out.println(Arrays.toString(arr));
			}
		}
	}
	public static void main(String[] args) {
		int[] arr = new int[] { 5, 4, 3, 2, 1 };
		bubbleSort(arr);
	}
}
[4, 5, 3, 2, 1]
[4, 3, 5, 2, 1]
[4, 3, 2, 5, 1]
[4, 3, 2, 1, 5]
[3, 4, 2, 1, 5]
[3, 2, 4, 1, 5]
[3, 2, 1, 4, 5]
[2, 3, 1, 4, 5]
[2, 1, 3, 4, 5]
[1, 2, 3, 4, 5]

在冒泡排序的每次迭代交换过程中,每次交换的两个元素存在数据冲突,对于每个元素,都有可能与其前面或后面的元素交换,因此很难改成并行算法;

2、奇偶交换排序

需要解开数据的相关性,就容易使用并行算法来实现排序;

分为奇交换和偶交换,奇交换比较奇数索引及其相邻后续元素,偶交换比较偶数索引及其相邻后续元素;奇偶校验成对出现;

奇偶交换迭代示意图如下

2.1 奇偶校验的串行实现如下

public class OddEvenSort {
	public static void oddEvenSort(int[] arr) {
		// 记录当前迭代是否发生数据交换,1为发生交换,0为未发生
		int exchangeFlag = 1;
		// 表示当前是奇交换还是偶交换,0为偶交换,1为奇交换
		int start = 0;
		// 如果上一次迭代发生了数据交换,或者当前正在进行的是奇交换,循环不会停止
		// 直到程序不在发生交换,并且当前进行的是偶交换
		while (exchangeFlag == 1 || start == 1) {
			exchangeFlag = 0;
			for (int i = start; i < arr.length - 1; i += 2) {
				if (arr[i] > arr[i + 1]) {
					int tmp = arr[i];
					arr[i] = arr[i + 1];
					arr[i + 1] = tmp;
					exchangeFlag = 1;
				}
				System.out.println(Arrays.toString(arr));
			}
			// 每次迭代结束,切换奇偶交换状态
			if (start == 0) {
				start = 1;
			} else {
				start = 0;
			}
		}
	}
	public static void main(String[] args) {
		int[] arr = new int[] { 5, 4, 3, 2, 1 };
		oddEvenSort(arr);
	}
}

2.2 奇偶校验的并行模式实现

上述代码中可以看到,由于把整个比较交换独立分割为奇校验和偶校验,使得在每个阶段内,所有的比较和交换都是没有数据相关性的,所以可以比较方便的改造为并行模式:

public class ParallelOddEvenSort {
	static volatile int[] arr;
	static int exchangeFlag = 1;
	static ExecutorService pool = Executors.newCachedThreadPool();
	static synchronized void setExchangeFlag(int v) {
		exchangeFlag = v;
	}
	static synchronized int getExchangeFlag() {
		return exchangeFlag;
	}
	public static class OddEvenSortTask implements Runnable {
		int i;
		CountDownLatch latch;

		public OddEvenSortTask(int i, CountDownLatch latch) {
			this.i = i;
			this.latch = latch;
		}
		@Override
		public void run() {
			// 进行数据比较和必要的交换
			if (arr[i] > arr[i + 1]) {
				int tmp = arr[i];
				arr[i] = arr[i + 1];
				arr[i + 1] = tmp;
				setExchangeFlag(1);
			}
			System.out.println(Arrays.toString(arr));
			latch.countDown();
		}
	}
	public static void pOddEvenSort(int[] arr) throws InterruptedException {
		int start = 0;
		while (getExchangeFlag() == 1 || start == 1) {
			setExchangeFlag(0);
			// 偶数的数组长度
			CountDownLatch latch = new CountDownLatch(arr.length / 2 - (arr.length % 2 == 0 ? start : 0));
			for (int i = start; i < arr.length - 1; i += 2) {
				pool.submit(new OddEvenSortTask(i, latch));
			}
			// 等待所有线程结束,在下次迭代开始前必须等待上一次迭代所有线程完成
			latch.await();
			if (start == 0) {
				start = 1;
			} else {
				start = 0;
			}
		}
	}
	public static void main(String[] args) throws InterruptedException {
		arr = new int[] { 5, 4, 3, 2, 1 };
		pOddEvenSort(arr);
		pool.shutdown();
	}
}
[4, 5, 2, 3, 1]
[4, 5, 2, 3, 1]
[4, 5, 2, 1, 3]
[4, 2, 5, 1, 3]
[4, 2, 1, 5, 3]
[2, 4, 1, 5, 3]
[2, 1, 4, 3, 5]
[2, 1, 4, 3, 5]
[1, 2, 4, 3, 5]
[1, 2, 3, 4, 5]
[1, 2, 3, 4, 5]
[1, 2, 3, 4, 5]

 3、插入排序

3.1 普通的插入排序

一个未排序的的数组分为两部分,前半部分为已经排序的,后半部分为未排序的;排序时,从未排序的后半部分中选择一个元素,将其插入前面有序的数组中即可,如此循环往复,直到未排序的部分为0即完成排序。

普通插入排序因为上一次的数据插入依赖于上一次得到的有序序列,所以多个步骤无法并行。

public class InsertSort {
	public static void insertSort(int[] arr) {
		int length = arr.length;
		int i, j, key;
		for (i = 1; i < length; i++) {
			key = arr[i];
			j = i - 1;
			while (j >= 0 && arr[j] > key) {
				arr[j + 1] = arr[j];
				j--;
			}
			arr[j + 1] = key;
			System.out.println(Arrays.toString(arr));
		}
	}
	public static void main(String[] args) {
		int[] arr = new int[] { 5, 4, 3, 2, 1 };
		insertSort(arr);
	}
}
[4, 5, 3, 2, 1]
[3, 4, 5, 2, 1]
[2, 3, 4, 5, 1]
[1, 2, 3, 4, 5]

3.2 希尔排序 Shell's Sort

希尔排序是插入排序的扩展,将整个数组根据间隔n分割为若干个子数组(即数组序号为n+1的倍数的元素为一组),每次排序,分别对每一个子数组进行插入排序;每组排序完成后,递减n值,进行更加精细的排序,直到n为1时,此时等价于一次插入排序;

其串行实现如下:(假设n=3)

public class ShellSort {
	public static void shellSort(int[] arr) {
		// 计算合适的间隔
		int n = 1;
		while (n <= arr.length / 3) {
			n = n * 3 + 1;
		}
		while (n > 0) {
			for (int i = n; i < arr.length; i++) {
				if (arr[i] < arr[i - n]) {
					int tmp = arr[i];
					int j = i - n;
					while (j >= 0 && arr[j] > tmp) {
						arr[j + n] = arr[j];
						j -= n;
					}
					arr[j + n] = tmp;
				}
			}
			// 递减间隔值
			n = (n - 1) / 3;
			System.out.println(Arrays.toString(arr));
		}
	}
	public static void main(String[] args) {
		int[] arr = new int[] { 9, 8, 7, 6, 5, 4, 3, 2, 1 };
		shellSort(arr);
	}
}
[1, 4, 3, 2, 5, 8, 7, 6, 9]
[1, 2, 3, 4, 5, 6, 7, 8, 9]

即使一个较小的元素在数组的末尾,由于每次元素移动都以n为间隔进行,因此数组末尾的小元素可以在很少的交换次数下被置换到最接近元素最终位置的地方;

并行版

public class ParallelShellSort {
	static volatile int[] arr;
	static ExecutorService pool = Executors.newCachedThreadPool();
	public static class ShellSortTask implements Runnable {
		int i = 0;
		int n = 0;
		CountDownLatch latch;
		public ShellSortTask(int i, int n, CountDownLatch latch) {
			this.i = i;
			this.n = n;
			this.latch = latch;
		}
		@Override
		public void run() {
			if (arr[i] < arr[i - n]) {
				int tmp = arr[i];
				int j = i - n;
				while (j >= 0 && arr[j] > tmp) {
					arr[j + n] = arr[j];
					j -= n;
				}
				arr[j + n] = tmp;
			}
			latch.countDown();
		}
	}
	public static void pShellSort(int[] arr) throws InterruptedException {
		int n = 1;
		CountDownLatch latch = null;
		while (n <= arr.length / 3) {
			n = n * 3 + 1;
		}
		while (n > 0) {
			System.out.println("n=" + n);
			if (n >= 4) {
				latch = new CountDownLatch(arr.length - n);
			}
			for (int i = n; i < arr.length; i++) {
				if (n >= 4) {
					pool.execute(new ShellSortTask(i, n, latch));
				} else {
					if (arr[i] < arr[i - n]) {
						int tmp = arr[i];
						int j = i - n;
						while (j >= 0 && arr[j] > tmp) {
							arr[j + n] = arr[j];
							j -= n;
						}
						arr[j + n] = tmp;
					}
					System.out.println(Arrays.toString(arr));
				}
			}
			// 等待线程排序完成进入下一次排序
			latch.await();
			n = (n - 1) / 3;
		}
	}
	public static void main(String[] args) throws InterruptedException {
		arr = new int[] { 9, 8, 7, 6, 5, 4, 3, 2, 1 };
		pShellSort(arr);
		pool.shutdown();
	}
}
n=4
n=1
[1, 4, 3, 2, 5, 8, 7, 6, 9]
[1, 3, 4, 2, 5, 8, 7, 6, 9]
[1, 2, 3, 4, 5, 8, 7, 6, 9]
[1, 2, 3, 4, 5, 8, 7, 6, 9]
[1, 2, 3, 4, 5, 8, 7, 6, 9]
[1, 2, 3, 4, 5, 7, 8, 6, 9]
[1, 2, 3, 4, 5, 6, 7, 8, 9]
[1, 2, 3, 4, 5, 6, 7, 8, 9]

 

C++的并行算法:并行排序 排序是计算机科学中的一个基本问题,它的目标是将一个序列的元素按照某种顺序排列。在C++中,我们可以使用std::sort算法进行排序。然而,std::sort是一个顺序算法,它在一个时间点只执行一个操作。为了提高排序的性能,我们可以使用并行排序算法,它在多个处理器上同时执行多个操作。C++17标准库中的std::sort并没有并行版本,但我们可以使用其他的并行算法库,如Intel Threading Building Blocks (TBB) 或 Parallel STL,来进行并行排序。 阅读详情

相关推荐

java高并发系列 - 第26篇:学会使用JUC中常见的集合,常看看!

可以看一下这个类的名称:CopyOnWrite,意思是在写入操作的时候,进行一次自我复制,换句话说,当这个List需要修改时,并不修改原有内容(这对于保证当前在读线程的数据一致性非常重要),而是在原有存放数据的数组上产生一个副本,在副本上修改数据,修改完毕之后,用副本替换原来的数组,这样也保证了写操作不会影响读。3种操作,每种操作有2个方法,不同点是队列为空或者满载时,调用方法是抛出异常还是返回特殊值,大家按照表格中的多看几遍,加深记忆。实现了Deque接口,内部使用链表实现的高效的并发双端队列。

专搞技术的小兔子 123

【高性能计算】经典的串行排序算法和并行排序算法

快速排序也可以并行化处理,它是一种基于分治策略的排序算法,常用的方法是选取一个枢纽元素,将数组划分为两个部分,并将小于枢纽元素的元素放入左侧部分,将大于枢纽元素的元素放入右侧部分。选择排序是一种简单直观的排序算法,它的工作原理是每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全部待排序的数据元素排完。归并排序也是一种常见的排序算法,它的核心思想是将待排序数组分成若干个子数组,对每个子数组进行排序,然后再将已经有序的子数组合并成一个有序的数组。冒泡排序是一种简单的排序算法。

慢热型网友的博客 3497

通达信源码-金牛暴起

趋势1:=3*SMA((CLOSE-LLV(LOW,27))/(HHV(HIGH,27)-LLV(LOW,27))*100,5,1)-2*SMA(SMA((CLOSE-LLV(LOW,27))/(HHV(HIGH,27)-LLV(LOW,27))*100,5,1),3,1) AND SJTJ,DOTLINE,COLORGRAY;谷:=(((DD-LLV(DD,21))/(HHV(DD,21)-LLV(DD,21)))*(4))*(25);{0,25指高度};

qq_57222058的博客 3532

不一样的排序算法【并行排序】

对于排序算法相信大家都不陌生,大部分排序的程序都是串行的排序算法,比如冒泡排序,插入排序,选择排序,堆排序等等,但是随着计算机的发展,现在的计算机都是多核的处理器,串行排序无法高效的利用CPU,为了更加有效的利用CPU,我们在这里介绍一下在并行世界中的排序算法。 一、奇偶交换排序 在介绍并行排序之前,我们有必要介绍一下奇偶排序,为了将串行排序算法改为并行排序,使用奇偶排序可以更加方便的修改...

不清不慎的博客 7438

电压抬升电路设计

一、实验目的 掌握集成运放电压放大电路设计基本方法。 掌握基本仪器使用方法(电源、信号发生器、示波器)。 二、实验内容及结果 实验内容 基于集成运放设计一传感器信号采集电路。传感器输出信号在±50mV,频率为100Hz以内,模数转换器允许输入电压在0~5V,要求设计传感器与模数转换器之间的信号采集电路,实现传感器信号放大10倍,并适合模数转换器的输入。模数转换器的输入电阻为1KHz,设计电路时,可使用1KHz电阻作为信号采集电路负载,代替模数转换器。 实验具体要求如下: (1)确定电路形式,说明电压转

Safaearth的博客 4万+

VSCode+Keil高效开发STM32:Keil Assistant插件配置全攻略

本文详细介绍了如何通过VSCode的Keil Assistant插件,将Keil MDK的编译调试能力与VSCode的现代编辑体验相结合,打造高效的STM32开发环境。文章涵盖了从环境配置、工程导入、智能提示设置到与STM32CubeMX协同工作的全流程攻略,旨在解决Keil编辑器效率低下的痛点,大幅提升嵌入式开发的编码与项目管理效率。

gold的博客 508

并行排序算法——时间复杂度O(n)的排序法

最近老师讲了并行的排序算法,让我对这个原来不是很了解的排序算法产生了浓厚的兴趣。并行排序方法,是指采用并行计算的方法对一组数据进行排序,理论上是在类似内排序的环境下,采用多核并行的方法让时间降低,排序的复杂度最好的情况下能降低至O(n)左右。排序的实质排序的实质是什么?这是一个不是问题的问题。我们可以说是让所有的数都按照一定的规则被放置,但这种说法实际上是解释了排序的汉字含义。换句话不如说排序是:从

往事散云烟,十寸光阴换一钱 1万+

别急着做AI:FDE教你识别真需求,让AI真正落地

人工智能项目在落地过程中,经常出现业务方高喊“我们要做AI”,却说不清具体应用场景的现象。判断一个需求是否值得用AI实现,不能只看技术流行度,而要从问题出现频率、当前替代方案以及可量化业务价值三个维度展开验证。同时需要冷静比较规则引擎与大模型的边界:不是所有语义任务都要靠生成式模型,稳定、可解释且低成本的传统方案往往更具工程性价比。数据闭环也至关重要,缺乏历史样本与反馈回流路径的项目,很难在真实环境中持续优化。针对智能客服、合同审查或企业知识库等典型场景,可借助人机协同与最小代价的用户验证,逐步筛除伪需求,

weixin_42624889的博客 342

C++26标准即将落地:5大并行算法在高并发系统中的真实应用案例

掌握C++26并行算法,提升高并发系统性能。通过2025全球C++及系统软件技术大会:C++26并行算法的工程应用,解析5大新标准算法在分布式计算、实时处理等场景的落地实践,显著优化吞吐与响应。核心方法实用,案例真实可复用,值得收藏。

PixelIsle的博客 881

c语言实现语音检测vad_壹鸽技术科普·智能语音客服系统中的前端语音处理技术...

随着智能客服机器人产业的迅速发展,尤其是伴随着人工智能AI的风潮,预计在2020年我国智能客服市场将达到万亿级别。在智能客服机器人领域中对电话信道语音进行端点检测处理的应用正在不断扩展和深化。目前语音端点检测(Voice Active Detection,简称VAD)的主要任务是准确快速判决出待处理的语音信号有话还是无话,作为自动语音识别(Automatic Speech Recognition,...

weixin_36431018的博客 509

海康威视 2024届 数字逻辑设计 实习笔试分析

记录一下 5月11日晚,做的海康威视的一场笔试。分享给需要的IC人。 岗位:数字逻辑设计工程师(浙江 杭州) 转载需要本人同意! 我的见解不一定都是准确的,欢迎评论区交流指正~~

记录所学,分享知识,结识挚友 1万+

【系统级编程进阶指南】:掌握C++26并行算法的7个关键工程实践

掌握C++26并行算法的7个关键工程实践,解决高性能系统开发中的并发难题。适用于高吞吐服务、实时数据处理等场景,结合2025全球C++及系统软件技术大会最新进展,详解任务拆分、资源调度与异常安全等核心方法,提升系统效率与可维护性,值得收藏。

VarFlow的博客 650

(五)深入理解蓝牙BLE之“Beacon包格式详解”

蓝牙Beacon格式详解 本文重点在结合Bluetooth官网spec和几个例子来使beacon帧格式更加容易被理解。 广播帧格式: 首先beacon是蓝牙BLE广播包的一种,蓝牙广播帧格式如下: PS:其实BLE数据帧除了报头字段和数据字段定义与广播帧不同之外,前导/接入地址/长度/校验格式均相同。 广播帧数据字段格式: 数据字段最长为37个字节:6个字节的广播设备地址 + 31个字节广播数据。 广播数据的格式如下:AD Structure1 +AD Structure2 + .....

1万+

C++26并行模型曝光:为什么顶尖科技公司已提前布局?

掌握C++26并行算法工程落地方法,提升系统性能。2025全球C++及系统软件技术大会:C++26并行算法的工程应用聚焦高并发、大数据处理场景,解析并行模型优化策略与主流科技公司实践案例,揭示性能倍增关键路径,值得收藏。

InitFlow的博客 1099

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

揭秘C++26 std::execution调度机制:如何实现高效并行任务管理

掌握C++26 std::execution任务调度,高效实现并行任务管理。适用于高性能计算与并发场景,通过统一执行策略简化异步编程,提升资源利用率和代码可维护性。深入解析核心机制与最佳实践,值得收藏。

SimProceed的博客 795

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

深入C++26标准委员会内部(并行算法设计背后不为人知的取舍)

掌握C++26并行算法工程落地的关键策略,提升系统软件性能。通过2025全球C++及系统软件技术大会深度解析标准委员会设计取舍,涵盖多线程场景优化、算法并行化实践与性能瓶颈突破方案,助力高性能系统开发,值得收藏。

StepLens的博客 999

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

【GCC 14与C++26并发新特性】:深度解析即将到来的并行编程革命

掌握高效并发编程新范式,本文深入解析GCC 14对C++26并发支持的全新特性,涵盖协程、并行算法与原子操作优化,适用于高性能计算与多线程系统开发,显著提升程序响应速度与资源利用率,值得收藏。

CompiTide的博客 998

揭秘C++26 std::execution on函数:5大特性让你的并行代码性能飙升

掌握C++26 std::execution on函数,轻松提升并行代码性能。该函数支持异步执行策略,优化任务调度,适用于高并发与计算密集型场景,显著降低延迟。结合新执行器模型,实现资源高效利用。五大特性全面解析,值得收藏。

ProceChat的博客 769

java奇偶排序算法_奇偶排序

在《java高并发程序设计》一书中看到关于一种并行算法排序方法:奇偶排序。结合书上与网上的各项资料,在这里按自己的理解做下梳理。介绍冒泡排序:是串行算法,在每次迭代过程中,对于每个元素可能与前面元素交换,也可能和后面的元素交换,数据的相关性比较强很难直接改成并行算法。奇偶排序:或奇偶换位排序,或砖排序,是一种相对简单的排序算法,最初发明用于有本地互连的并行计算。这是与冒泡排序特点类似的一种比较排序...

weixin_30789053的博客 1060

std::execution实战指南,掌握C++26高性能并发编程关键技术

掌握C++26高性能并发编程,深入解析std::execution任务调度机制。涵盖并行算法、执行策略与实际应用场景,提升多核利用率与程序效率。详解任务拆分、资源管理与性能优化技巧,实战指南值得收藏。

ByteGlow的博客 942

java高并发系列 - 第26篇:学会使用JUC中常见的集合,常看看

List实现List的接口的,一般我们使用,其中只有Vector是线程安全的,可以使用Collections静态类的synchronizedList方法对ArrayList、LinkedList包装为线程安全的List,不过这些方式在保证线程安全的情况下性能都不高。CopyOnWriteArrayList是线程安全的List,内部使用数组存储数据,,所以读取的效率非常高。可以看一下这个类的名称:CopyOnWrite,意思是在写入操作的时候,进行一次自我复制,换句话说,当这个List需要修改时,并不修改原有

2401_84048832的博客 878

C++26来了,你的代码准备好迎接TB级数据实时处理了吗?

掌握C++26并行算法,高效应对TB级数据实时处理挑战。在2025全球C++及系统软件技术大会中,深入解析并行算法的工程应用,覆盖高性能计算、大规模数据流水线等场景,提升系统吞吐与响应速度,值得收藏。

QuickSolve的博客 757
上一篇: 【高并发系列】25、并行搜索
下一篇: 打赏
hellboy0621
博客等级 码龄9年 61粉丝 147原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值