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
英文地址,請注明出處:http://en.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;可以理解為…