001 Configuration结构体构造

目录

  • DramSys 代码分析
    • 1 Configuration结构体构造
      • 1.1 `from_path` 函数详解
      • 1.2 构造过程总结
      • 这种设计的好处
    • 2 Simulator 例化过程
      • 2.1 instantiateInitiator

DramSys 代码分析

1 Configuration结构体构造

好的,我们来详细解释一下 DRAMSysConfiguration.cpp 文件中 from_path 函数的配置构造过程。这个文件是 DRAMSys 从 JSON 配置文件加载配置的关键部分。

从代码来看,DRAMSys 采用了一种非常巧妙且强大的方式来处理配置:主配置文件中可以引用(嵌入)其他子配置文件。这使得配置模块化,更易于管理。

from_path 函数主要利用了 nlohmann/json 库的强大功能,特别是它的**解析回调(parser callback)**机制,实现了在解析过程中动态加载和替换 JSON 内容。

1.1 from_path 函数详解

#include "DRAMSysConfiguration.h" // 包含配置结构体的定义
#include "DRAMSys/config/MemSpec.h" // 包含 MemSpec 相关的常量和结构体#include <fstream> // 用于文件操作
#include <nlohmann/json.hpp> // nlohmann/json 库namespace DRAMSys::Config
{// 这是核心函数,从给定路径的配置文件中构造 Configuration 对象
Configuration from_path(std::filesystem::path baseConfig)
{// 1. 打开主配置文件std::ifstream file(baseConfig); // 使用 std::ifstream 打开 JSON 文件std::filesystem::path baseDir = baseConfig.parent_path(); // 获取配置文件所在的目录,用于构建子配置文件的绝对路径// 2. 定义内部枚举类,用于识别当前正在处理的子配置类型enum class SubConfig{MemSpec,AddressMapping,McConfig,SimConfig,TraceSetup,Unkown // 未知类型} current_sub_config; // 声明一个变量来存储当前识别到的子配置类型// 3. 定义自定义解析回调函数// 这是一个 std::function 对象,它会在 nlohmann::json 解析 JSON 文件时被调用// 它的作用是在遇到特定的键(例如 "MemSpec")时,将该键对应的值(通常是子配置文件的文件名字符串)// 替换为实际解析后的子配置文件 JSON 对象。std::function<bool(int depth, nlohmann::detail::parse_event_t event, json_t& parsed)>parser_callback;parser_callback = [&parser_callback, &current_sub_config, baseDir](int depth, nlohmann::detail::parse_event_t event, json_t& parsed) -> bool{using nlohmann::detail::parse_event_t;// nlohmann::json 的解析回调会在解析 JSON 文件的不同事件(如开始对象、遇到键、遇到值等)触发// depth 表示当前解析的 JSON 深度。// event 表示触发回调的事件类型。// parsed 表示当前解析到的 JSON 值(可能是键名、字符串、数字、对象等)。// 我们只关心深度为 2 的事件。DRAMSys 的主配置文件可能在顶层(深度0)有一个总键(如"DRAMSys"),// 接着是各个子配置的键(如"MemSpec"、"AddressMapping"),这些键的值是文件的路径字符串。// 比如:// {//   "DRAMSys": {  // depth 1//     "MemSpec": "memory.json", // depth 2: "MemSpec" 是 key,"memory.json" 是 value//     "AddressMapping": "address.json",//     // ...//   }// }if (depth != 2)return true; // 如果深度不是2,则不处理,直接返回 true 继续解析// 处理“键”(key)事件if (event == parse_event_t::key){assert(parsed.is_string()); // 断言当前解析到的值是字符串(即键名)// 根据键名识别当前正在处理的子配置类型if (parsed == MemSpecConstants::KEY) // 例如 "MemSpec"current_sub_config = SubConfig::MemSpec;else if (parsed == AddressMapping::KEY) // 例如 "AddressMapping"current_sub_config = SubConfig::AddressMapping;else if (parsed == McConfig::KEY) // 例如 "McConfig"current_sub_config = SubConfig::McConfig;else if (parsed == SimConfig::KEY) // 例如 "SimConfig"current_sub_config = SubConfig::SimConfig;else if (parsed == TraceSetupConstants::KEY) // 例如 "TraceSetup"current_sub_config = SubConfig::TraceSetup;elsecurrent_sub_config = SubConfig::Unkown; // 未识别的键}// 处理“值”(value)事件// 只有当当前识别到的子配置类型不是未知(即我们之前识别到了一个有效的子配置键)// 并且当前事件是 value 时才进入此逻辑。if (event == parse_event_t::value && current_sub_config != SubConfig::Unkown){// 在这里,`parsed` 变量包含了子配置文件的文件名字符串(例如 "memory.json")。// 我们的目标是将这个字符串替换为实际解析后的 JSON 对象。// 定义一个 lambda 表达式 `parse_json`,用于加载和解析子 JSON 文件auto parse_json = [&parser_callback, baseDir](std::string_view sub_config_key,const std::string& filename) -> json_t{// 构建子配置文件的完整路径std::filesystem::path path{baseDir}; // 以主配置文件所在目录为基础path /= filename; // 拼接子文件名std::ifstream json_file(path); // 打开子配置文件if (!json_file.is_open())throw std::runtime_error("Failed to open file " + std::string(path)); // 错误处理:文件无法打开// 递归地解析子 JSON 文件。// 注意这里再次使用了 `parser_callback`。这意味着子配置文件中如果也包含对其他子配置文件的引用,// 也可以被这个机制处理,形成一个递归加载的过程。// `json_t::parse(json_file, parser_callback, true, true)` 会解析文件并应用回调。// `.at(sub_config_key)` 是因为子配置文件可能也有一个顶层键,例如 `{"MemSpec": {...}}`。json_t json =json_t::parse(json_file, parser_callback, true, true).at(sub_config_key);return json;};// 根据之前识别到的 `current_sub_config` 类型,调用 `parse_json` 来加载对应的子文件// 并将 `parsed` 变量(它原本是文件名字符串)替换为解析后的 JSON 对象if (current_sub_config == SubConfig::MemSpec)parsed = parse_json(MemSpecConstants::KEY, parsed);else if (current_sub_config == SubConfig::AddressMapping)parsed = parse_json(AddressMapping::KEY, parsed);else if (current_sub_config == SubConfig::McConfig)parsed = parse_json(McConfig::KEY, parsed);else if (current_sub_config == SubConfig::SimConfig)parsed = parse_json(SimConfig::KEY, parsed);else if (current_sub_config == SubConfig::TraceSetup)parsed = parse_json(TraceSetupConstants::KEY, parsed);}return true; // 返回 true 继续解析过程};// 4. 开始解析主配置文件if (file.is_open()){// 调用 nlohmann::json 的 parse 函数,传入文件流和自定义的 parser_callback// `true, true` 参数表示:// 第一个 true: allow_exceptions - 允许抛出解析异常// 第二个 true: ignore_comments - 忽略 JSON 中的注释// `.at(Configuration::KEY)`: 主配置文件可能有一个顶层键(例如 "DRAMSys"),我们需要进入这个键对应的对象。json_t simulation = json_t::parse(file, parser_callback, true, true).at(Configuration::KEY);// 5. 将最终解析得到的完整 JSON 对象(包含了所有内嵌子配置)// 反序列化为 DRAMSys::Config::Configuration C++ 结构体。// 这需要 Configuration 结构体及其所有嵌套结构体(如 MemSpec、AddressMapping 等)// 都使用了 nlohmann::json 的 `NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE` 或类似的机制进行了序列化/反序列化定义。return simulation.get<Config::Configuration>();}// 6. 文件打开失败的错误处理throw std::runtime_error("Failed to open file " + std::string(baseConfig));
}} // namespace DRAMSys::Config

