手写单向队列性能秒杀std::queue

2024-06-18 05:32

本文主要是介绍手写单向队列性能秒杀std::queue,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

std::queue即单向队列,是一种先入先出的FIFO队列。具有以下特点:

  • 只允许从队尾插入元素,从队头删除元素
  • 先进先出(First In First Out)
  • 不允许在中间部位进行操作

一共6个函数front()、back()、push()、pop()、empty()、size(),自己手写实现,也是比较简单的。

接下来, 我们就手写实现一个定制的queue队列,然后将其与std::queue性能进行对比。

一、IORequestQueue队列类实现

IORequestQueue.h

#ifndef IOREQUESTQUEUE_H
#define IOREQUESTQUEUE_H#include <assert.h>struct IORequest
{IORequest* p;int data;int ioType;unsigned int requestIndex;
};class IORequestQueue
{
public:IORequestQueue(): pHead(nullptr),pTail(nullptr),count(0){}// 队列是否为空bool empty() const{return (pHead == nullptr);}// 返回队列中元素个数size_t size() const{return count;}// 返回队头元素IORequest* front(){assert(!empty());return pHead;}// 返回队尾元素IORequest* back(){assert(!empty());return pTail;}// 将变量request从队列尾入队void push(IORequest* request){request->p = nullptr;if (pHead == nullptr){assert(pTail == nullptr);pHead = request;}else{assert(pTail != nullptr);pTail->p = request;}pTail = request;count++;}// 将队头元素弹出void pop(){assert(!empty());pHead = pHead->p;if (pHead == nullptr){pTail = nullptr;}count--;}private:IORequest* pHead;IORequest* pTail;size_t count;
};#endif // IOREQUESTQUEUE_H

struct IORequest结构体是需要加入到队列中的元素类型,其中IORequest* p为指向下一个元素的指针,其余均为测试成员变量。

IORequestQueue类是队列实现,分别参考std::queue实现了6个函数,基本原理是记录首尾元素的地址,其间各元素之间依靠IORequest* p进行连接。

二、IORequestQueue与std::queue性能对比

对IORequestQueue与std::queue进行测试,main.cpp如下:

#include <QCoreApplication>
#include <queue>
#include <QDebug>
#include "IORequestQueue.h"
#include "CTimer.h"void testStdQueue(std::vector<IORequest>& requests)
{/***********************测试入队*************************/CTimer timer;timer.reset();std::queue<IORequest*> stdQueue;for (unsigned int i = 0; i < requests.size(); i++) // 将IO请求添加到std::queue队列中{stdQueue.push(&requests[i]);}double elapsed = timer.end();qDebug() << "std::queue push:" << elapsed << "us";/***********************测试出队*************************/timer.reset();for (unsigned int i = 0; i < requests.size(); i++) // 从std::queue出队列{IORequest* req = stdQueue.front(); // 返回队头元素stdQueue.pop(); // 将队头元素弹出}elapsed = timer.end();qDebug() << "std::queue pop:" << elapsed << "us";
}void testIOQueue(std::vector<IORequest>& requests)
{/***********************测试入队*************************/CTimer timer;timer.reset();IORequestQueue ioQueue;for (unsigned int i = 0; i < requests.size(); i++) // 将IO请求添加到IORequestQueue队列中{ioQueue.push(&requests[i]);}double elapsed = timer.end();qDebug() << "IORequestQueue push:" << elapsed << "us";/***********************测试出队*************************/timer.reset();for (unsigned int i = 0; i < requests.size(); i++) // 从IORequestQueue出队列{IORequest* req = ioQueue.front(); // 返回队头元素ioQueue.pop(); // 将队头元素弹出}elapsed = timer.end();qDebug() << "IORequestQueue pop:" << elapsed << "us";
}int main(int argc, char *argv[])
{QCoreApplication a(argc, argv);// 准备测试元素10000个std::vector<IORequest> requests;for (unsigned int index = 0; index < 10000; index++){IORequest req;req.data = 0;req.ioType = 0;req.requestIndex = index;requests.push_back(req);}testStdQueue(requests); // 测试std::queuetestIOQueue(requests);  // 测试IORequestQueuereturn a.exec();
}

程序中分别对10000个元素,使用std::queue进行入队和出队,使用IORequestQueue进行入队和出队,并记录每个步骤消耗的时间。

运行结果:

在这里插入图片描述

可以看到同样入队10000个元素时:

  • std::queue消耗173062 us
  • IORequestQueue消耗499.2 us

IORequestQueue性能比std::queue提升99.7%

同样出队10000个元素时:

  • std::queue消耗2338 us
  • IORequestQueue消耗274.9 us

IORequestQueue性能比std::queue提升88.2%

然而,这还只是MSVC编译器,debug版本下的测试结果,release下提升更多。

三、总结

std::queue由于使用模板,适用于各种类型,另外其底下使用deque(double-ended queue,双端队列)实现,可能在性能上,有一些下降。

