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

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

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. 项目总结

高并发内存池项目到这里就结项了,

三层缓存结构设计的非常之巧妙,做

这个项目为了去解决某个问题,而是

去学习别人的优秀的,先进的思想


相关文章
|
1月前
|
人工智能 测试技术 项目管理
测试不再碎片化:AI智能体平台「项目资料套件」功能上线!
在实际项目中,需求文档分散、整理费时、测试遗漏等问题常困扰测试工作。霍格沃兹推出AI智能体测试平台全新功能——项目资料套件,可将多个关联文档打包管理,并一键生成测试用例,提升测试完整性与效率。支持套件创建、文档关联、编辑删除及用例生成,适用于复杂项目、版本迭代等场景,助力实现智能化测试协作,让测试更高效、更专业。
|
14天前
|
机器学习/深度学习 人工智能 自然语言处理
如何让AI更“聪明”?VLM模型的优化策略与测试方法全解析​
本文系统解析视觉语言模型(VLM)的核心机制、推理优化、评测方法与挑战。涵盖多模态对齐、KV Cache优化、性能测试及主流基准,助你全面掌握VLM技术前沿。建议点赞收藏,深入学习。
206 8
|
19天前
|
测试技术 UED 开发者
性能测试报告-用于项目的性能验证、性能调优、发现性能缺陷等应用场景
性能测试报告用于评估系统性能、稳定性和安全性,涵盖测试环境、方法、指标分析及缺陷优化建议,是保障软件质量与用户体验的关键文档。
|
3月前
|
Java 测试技术 Spring
简单学Spring Boot | 博客项目的测试
本内容介绍了基于Spring Boot的博客项目测试实践,重点在于通过测试驱动开发(TDD)优化服务层代码,提升代码质量和功能可靠性。案例详细展示了如何为PostService类编写测试用例、运行测试并根据反馈优化功能代码,包括两次优化过程。通过TDD流程,确保每项功能经过严格验证,增强代码可维护性与系统稳定性。
161 0
|
6月前
|
JSON 测试技术 API
优化你的 REST Assured 测试:设置默认主机与端口、GET 请求与断言
REST Assured 是一个强大的 Java 库,用于简化 RESTful API 测试。本文详解了其核心功能:设置默认主机和端口以减少代码重复、发起 GET 请求并验证响应结果,以及通过断言确保接口行为符合预期。同时推荐 Apipost 工具,助力开发者提升 API 测试效率,实现更高效的接口管理与团队协作。掌握这些技巧,可显著优化测试流程与代码质量。
|
3月前
|
人工智能 数据可视化 测试技术
UAT测试排程工具深度解析:让验收测试不再失控,项目稳稳上线
在系统交付节奏加快的背景下,“测试节奏混乱”已成为项目延期的主因之一。UAT测试排程工具应运而生,帮助团队结构化拆解任务、清晰分配责任、实时掌控进度,打通需求、测试、开发三方协作闭环,提升测试效率与质量。本文还盘点了2025年热门UAT工具,助力团队选型落地,告别靠表格和群聊推进测试的低效方式,实现有节奏、有章法的测试管理。
|
7月前
|
缓存 JavaScript 中间件
如何测试中间件优化后的 Pinia 状态管理?
如何测试中间件优化后的 Pinia 状态管理?
193 64
|
8月前
|
人工智能 自然语言处理 测试技术
Potpie.ai:比Copilot更狠!这个AI直接接管项目代码,自动Debug+测试+开发全搞定
Potpie.ai 是一个基于 AI 技术的开源平台,能够为代码库创建定制化的工程代理,自动化代码分析、测试和开发任务。
605 19
Potpie.ai:比Copilot更狠!这个AI直接接管项目代码,自动Debug+测试+开发全搞定
|
8月前
|
缓存 Java 测试技术
【01】噩梦终结flutter配安卓android鸿蒙harmonyOS 以及next调试环境配鸿蒙和ios真机调试环境-flutter项目安卓环境配置-gradle-agp-ndkVersion模拟器运行真机测试环境-本地环境搭建-如何快速搭建android本地运行环境-优雅草卓伊凡-很多人在这步就被难倒了
【01】噩梦终结flutter配安卓android鸿蒙harmonyOS 以及next调试环境配鸿蒙和ios真机调试环境-flutter项目安卓环境配置-gradle-agp-ndkVersion模拟器运行真机测试环境-本地环境搭建-如何快速搭建android本地运行环境-优雅草卓伊凡-很多人在这步就被难倒了
854 3
【01】噩梦终结flutter配安卓android鸿蒙harmonyOS 以及next调试环境配鸿蒙和ios真机调试环境-flutter项目安卓环境配置-gradle-agp-ndkVersion模拟器运行真机测试环境-本地环境搭建-如何快速搭建android本地运行环境-优雅草卓伊凡-很多人在这步就被难倒了
|
8月前
|
JSON 前端开发 API
以项目登录接口为例-大前端之开发postman请求接口带token的请求测试-前端开发必学之一-如果要学会联调接口而不是纯写静态前端页面-这个是必学-本文以优雅草蜻蜓Q系统API为实践来演示我们如何带token请求接口-优雅草卓伊凡
以项目登录接口为例-大前端之开发postman请求接口带token的请求测试-前端开发必学之一-如果要学会联调接口而不是纯写静态前端页面-这个是必学-本文以优雅草蜻蜓Q系统API为实践来演示我们如何带token请求接口-优雅草卓伊凡
349 5
以项目登录接口为例-大前端之开发postman请求接口带token的请求测试-前端开发必学之一-如果要学会联调接口而不是纯写静态前端页面-这个是必学-本文以优雅草蜻蜓Q系统API为实践来演示我们如何带token请求接口-优雅草卓伊凡