比较日志性能:Glog、Spdlog 和 ofstream 在不同硬件上的表现(推荐Spdlog)

2024-06-19 23:36

本文主要是介绍比较日志性能:Glog、Spdlog 和 ofstream 在不同硬件上的表现(推荐Spdlog),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

  • 比较日志性能:Glog、Spdlog 和 ofstream 在不同硬件上的表现
    • 1. 引言
    • 2. 测试简介
    • 3. 硬件配置
      • 桌面电脑(Ubuntu 18.04)
      • 树莓派 5(Ubuntu 24.04)
    • 4. 测试结果
      • 桌面电脑(Ubuntu 18.04)
      • 树莓派 5(Ubuntu 24.04)
    • 5. 详细分析
    • 6. 实现代码
    • 7. 其他
      • 7.1 Spdlog 的同步和异步支持
      • 7.2 Glog 的同步和异步
      • 7.3 Glog 的多线程和多进程写入顺序问题
    • 8. 结论
      • 8.1 测试版本差异

比较日志性能:Glog、Spdlog 和 ofstream 在不同硬件上的表现

1. 引言

日志记录是嵌入式系统中关键的调试和监控手段。在选择合适的日志库时,性能往往是重要的考量因素。本文对比了 Spdlog 和 Glog 这两个流行的日志库,以及标准的 ofstream 流在嵌入式平台上的性能表现。测试包括同步和异步模式,以全面评估它们在高频日志记录场景下的性能表现。

2. 测试简介

为了比较 Spdlog 和 Glog 在嵌入式 ARM64 平台上的性能表现,设计了测试代码,分别测试了以下场景下的日志记录耗时:

  • ofstream 同步文件写
  • Spdlog 同步日志记录
  • Spdlog 异步日志记录
  • Glog 同步日志记录

测试使用 10 万条日志数据,每个日志记录 200 字节的消息。测试平台包括桌面电脑(Ubuntu 18.04)和树莓派 5(Ubuntu 24.04)。

3. 硬件配置

桌面电脑(Ubuntu 18.04)

  • CPU:AMD Ryzen 5 3500U with Radeon Vega Mobile Gfx
  • 内存:4.8G
  • 操作系统:Ubuntu 18.04.4 LTS
  • GCC 版本:7.5.0
  • G++ 版本:7.5.0

树莓派 5(Ubuntu 24.04)

  • CPU:Cortex-A76
  • 内存:7.8Gi
  • 操作系统:Ubuntu 24.04 LTS
  • GCC 版本:13.2.0
  • G++ 版本:13.2.0

4. 测试结果

桌面电脑(Ubuntu 18.04)

ofstream logging: 0.0719351 seconds
spdlog sync logging: 0.0720132 seconds
spdlog async logging: 0.160619 seconds
glog logging: 0.297419 seconds

树莓派 5(Ubuntu 24.04)

ofstream logging: 0.390023 seconds
spdlog sync logging: 0.0649371 seconds
spdlog async logging: 0.1108 seconds
glog logging: 0.953905 seconds

测试结果表明,在同步日志记录场景下,ofstream 方式的日志记录耗时最短,其次是 Spdlog 同步日志记录,Glog 同步日志记录耗时最长。Spdlog 异步日志记录耗时比 Spdlog 同步日志记录高出约 2 倍

5. 详细分析

  • ofstream:作为标准的文件写操作,其性能表现优异,但在嵌入式系统中,频繁的文件读写操作可能会对系统整体性能造成一定影响。

  • spdlog:在同步场景下通过原子操作和锁机制保证线程安全,相比 Glog 的单线程模型,性能有所提升。Spdlog 的异步日志记录采用多线程和消息队列的方式,将日志记录操作与主线程分离,有效降低了对主线程的影响,但也带来了一定的性能开销。

  • glog:同步日志记录存在性能问题,可能与其单线程模型以及日志格式化等因素有关。Glog 的异步日志记录并未进行测试,因为标准 Glog 库不直接支持异步日志记录。