在性能和类型通用性上有一些兼顾,故而导致性能不及为某一特定类型元素定制的queue,也可以理解。

建议:

  • 在频繁入队、出队的场合下,尽量使用自己手写实现的queue,这样性能损失较少,并在业务代码中,尽量复用元素对象,避免频繁申请和释放内存。可参考IORequestQueue实现,比较简单。

  • 在非频繁入队、出队的场合下,使用std::queue,适用于各种类型,代码实现更方便。



若对你有帮助,欢迎点赞、收藏、评论,你的支持就是我的最大动力!!!

同时,阿超为大家准备了丰富的学习资料,欢迎关注公众号“超哥学编程”,即可领取。

本文涉及工程代码,公众号回复:20Queue,即可下载。

在这里插入图片描述

这篇关于手写单向队列性能秒杀std::queue的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1071428

相关文章

SpringBoot集成redisson实现延时队列教程

《SpringBoot集成redisson实现延时队列教程》文章介绍了使用Redisson实现延迟队列的完整步骤,包括依赖导入、Redis配置、工具类封装、业务枚举定义、执行器实现、Bean创建、消费... 目录1、先给项目导入Redisson依赖2、配置redis3、创建 RedissonConfig 配

RabbitMQ 延时队列插件安装与使用示例详解(基于 Delayed Message Plugin)

《RabbitMQ延时队列插件安装与使用示例详解(基于DelayedMessagePlugin)》本文详解RabbitMQ通过安装rabbitmq_delayed_message_exchan... 目录 一、什么是 RabbitMQ 延时队列? 二、安装前准备✅ RabbitMQ 环境要求 三、安装延时队

从原理到实战解析Java Stream 的并行流性能优化

《从原理到实战解析JavaStream的并行流性能优化》本文给大家介绍JavaStream的并行流性能优化:从原理到实战的全攻略,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的... 目录一、并行流的核心原理与适用场景二、性能优化的核心策略1. 合理设置并行度:打破默认阈值2. 避免装箱

深入解析C++ 中std::map内存管理

《深入解析C++中std::map内存管理》文章详解C++std::map内存管理,指出clear()仅删除元素可能不释放底层内存,建议用swap()与空map交换以彻底释放,针对指针类型需手动de... 目录1️、基本清空std::map2️、使用 swap 彻底释放内存3️、map 中存储指针类型的对象

深度剖析SpringBoot日志性能提升的原因与解决

《深度剖析SpringBoot日志性能提升的原因与解决》日志记录本该是辅助工具,却为何成了性能瓶颈,SpringBoot如何用代码彻底破解日志导致的高延迟问题,感兴趣的小伙伴可以跟随小编一起学习一下... 目录前言第一章:日志性能陷阱的底层原理1.1 日志级别的“双刃剑”效应1.2 同步日志的“吞吐量杀手”

spring AMQP代码生成rabbitmq的exchange and queue教程

《springAMQP代码生成rabbitmq的exchangeandqueue教程》使用SpringAMQP代码直接创建RabbitMQexchange和queue,并确保绑定关系自动成立,简... 目录spring AMQP代码生成rabbitmq的exchange and 编程queue执行结果总结s

Java慢查询排查与性能调优完整实战指南

《Java慢查询排查与性能调优完整实战指南》Java调优是一个广泛的话题,它涵盖了代码优化、内存管理、并发处理等多个方面,:本文主要介绍Java慢查询排查与性能调优的相关资料,文中通过代码介绍的非... 目录1. 事故全景:从告警到定位1.1 事故时间线1.2 关键指标异常1.3 排查工具链2. 深度剖析:

深入解析Java NIO在高并发场景下的性能优化实践指南

《深入解析JavaNIO在高并发场景下的性能优化实践指南》随着互联网业务不断演进,对高并发、低延时网络服务的需求日益增长,本文将深入解析JavaNIO在高并发场景下的性能优化方法,希望对大家有所帮助... 目录简介一、技术背景与应用场景二、核心原理深入分析2.1 Selector多路复用2.2 Buffer

基于Python Playwright进行前端性能测试的脚本实现

《基于PythonPlaywright进行前端性能测试的脚本实现》在当今Web应用开发中,性能优化是提升用户体验的关键因素之一,本文将介绍如何使用Playwright构建一个自动化性能测试工具,希望... 目录引言工具概述整体架构核心实现解析1. 浏览器初始化2. 性能数据收集3. 资源分析4. 关键性能指

Zabbix在MySQL性能监控方面的运用及最佳实践记录

《Zabbix在MySQL性能监控方面的运用及最佳实践记录》Zabbix通过自定义脚本和内置模板监控MySQL核心指标(连接、查询、资源、复制),支持自动发现多实例及告警通知,结合可视化仪表盘,可有效... 目录一、核心监控指标及配置1. 关键监控指标示例2. 配置方法二、自动发现与多实例管理1. 实践步骤