多设计模式下的日志系统——(四)性能测试

之前博主实现了日志系统,实现的模块有日志等级、消息结构体、格式化消息、实际落地、日志器整合这些模块。并且利用全局的单例对日志器进行管理。后来针对同步写日志出现的问题,主要是阻塞、也有网络波动等。衍生出异步工作器,实现双缓冲区下的异步写日志系统。由于创建日志器需要多个属性,用户难以控制,引入建造者模式辅助构建日志器。另外用户创建全局的日志器,需要手动添加到单例中,为了简化操作,全局函数代理直接对单例的调用。

综上部分基于多设计模式下的同步异步日志系统就算完成了

gitee:提取项目源码

下面进行日志系统的测试部分。主要是测试写日志的性能。

测试的环境

博主的采用的腾讯云服务器编写的日志系统

云服务器的配置如下

  • CPU:2-核 
  • RAM:内存是2GB
  • ROM:SSD云硬盘 40GB
  • OS:Ubuntu Server 24.04 LTS 64bit

测试的内容

测试在单线程和多线程下同步/异步日志的输出性能

性能主要以俩种方式展现

  • 每秒输出的消息条数:(总消息条数)/(总耗时)
  • 每秒输出消息的大小MB:(总消息条数*消息长度)/(总耗时*1024*1024)

测试的方法

利用控制变量法

分别测试单线程下同步和异步写日志的性能。再测试多线程下同步和异步写日志的性能。

为了做到统一,对这些参数进行规定。

  1. 写入消息的条数:100万条
  2. 每条消息的长度:100字节
  3. 多线程的数量:5个线程

测试的程序

编写一个接口,可以控制传入的日志器类型、传入线程数量、消息数量、消息长度等

设计思路:

  1. 获取日志器
  2. 构造指定长度的消息
  3. 创建线程
  4. 开始计时
  5. 循环写指定数量的消息
  6. 结束计时
  7. 得出性能
void bench(const std::string &logger_name, size_t thr_count, size_t msg_count, size_t msg_len)
{
    // 1.获取日志器
    LoggerPtr lg = getLogger(logger_name);
    if (!lg.get())
    {
        std::cout << "不存在日志器:" << logger_name << std::endl;
        return;
    }
    // 2.创建消息
    std::string msg(msg_len - 1, 'A');
    std::cout << "线程数量:" << thr_count << "个,消息数量:" << msg_count << "条,消息总大小:" << (msg_count * msg_len) / (1024*1024) << "(MB)" << std::endl;
    // 3.创建线程
    std::vector<std::thread> threads;
    std::vector<double> cost(thr_count);
    // 计算每个线程处理多少条消息
    size_t msg_per_thr = msg_count / thr_count;
    for (int i = 0; i < thr_count; i++)
    {
        threads.emplace_back([&, i]()
                             {
            //开始计时
            auto start=std::chrono::high_resolution_clock::now();
            //开始循环写日志
            for(int j=0;j<msg_per_thr;j++){
                lg->Fatal("%s",msg.c_str());
            }
            //结束计时
            auto end=std::chrono::high_resolution_clock::now();
            std::chrono::duration<double> diff=end-start;
            cost[i]=diff.count();
            std::cout<<"线程"<<i<<"耗时:"<<diff.count()<<"s,处理"<<msg_per_thr<<"条消息\n"; });
    }
    for (auto &thr : threads)
    {
        thr.join();
    }
    double maxcost = cost[0];
    for (int i = 1; i < thr_count; i++)
    {
        maxcost = std::max(maxcost, cost[i]);
    }
    size_t msg_per_sec = msg_count / maxcost;
    size_t size_per_sec = (msg_count * msg_len) / (maxcost * 1024*1024);
    std::cout << "总耗时:" << maxcost << std::endl;
    std::cout << "每秒输出:" << msg_per_sec << "条消息" << std::endl;
    std::cout << "每秒输出:" << size_per_sec << "(MB)" << std::endl;
}

测试结果

同步单线程写100万条日志

每秒输出67万条消息,每秒输出63MB

异步单线程写100万条日志

 异步写日志的性能每秒输出55万条消息,每秒写53MB

同步多线程写100万条消息

在五个线程同步写日志的情况下,每秒输出64万条消息,每秒输出61MB

异步多线程写100万条消息

在五个多线程异步写日志的情况下,每秒输出90万条消息,每秒写入86MB消息

 

结果分析

绘制成表格

性能比较
同步单线程写异步单线程写同步多线程写异步多线程写
耗时(s)141815.511

每秒写入

(万条)

67566490
每秒输出(MB)63536186
排名2431

比较性能的优越

多线程异步写>>单线程同步写>多线程程异步写>单线程异步写

几个问题

为什么单线程同步写日志性能由于多线程同步写日志?

1.多线程存在锁的竞争与释放会消耗时间。

2.另外写磁盘是被加锁保护的,同一时间只能由一个线程写。

3.其次线程间上下文切换也需要消耗时间。

为什么单线程异步写的性能最低?

现在的OS都会用户开辟一块用户缓冲区,用户往磁盘写数据的时候,会先被拷贝到 用户缓冲区中,等到缓冲区慢了,再将数据刷到磁盘中。这是减少IO的体现,也就是是磁盘的性能到达极限(几乎不消耗时间)

异步写数据将消息由用户用户态队列拷贝到内核缓冲区。涉及到俩次申请释放锁。线程上下文切换的时间。

为什么异步多线程最快?

同步多线程写日志会存在磁盘写日志时候的锁冲突。
异步多线程写日志只需要将消息放到队列即可,不存在写日志的锁冲突。

对于同步写日志影响最大的因素是磁盘的性能,线程数多了效率反而更低,因为存在锁冲突,线程间上下文切换。

对于异步影响写日志的最大因素是CPU性能。因为异步不会去实际落地,CPU调度越快,写数据就会越多。


本项目到此结束了,这个项目运用了大量的继承与多态,对C++的学习非常有帮助。同时也涉及到很多常见的设计模式,对提高代码质量有很大的帮助。

  • 9
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

深度搜索

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值