6. 实现代码

#include <glog/logging.h>
#include <spdlog/async.h>
#include <spdlog/sinks/basic_file_sink.h>
#include <spdlog/spdlog.h>#include <chrono>
#include <cstdio>  // for std::remove
#include <fstream>
#include <iostream>
#include <memory>
#include <string>
#include <vector>const int LOG_COUNT = 100000;
const int MESSAGE_SIZE = 200;std::vector<std::string> generate_messages(int count, int size) {std::vector<std::string> messages(count);std::string sample_message(size, 'x');for (int i = 0; i < count; ++i) {messages[i] = "This is a test log message number " + std::to_string(i) + ": " + sample_message;}return messages;
}void initialize_logging() {FLAGS_log_dir = ".";FLAGS_logtostderr = false;FLAGS_alsologtostderr = false;google::InitGoogleLogging("glog_async_worker");spdlog::init_thread_pool(8192, 1);  // Assuming 1 background thread for the example
}void shutdown_logging() {google::ShutdownGoogleLogging();spdlog::shutdown();
}void prepare_log_files() {// Create or truncate the log filesstd::ofstream("ofstream_log.txt").close();std::ofstream("spdlog_sync.txt").close();std::ofstream("spdlog_async.txt").close();std::ofstream("glog_example.INFO").close();
}void test_logging(const std::string& test_name, std::function<void(const std::vector<std::string>&)> log_function,const std::vector<std::string>& messages) {auto start = std::chrono::high_resolution_clock::now();log_function(messages);auto end = std::chrono::high_resolution_clock::now();std::chrono::duration<double> elapsed = end - start;std::cout << test_name << ": " << elapsed.count() << " seconds" << std::endl;
}void test_ofstream(const std::vector<std::string>& messages) {std::ofstream log_file("ofstream_log.txt");for (const auto& message : messages) {log_file << message << "\n";}
}void test_spdlog_sync(const std::vector<std::string>& messages) {auto logger = spdlog::basic_logger_mt("spdlog_sync", "spdlog_sync.txt");for (const auto& message : messages) {logger->info(message);}
}void test_spdlog_async(const std::vector<std::string>& messages) {auto logger = spdlog::basic_logger_mt<spdlog::async_factory>("spdlog_async", "spdlog_async.txt");for (const auto& message : messages) {logger->info(message);}
}void test_glog(const std::vector<std::string>& messages) {for (const auto& message : messages) {LOG(INFO) << message;}
}int main() {initialize_logging();auto messages = generate_messages(LOG_COUNT, MESSAGE_SIZE);prepare_log_files();test_logging("ofstream logging", test_ofstream, messages);prepare_log_files();test_logging("spdlog sync logging", test_spdlog_sync, messages);prepare_log_files();test_logging("spdlog async logging", test_spdlog_async, messages);prepare_log_files();test_logging("glog logging", test_glog, messages);shutdown_logging();return 0;
}

7. 其他

7.1 Spdlog 的同步和异步支持

  • Spdlog 明确支持同步和异步两种日志记录模式。
  • 在异步模式下,Spdlog 使用单独的线程处理日志写入操作,减少了日志记录对主线程的影响,特别是在高并发写入日志的场景下,能显著提高性能。

7.2 Glog 的同步和异步

  • Glog 原生设计为同步日志库,即日志记录操作在调用线程中执行。
  • 同步模式在高日志输出频率的场景中,可能会影响应用程序的性能,尤其是对响应时间有严格要求的场景。
  • 标准 Glog 库本身不直接支持异步日志记录。
  • 社区中存在一些非官方的扩展,如 G2log 和 G3log,它们基于 C++11 编写,提供了异步日志功能。

7.3 Glog 的多线程和多进程写入顺序问题

  • Glog 的设计没有明确保证多线程或多进程同时写入同一日志文件时,日志的顺序能严格按照写入时的时间顺序。
  • 高并发写入情况下,不同线程或进程的日志条目可能因操作系统调度、磁盘 I/O 缓冲等原因交错写入,导致日志顺序混乱。