1.2 构造过程总结

  1. 打开主配置文件: 函数首先尝试打开 baseConfig 指定的主 JSON 配置文件。
  2. 获取基础目录: 记录主配置文件的父目录 baseDir,这对于构建子配置文件的相对路径至关重要。
  3. 定义 SubConfig 枚举: 这是一个内部枚举,用于在解析过程中识别当前处理的是哪种类型的子配置(例如 MemSpec、AddressMapping 等)。
  4. 核心:自定义解析回调 parser_callback
    • 这个回调函数是整个机制的核心。它会在 nlohmann::json 解析 JSON 文件时,针对不同的事件(如遇到键、遇到值)和深度被调用。
    • 识别子配置键: 当解析深度为 2 且事件为 key 时(例如解析到 "MemSpec"),回调会根据键名设置 current_sub_config 变量,以识别当前要加载的子配置类型。
    • 替换文件名字符串为 JSON 对象: 当解析深度为 2 且事件为 value 时(此时 parsed 变量是子配置文件的文件名字符串,例如 "memory.json"),回调会执行以下操作:
      • 构建子配置文件的完整路径(baseDir + filename)。
      • 递归调用 json_t::parse 打开并解析这个子配置文件。关键在于,这里再次传入了 parser_callback。这意味着如果子配置文件内部也引用了其他子配置文件,这个机制可以递归地处理它们,实现多层级的配置嵌套。
      • 从解析后的子 JSON 中提取出对应子配置根键下的内容(例如 {"MemSpec": {...}}{...} 部分)。
      • parsed 变量(原始的文件名字符串)替换为这个新解析出来的子 JSON 对象。
  5. 启动主文件解析: 调用 json_t::parse(file, parser_callback, true, true) 来启动对主配置文件的解析,并将 parser_callback 应用于整个解析过程。
  6. 反序列化为 C++ 对象: 一旦整个 JSON 对象(包括所有内嵌的子配置)被成功解析和组装,最后一步是调用 .get<Config::Configuration>()。这会将完整的 nlohmann::json::json_t 对象反序列化(deserialize)成 DRAMSys::Config::Configuration 结构体的实例。这要求 Configuration 结构体及其所有成员(例如 MemSpec, AddressMapping, McConfig 等结构体)都必须使用 nlohmann/json 提供的宏(如 NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE)或者自定义的 to_json/from_json 函数进行了注册,以便 nlohmann/json 知道如何将 JSON 数据映射到 C++ 类型。

