【项目日记(九)】项目整体测试,优化以及缺陷分析

news/2024/4/25 18:05:42

💓博主CSDN主页:杭电码农-NEO💓

⏩专栏分类:项目日记-高并发内存池⏪

🚚代码仓库:NEO的学习日记🚚

🌹关注我🫵带你做项目
  🔝🔝
开发环境: Visual Studio 2022


在这里插入图片描述

项目日记

  • 1. 前言
  • 2. 整体项目测试
  • 3. 项目的效率上限分析
  • 4. 效率上限问题的解决方法
  • 5. 项目的缺陷分析
  • 6. 项目总结

1. 前言

整个项目的代码和框架就已经介绍
完毕了,项目的所有代码在下面的链接:

gitee代码仓库项目源代码

本章重点:

本篇文章着重讲解本项目是如何测试的,
以及本代码的一些效率上限问题,最后会
引入基数树来对项目整体做优化


2. 整体项目测试

对本项目的测试无非就是将自己写的
内存池与C语言的malloc做对比,代码如下:

#include<cstdio>
#include<iostream>
#include<vector>
#include<thread>
#include<mutex>
#include"ConcurrentAlloc.h"
using namespace std;
void BenchmarkMalloc(size_t ntimes, size_t nworks, size_t rounds)//ntime一轮申请和释放内存的次数,round是跑多少轮,nworks是线程数
{std::vector<std::thread> vthread(nworks);std::atomic<size_t> malloc_costtime = 0;std::atomic<size_t> free_costtime = 0;for (size_t k = 0; k < nworks; ++k){vthread[k] = std::thread([&, k]() {std::vector<void*> v;v.reserve(ntimes);for (size_t j = 0; j < rounds; ++j){size_t begin1 = clock();for (size_t i = 0; i < ntimes; i++){//v.push_back(malloc(16));v.push_back(malloc((16 + i) % 8192 + 1));}size_t end1 = clock();size_t begin2 = clock();for (size_t i = 0; i < ntimes; i++){free(v[i]);}size_t end2 = clock();v.clear();malloc_costtime += (end1 - begin1);free_costtime += (end2 - begin2);}});}for (auto& t : vthread){t.join();}printf("%u个线程并发执行%u轮次,每轮次malloc %u次: 花费:%u ms\n",nworks, rounds, ntimes, malloc_costtime.load());printf("%u个线程并发执行%u轮次,每轮次free %u次: 花费:%u ms\n",nworks, rounds, ntimes, free_costtime.load());printf("%u个线程并发malloc&free %u次,总计花费:%u ms\n",nworks, nworks * rounds * ntimes, malloc_costtime.load() + free_costtime.load());
}// 单轮次申请释放次数 线程数 轮次
void BenchmarkConcurrentMalloc(size_t ntimes, size_t nworks, size_t rounds)
{std::vector<std::thread> vthread(nworks);std::atomic<size_t> malloc_costtime = 0;std::atomic<size_t> free_costtime = 0;for (size_t k = 0; k < nworks; ++k){vthread[k] = std::thread([&]() {std::vector<void*> v;v.reserve(ntimes);for (size_t j = 0; j < rounds; ++j){size_t begin1 = clock();for (size_t i = 0; i < ntimes; i++){//v.push_back(ConcurrentAlloc(16));v.push_back(ConcurrentAlloc((16 + i) % 8192 + 1));}size_t end1 = clock();size_t begin2 = clock();for (size_t i = 0; i < ntimes; i++){ConcurrentFree(v[i]);}size_t end2 = clock();v.clear();malloc_costtime += (end1 - begin1);free_costtime += (end2 - begin2);}});}for (auto& t : vthread){t.join();}printf("%u个线程并发执行%u轮次,每轮次concurrent alloc %u次: 花费:%u ms\n",nworks, rounds, ntimes, malloc_costtime.load());printf("%u个线程并发执行%u轮次,每轮次concurrent dealloc %u次: 花费:%u ms\n",nworks, rounds, ntimes, free_costtime.load());printf("%u个线程并发concurrent alloc&dealloc %u次,总计花费:%u ms\n",nworks, nworks * rounds * ntimes, malloc_costtime.load() + free_costtime.load());
}
int main()
{size_t n = 10000;cout << "==========================================================" << endl;BenchmarkConcurrentMalloc(n, 10, 10);cout << endl << endl;BenchmarkMalloc(n, 10, 10);cout << "==========================================================" <<endl;return 0;
}

本代码是现成的,不用在意细节

当我们运行代码后会发现,为什么我们自己写的内存池的效率比不上C语言中的malloc函数,这一点显然超出了我们的预期,下面就来分析一下项目的效率上限问题

在这里插入图片描述


3. 项目的效率上限分析

在vs的调试中有一个性能探测器

在这里插入图片描述

我们可以使用这个功能来分析哪个步骤比较用时,当我们完成检测后会发现,在pagecache文件中的函数耗时都比较久,其实我们隐约已经知道问题出现在哪里了,我们知道unordered_map的底层是哈希桶结构,然而find函数会将每一个桶中的链表都遍历一遍,直到找到了对应的key值,很明显这个查找的过程是比较费时的,并且如果不切换一个容器来代替unordered_map的话,在这个基础上不管怎样去优化都不会有质的提升!!!


4. 效率上限问题的解决方法

对于上面的问题显然超出了我们的能力范围,对于一个C++的初学者来说,标准库中的容器已经是很优秀的了,如果要抛弃标准库,我们也不能写出更好的,所以这里直接将TCmalloc开源项目中的解决方法给搬过来,谷歌的团队使用了一个叫基数树的结构来完美的解决此问题

基数树的文档说明: 基数树百度百科

由于基数树属于此项目的拓展内容,所以这里就不详细介绍了,完美直接把代码搬出来用就可以了!

#pragma once
#include"shared.h"
// Single-level array
template <int BITS>
class TCMalloc_PageMap1 {
private:static const int LENGTH = 1 << BITS;void** array_;public:typedef uintptr_t Number;//explicit TCMalloc_PageMap1(void* (*allocator)(size_t)) {explicit TCMalloc_PageMap1() {//array_ = reinterpret_cast<void**>((*allocator)(sizeof(void*) << BITS));size_t size = sizeof(void*) << BITS;size_t alignSize = AlignmentRule::_AlignUp(size, 1 << PAGE_SHIFT);array_ = (void**)SystemAlloc(alignSize >> PAGE_SHIFT);memset(array_, 0, sizeof(void*) << BITS);}// Return the current value for KEY.  Returns NULL if not yet set,// or if k is out of range.void* get(Number k) const {if ((k >> BITS) > 0) {return NULL;}return array_[k];}// REQUIRES "k" is in range "[0,2^BITS-1]".// REQUIRES "k" has been ensured before.// Sets the value 'v' for key 'k'.void set(Number k, void* v) {array_[k] = v;}
};

之后将所有使用unordered_map的地方都替换成基数树的get和set函数即可!现在我们再来测试一下整个项目的性能如何:

在这里插入图片描述

使用基数树后,整个效率就比malloc快了!

在这里插入图片描述


5. 项目的缺陷分析

本项目看似每一步都做的天衣无缝,申请
和释放内存一层一层不断递进,但是它有
一个致命的缺陷,那就是内存泄漏问题:

bug出现的情景:

假设线程缓存的K号桶中有10个小块儿内存挂在桶上,此时K号桶向中心缓存申请的小块儿内存个数是7个,小于了桶中小块儿内存的个数,此时会将线程缓存中的7个小块儿内存还给中心缓存,那么也就还剩下三个小块儿内存在桶中没有被还回去,此时如果没有线程来这个桶中申请或释放内存,那么这三块儿内存就会一直挂在桶上,既无法释放它,又失去了对它的控制从而造成内存泄漏!

解决bug的方式:

博主本人比较推荐的方式就是在每次使用完内存池后,手动调用一个释放内存的函数对每一个桶进行遍历,来释放还没有被使用的小块儿内存


6. 项目总结

高并发内存池项目到这里就结项了,
三层缓存结构设计的非常之巧妙,做
这个项目为了去解决某个问题,而是
去学习别人的优秀的,先进的思想


https://www.xjx100.cn/news/3271456.html

相关文章

C#,21根火柴棍问题(21 Matchticks Problem)的算法与源代码

一、21根火柴棍问题&#xff08;21 Matchticks Problem&#xff09; 21根火柴棍问题是西方经典游戏之一。 给定21根火柴&#xff0c;2个人A和B&#xff08;比如&#xff1a;分别是计算机和用户&#xff09;。 每个人一次可以挑选 1-- 4 根火柴。 被迫挑最后一根火柴的人输了…

java中ArrayList类常用API

前言&#xff1a;在学习java的ArrayList类的时候&#xff0c;有很多的API需要了解&#xff0c;下面我将举出其中在新手学习时使用频率较大的几个API。 先大体看一下有哪几个&#xff1a;&#xff08;如图&#xff09; 目录 1.add&#xff08;&#xff09; 解释&#xff1a; …

Ubuntu Desktop - Terminal 输出全部选中 + 复制

Ubuntu Desktop - Terminal 输出全部选中 复制 1. Terminal2. Terminal 最大化3. Edit -> Select All4. Copy & PasteReferences 1. Terminal 2. Terminal 最大化 3. Edit -> Select All 4. Copy & Paste Edit -> Copy or Shift Ctrl C Edit -> Paste…

时间序列预测——BiGRU模型

时间序列预测——BiGRU模型 时间序列预测是指根据历史数据的模式来预测未来时间点的值或趋势的过程。在深度学习领域&#xff0c;循环神经网络&#xff08;Recurrent Neural Networks, RNNs&#xff09;是常用于时间序列预测的模型之一。在RNNs的基础上&#xff0c;GRU&#x…

幻兽帕鲁 Linux 服务器迁移完成之后,进入游戏会出现闪退?怎么解决?

主要的原因是迁移的存档文件&#xff0c;新服务器可能没有操作存档文件的权限&#xff0c;不能成功更新存档&#xff0c;从而导致闪退。 建议&#xff1a;在 Linux 服务器内&#xff0c;依次运行如下命令后&#xff0c;再次尝试连接游戏&#xff1a; 第一步&#xff1a; s…

119.乐理基础-五线谱-五线谱的标记

内容参考于&#xff1a;三分钟音乐社 上一个内容&#xff1a;音值组合法&#xff08;二&#xff09; 力度记号&#xff1a;简谱里什么意思&#xff0c;五线谱也完全是什么意思&#xff0c;p越多就越弱&#xff0c;f越多就越强&#xff0c;然后这些渐强、渐弱、sf、fp这些标记…

C# EventHandler<T> 示例

新建一个form程序&#xff0c;在调试窗口输出执行过程&#xff1b; 为了使用Debug.WriteLine&#xff0c;添加 using System.Diagnostics; using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using S…