8. 结论

在嵌入式 ARM64 平台上:

  • 追求极致性能:建议使用 ofstream 进行日志记录。
  • 兼顾性能和线程安全spdlog 是一个不错的选择。
  • 高性能要求场景glog 在性能方面表现不佳,不推荐用于对性能要求较高的嵌入式系统。

8.1 测试版本差异

需要注意的是,不同平台、不同版本的操作系统及编译器版本可能会对测试结果产生影响。本文测试结果基于特定版本的硬件和软件配置,其他配置可能会有不同的性能表现。

这篇关于比较日志性能:Glog、Spdlog 和 ofstream 在不同硬件上的表现(推荐Spdlog)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



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

相关文章

macOS彻底卸载Python的超完整指南(推荐!)

《macOS彻底卸载Python的超完整指南(推荐!)》随着python解释器的不断更新升级和项目开发需要,有时候会需要升级或者降级系统中的python的版本,系统中留存的Pytho版本如果没有卸载干... 目录MACOS 彻底卸载 python 的完整指南重要警告卸载前检查卸载方法(按安装方式)1. 卸载

Java 日志中 Marker 的使用示例详解

《Java日志中Marker的使用示例详解》Marker是SLF4J(以及Logback、Log4j2)提供的一个接口,它本质上是一个命名对象,你可以把它想象成一个可以附加到日志语句上的标签或戳... 目录什么是Marker?为什么使用Markejavascriptr?1. 精细化的过滤2. 触发特定操作3

linux查找java项目日志查找报错信息方式

《linux查找java项目日志查找报错信息方式》日志查找定位步骤:进入项目,用tail-f实时跟踪日志,tail-n1000查看末尾1000行,grep搜索关键词或时间,vim内精准查找并高亮定位,... 目录日志查找定位在当前文件里找到报错消息总结日志查找定位1.cd 进入项目2.正常日志 和错误日

Docker多阶段镜像构建与缓存利用性能优化实践指南

《Docker多阶段镜像构建与缓存利用性能优化实践指南》这篇文章将从原理层面深入解析Docker多阶段构建与缓存机制,结合实际项目示例,说明如何有效利用构建缓存,组织镜像层次,最大化提升构建速度并减少... 目录一、技术背景与应用场景二、核心原理深入分析三、关键 dockerfile 解读3.1 Docke

SpringBoot实现不同接口指定上传文件大小的具体步骤

《SpringBoot实现不同接口指定上传文件大小的具体步骤》:本文主要介绍在SpringBoot中通过自定义注解、AOP拦截和配置文件实现不同接口上传文件大小限制的方法,强调需设置全局阈值远大于... 目录一  springboot实现不同接口指定文件大小1.1 思路说明1.2 工程启动说明二 具体实施2

SpringBoot日志级别与日志分组详解

《SpringBoot日志级别与日志分组详解》文章介绍了日志级别(ALL至OFF)及其作用,说明SpringBoot默认日志级别为INFO,可通过application.properties调整全局或... 目录日志级别1、级别内容2、调整日志级别调整默认日志级别调整指定类的日志级别项目开发过程中,利用日志

JavaScript中比较两个数组是否有相同元素(交集)的三种常用方法

《JavaScript中比较两个数组是否有相同元素(交集)的三种常用方法》:本文主要介绍JavaScript中比较两个数组是否有相同元素(交集)的三种常用方法,每种方法结合实例代码给大家介绍的非常... 目录引言:为什么"相等"判断如此重要?方法1:使用some()+includes()(适合小数组)方法2

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

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

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

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

Python如何实现高效的文件/目录比较

《Python如何实现高效的文件/目录比较》在系统维护、数据同步或版本控制场景中,我们经常需要比较两个目录的差异,本文将分享一下如何用Python实现高效的文件/目录比较,并灵活处理排除规则,希望对大... 目录案例一:基础目录比较与排除实现案例二:高性能大文件比较案例三:跨平台路径处理案例四:可视化差异报