这种设计的好处

  • 模块化和可重用性: 不同的子配置(如内存规格、地址映射)可以存储在独立的文件中,便于管理和在不同项目中重用。
  • 清晰的结构: 主配置文件可以像目录一样,组织和引用各个部分的配置。
  • 灵活性: 支持多层级的配置嵌套。
  • 解耦: 允许配置文件的编写者专注于特定模块的参数,而不需要在一个巨大的文件中定义所有内容。
  • 易于扩展: 当需要添加新的配置类型时,只需在 SubConfig 枚举中添加新类型,并在回调中增加相应的处理逻辑即可。

这个 DRAMSysConfiguration.cpp 文件展示了 nlohmann/json 库在复杂配置管理方面的强大应用,特别是其解析回调机制,使得实现动态加载和组装配置成为可能。

2 Simulator 例化过程

  1. 首先通过teminateInitiator设置了终止仿真条件。
  2. 通过finishTransaction设置了仿真进度条。
  3. 关键是如何设置仿真的激励,即trace

2.1 instantiateInitiator

  
Simulator通过configure配置发起方针的initiator,这里的initiator可以有多个,有多少个取决于配置的json文件;
  
instantiateInitiator函数本身并不是一个构造函数,而是一个负责根据配置创建不同类型Initiator对象(流量发起器)的工厂方法。它根据不同的配置创建对应的流量发起器。

  
使用std::visit来处理std::variant类型的配置(这里initiator为什么是std::variant类型的配置?)

  1. 获取公共参数: 在开始创建具体的 Initiator 之前,函数首先从 dramSys 模块获取一些所有发起器都可能需要的公共参数,例如模拟内存的总大小、DRAM 接口的时钟周期以及默认的每次突发传输的字节数。

  2. std::variant 和 std::visit 的使用:

    • DRAMSys::Config::Initiator 结构体内部包含一个 std::variant 成员(通过 initiator.getVariant() 访问),这个 variant 可以持有不同类型的发起器配置(TrafficGenerator, TracePlayer, RowHammer)。

    • std::visit 是 C++17 引入的一个工具,它允许你对 std::variant 中当前激活的类型执行相应的操作。它会根据 variant 中实际存储的类型,调用 lambda 表达式中对应的 if constexpr 分支。

  3. 类型判别与实例化:

    • if constexpr 语句在编译时判断 config 的具体类型 (T)。

    • TrafficGenerator: 如果配置是 TrafficGenerator 或 TrafficGeneratorStateMachine 类型,它会直接创建并返回一个 TrafficGenerator 对象,并传入其特有的配置以及公共的模拟参数、内存管理器和回调函数。

    • TracePlayer: 如果配置是 TracePlayer 类型,函数会进一步根据轨迹文件的扩展名(.stl 或 .rstl)来确定轨迹类型(绝对时间或相对时间)。然后,它会创建一个 StlPlayer 对象(负责读取和解析轨迹文件),并将其包装在一个 SimpleInitiator 中返回。SimpleInitiator 是一个更通用的发起器模板,可以适配不同的流量源。

    • RowHammer: 如果配置是 RowHammer 类型,它会创建一个 RowHammer 对象(实现 Row Hammer 逻辑),同样将其包装在一个 SimpleInitiator 中返回。

  4. 返回 std::unique_ptr: 无论是哪种类型的发起器,instantiateInitiator 函数最终都会返回一个 std::unique_ptr。这意味着它返回一个指向基类 Initiator 的智能指针,确保了内存的安全管理和多态性,允许 Simulator 以统一的方式管理不同类型的发起器。

