手写单向队列性能秒杀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中分析SQL性能的两种方式详解

《Springboot中分析SQL性能的两种方式详解》文章介绍了SQL性能分析的两种方式:MyBatis-Plus性能分析插件和p6spy框架,MyBatis-Plus插件配置简单,适用于开发和测试环... 目录SQL性能分析的两种方式:功能介绍实现方式:实现步骤:SQL性能分析的两种方式:功能介绍记录

如何通过Python实现一个消息队列

《如何通过Python实现一个消息队列》这篇文章主要为大家详细介绍了如何通过Python实现一个简单的消息队列,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录如何通过 python 实现消息队列如何把 http 请求放在队列中执行1. 使用 queue.Queue 和 reque

c++中std::placeholders的使用方法

《c++中std::placeholders的使用方法》std::placeholders是C++标准库中的一个工具,用于在函数对象绑定时创建占位符,本文就来详细的介绍一下,具有一定的参考价值,感兴... 目录1. 基本概念2. 使用场景3. 示例示例 1:部分参数绑定示例 2:参数重排序4. 注意事项5.

Tomcat高效部署与性能优化方式

《Tomcat高效部署与性能优化方式》本文介绍了如何高效部署Tomcat并进行性能优化,以确保Web应用的稳定运行和高效响应,高效部署包括环境准备、安装Tomcat、配置Tomcat、部署应用和启动T... 目录Tomcat高效部署与性能优化一、引言二、Tomcat高效部署三、Tomcat性能优化总结Tom

解读Redis秒杀优化方案(阻塞队列+基于Stream流的消息队列)

《解读Redis秒杀优化方案(阻塞队列+基于Stream流的消息队列)》该文章介绍了使用Redis的阻塞队列和Stream流的消息队列来优化秒杀系统的方案,通过将秒杀流程拆分为两条流水线,使用Redi... 目录Redis秒杀优化方案(阻塞队列+Stream流的消息队列)什么是消息队列?消费者组的工作方式每

C#使用yield关键字实现提升迭代性能与效率

《C#使用yield关键字实现提升迭代性能与效率》yield关键字在C#中简化了数据迭代的方式,实现了按需生成数据,自动维护迭代状态,本文主要来聊聊如何使用yield关键字实现提升迭代性能与效率,感兴... 目录前言传统迭代和yield迭代方式对比yield延迟加载按需获取数据yield break显式示迭

Redis延迟队列的实现示例

《Redis延迟队列的实现示例》Redis延迟队列是一种使用Redis实现的消息队列,本文主要介绍了Redis延迟队列的实现示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习... 目录一、什么是 Redis 延迟队列二、实现原理三、Java 代码示例四、注意事项五、使用 Redi

Java实现任务管理器性能网络监控数据的方法详解

《Java实现任务管理器性能网络监控数据的方法详解》在现代操作系统中,任务管理器是一个非常重要的工具,用于监控和管理计算机的运行状态,包括CPU使用率、内存占用等,对于开发者和系统管理员来说,了解这些... 目录引言一、背景知识二、准备工作1. Maven依赖2. Gradle依赖三、代码实现四、代码详解五

正则表达式高级应用与性能优化记录

《正则表达式高级应用与性能优化记录》本文介绍了正则表达式的高级应用和性能优化技巧,包括文本拆分、合并、XML/HTML解析、数据分析、以及性能优化方法,通过这些技巧,可以更高效地利用正则表达式进行复杂... 目录第6章:正则表达式的高级应用6.1 模式匹配与文本处理6.1.1 文本拆分6.1.2 文本合并6

C++11的函数包装器std::function使用示例

《C++11的函数包装器std::function使用示例》C++11引入的std::function是最常用的函数包装器,它可以存储任何可调用对象并提供统一的调用接口,以下是关于函数包装器的详细讲解... 目录一、std::function 的基本用法1. 基本语法二、如何使用 std::function