简而言之,instantiateInitiator 函数是一个动态的工厂,它根据配置文件中指定的确切类型,“构造”出并返回相应功能的流量发起器对象,这些对象都以 Initiator 接口的形式提供给模拟器使用。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/web/91145.shtml
繁体地址,请注明出处:http://hk.pswp.cn/web/91145.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

以太坊十年:智能合约与去中心化的崛起

以太坊10周年&#xff0c;敬开发者&#xff0c;敬构建者&#xff0c;敬还在链上的我们 以太坊即将迎来十周年纪念,作为一名在这个生态中深耕了8到9年的见证者&#xff0c;我亲历了它从一纸白皮书的构想到成长为全球领先去中心化平台的全过程。这十年间&#xff0c;以太坊经历了…

kafka 3.9.1版本: kraft + sasl+ standlone 模式完整可行安装步骤

Kafka 3.9.1 Kraft 单机模式安装 安装 OpenJDK 11 CentOS/RHEL yum install -y java-11-openjdk-develUbuntu/Debian apt install -y openjdk-11-jdk下载安装包 wget https://mirrors.aliyun.com/apache/kafka/3.9.1/kafka_2.12-3.9.1.tgz tar -zxvf kafka_2.12-3.9.1.tgz -C /…

Gitee DevOps平台深度评测:本土化优势与功能特性全面解析

Gitee DevOps平台深度评测&#xff1a;本土化优势与功能特性全面解析 在数字化转型浪潮下&#xff0c;企业软件开发流程的自动化与协作效率成为核心竞争力。作为国内领先的代码托管与DevOps平台&#xff0c;Gitee&#xff08;码云&#xff09;凭借其本土化服务与全流程支持能力…

从零开始本地化部署Dify:开源大模型应用平台搭建全指南

在AI应用开发的浪潮中&#xff0c;Dify作为一款开源的大语言模型(LLM)应用开发平台&#xff0c;正逐渐成为开发者和企业的首选工具。它巧妙地融合了后端即服务&#xff08;BaaS&#xff09;和LLMOps的理念&#xff0c;让开发者能够快速搭建生产级的生成式AI应用。无论是构建智能…

Qt 多媒体开发:音频与视频处理

Qt 多媒体模块提供了一套完整的 API&#xff0c;用于开发音频和视频处理应用。从简单的媒体播放到复杂的音视频编辑&#xff0c;Qt 都提供了相应的工具和组件。本文将从基础到高级全面解析 Qt 多媒体开发。 一、Qt 多媒体模块概述 1. 主要组件 Qt 多媒体模块包含以下核心组件&a…

Mac 专业图像处理 Pixelmator Pro

原文地址&#xff1a;Pixelmator Pro Mac 专业图像处理 Pixelmator Pro&#xff0c;是一款非常强大、美观且易于使用的图像编辑器&#xff0c;专为 Mac 设计。 采用单窗口界面、基于机器学习的智能图像编辑、自动水平检测&#xff0c;智能快速选择及更好的修复工具等功能优点…

iptables和IPVS比较

iptables 和 IPVS (IP Virtual Server) 都是 Linux 系统上用于处理网络流量的强大工具&#xff0c;但它们的设计目标、工作原理和适用场景有显著区别&#xff1a; 核心区别&#xff1a;主要目的&#xff1a; iptables&#xff1a; 核心是一个包过滤防火墙和网络地址转换工具。它…

语音识别指标计算 WER

目录 CER&#xff08;Character Error Rate&#xff09; WER Word Error Rate&#xff08;词错误率&#xff09; &#x1f9ee; WER 计算方式 &#x1f4cc; 示例 ✅ 理解要点 CER&#xff08;Character Error Rate&#xff09; 语音识别中的 CER&#xff08;Character …

【前端基础篇】JavaScript之jQuery介绍

文章目录前言JQuery基本介绍和使用方法引入依赖jQuery语法jQuery选择器jQuery事件操作元素获取/设置元素内容获取/设置元素属性获取/返回css属性添加元素删除元素总结&#xff1a;常用的jQuery方法 - 详细解释与示例事件处理拓展 - 详细解释与示例其他拓展内容前言 在阅读过程…

Vue入门:vue项目的创建和基本概念

一、vue的基本简介1. 什么是vue?Vue (发音为 /vjuː/&#xff0c;类似 view) 是一款用于构建用户界面的 JavaScript 框架。它基于标准 HTML、CSS 和 JavaScript 构建&#xff0c;并提供了一套声明式的、组件化的编程模型&#xff0c;帮助你高效地开发用户界面。无论是简单还是…

2.oracle保姆级安装教程

一、Oracle数据库安装1.找到软件的位置 D:\学习软件\Oracle&#xff0c;并解压软件2.双击setup.exe3.选择 是4.去掉勾&#xff0c;下一步5.创建和配置数据库&#xff0c;下一步6.桌面类&#xff0c;下一步7.配置安装路径地址和密码8.点完成9.正在安装&#xff0c;稍等片刻10.有…

STM32 软件模拟 I2C 读写 MPU6050--实现加速度与陀螺仪数据采集

演示视频&#xff1a; https://www.bilibili.com/video/BV1iCQRYXEBb/?share_sourcecopy_web&vd_source0e4269581b0bc60d57a80c9a27c98905一、前言在嵌入式开发中&#xff0c;MPU6050 六轴传感器因其集成加速度计和陀螺仪且成本低廉&#xff0c;广泛应用于平衡小车、飞控、…

TFLOPs与TOPS的转换关系详解:如何衡量AI芯片的算力?

在评估AI芯片或计算硬件的性能时&#xff0c;我们经常会遇到TFLOPs和TOPS这两个关键指标。很多开发者对它们的区别和转换关系存在疑惑。本文将深入解析这两个指标的含义、应用场景及转换方法&#xff0c;并提供实际应用中的注意事项。 一、基本概念解析 1.1 TFLOPs&#xff08;…

C语言:第11天笔记

C语言&#xff1a;第11天笔记 内容提要函数函数的概述函数的分类函数的定义形参和实参函数的返回值函数的调用函数的声明函数 函数的概述 **函数&#xff1a;**实现一定功能的&#xff0c;独立的代码模块&#xff0c;对于函数的使用&#xff0c;一定是先定义&#xff0c;后使 ​…

java导出pdf(使用html)

引入maven <dependencies><!-- Thymeleaf --><dependency><groupId>org.thymeleaf</groupId><artifactId>thymeleaf</artifactId><version>3.1.1.RELEASE</version> <!-- 或与 Spring Boot 匹配的版本 --></de…

Qt 远程过程调用(RPC)实现方案

在分布式系统开发中&#xff0c;远程过程调用&#xff08;RPC&#xff09;是实现跨进程、跨机器通信的重要技术。Qt 作为一个强大的跨平台框架&#xff0c;提供了多种 RPC 实现方案&#xff0c;能够满足不同场景下的通信需求。本文将深入探讨 Qt 中 RPC 的各种实现方式&#xf…

攻防世界-引导-Web_php_unserialize

题目内容&#xff1a;出现一段源代码&#xff0c;分段分析第一部分如下<?php class Demo { private $file index.php;public function __construct($file) { $this->file $file; }function __destruct() { echo highlight_file($this->file, true); }function __w…

pytorch学习笔记-自定义卷积

未完结的草稿———&#xff01;大概是准备整合一下常见的层&#xff0c;整合完感觉就可以进行搭建了&#xff08;还没进行到这一步所以不太确定版&#xff09; &#xff08;ps我将在完结这一篇的时候删除上面的小字and二编一下整篇文章的结构&#xff0c;如果看到了这部分文字…

[明道云]-基础教学2-工作表字段 vs 控件:选哪种?

本文深入解析“工作表字段”与“控件”的关系与差别,并从结构、功能、使用场景和选型建议等方面进行对比。 一、基础概念厘清 ✅ 工作表字段 = 数据模型中的列 工作表字段相当于数据库表中的列,是记录每条业务对象(如订单、客户等)属性的数据项,每个字段都有明确的名称和…

C++-一篇文章入门coroutines协程

文章目录前言什么是协程协程实现原理C协程的最小例子12345协程等效代码协程传值的例子前言 最近学习了一下C协程&#xff0c;这篇文章将介绍协程的相关概念&#xff0c;以及在C中如何使用协程。 什么是协程 C中&#xff0c;协程&#xff08;coroutines&#xff09;可以理解为…