DRAMSim2模拟器分析与应用

        DRAMSim2是一个主要模拟DRAM memory读写访问延迟和工作能耗的工具,因其模拟结果与实际运行结果非常接近而被科研工作者广泛使用。本文将介绍DRAMSim2的代码架构,方便大家理解DRAMSim2的工作过程。之后再结合自己的使用经历,给出修改DRAMSim2的一些建议。


(一)DRAMSim2逻辑架构:

        DRAMSim2的逻辑架构图如图所示:

        DRAMSim2模拟器分析与应用_第1张图片

        DRAMSim2可以模拟多channel(通道)的内存。概念和分布式存储类似。每一个channel内都是一个独立的内存系统。内存系统中的结构我们应该是非常熟悉的:它有一个重要的内存控制器,完成地址映射,记录指令序列,记录各rank的状态;同时它分为多个rank,对于每个rank:都有一个bank控制器,控制着bank的状态,访问队列等;同时每个rank 又有多个bank,每个bank都是地址范围相同的行列矩阵,及我们读写访问的最终地方。


(二)DRAMSim2指令流:

        这里借用DRAMSim2说明文档中的指令流图:

        DRAMSim2模拟器分析与应用_第2张图片


(三)DRAMSim2代码架构:

        下面根据trace文件的输入命令过程,跟踪DRAMSim的处理流程,来理解DRAMSim的代码架构。其中我不可能将源代码都放出来,那样思路太不清晰。更多的是语言来表达,这样就需要大家通过搜索了解关键变量,仔细阅读源码,才能真正理解。


    A.TraceBasedSim.cpp:

        (1)在其中的main函数中,可以找到代码对运行DRAMSim时我们输入的指令进行了处理。我们需要找到traceFile 这个变量,它是一个输入流,绑定了我们要访问的trace文件。
        (2)在main函数中,实例化了一个MultiChannelMemorySystem类:memorySystem。这就是整个存储系统。MultiChannelMemorySystem类是在MultiChannelMemorySystem.h中定义的。其中最重要的就是update函数。之后我们会详细说明。
        (3)在main函数中,我们可以找到变量numCycles。它的意义很容易理解,就是最大运行时钟周期数。当运行时钟周期数超过numCycles,就会终止运行。
        (4)main函数中:transaction这个函数也很重要,但很好理解:当我们处理每个请求时,都是把它作为一个transaction(事务)处理的。而MultiChannelMemorySystem类中,定义了addTransaction函数,就是处理外部输入的每个事务请求的。详细过程会在之后详细说明。
        (5)最后我们就要了解main函数中最重要的一个函数:update。它是MultiChannelMemorySystem类中的一个子函数。如果仔细看代码,会发现每个时钟周期都会执行一次update,所以我们可以确定,这就是内存中每个周期更新状态的关键函数。但具体它更新了哪些内容,我们还需要深入了解。


    下面我们就根据之前提到的逻辑架构和指令流重点理解前面所述的两个重点:(a)addTransaction函数(存储系统中,如何处理到达的事务)。(b)update函数(存储系统中,如何更新状态)。


    B.MultiChannelMemorySystem.cpp:

        其实MultiChannelMemorySystem类并不是重点。因为我们通常设置的channel为1.而在其中我们可以找到channel 变量。可以发现channel为MemorySystem类的实例化。MemorySystem类是在MemorySystem.h中定义的。所以对照逻辑架构,发现一个channel其实是一个MemorySystem类。同样MultiChannelMemorySystem类中的addTransaction函数和update函数其实都只是调用了对应的MemorySystem类中的addTransaction函数和update函数。


    C.MemorySystem.cpp:

        逻辑架构中已经说过每个channel中有两部分:MemoryController和rank。同样MemorySystem中也分别调用了两部分:MemoryController类和rank类。(a)对于addTransaction函数,因为它是添加一个新的事务,并要分配给对应的rank,属于MemoryController的任务,所以调用了MemoryController类中的addTransaction函数。(b)而update函数则要将MemoryController和rank都更新一个时钟周期,所以对应的调用了MemoryController和每个rank的update函数。


    D.MemoryController.cpp:

        (a)终于我们在这里到达了addTransaction的底层,它将事务放入了transactionQueue(事务队列)中。(b)对于update同样到了真正有“实际意义”的update函数中。但同时我们可以发现单单一个update函数就有600行代码。想要真正看懂更新过程,我们必须要对DRAM的工作原理有深入的理解,比如:启动,刷新,行激活等。这篇文章的重点是理清DRAMSim代码思路,所以在此就不详细介绍DRAM的工作原理了,之后会有文章进行详细介绍。我们只需要明确一点:DRAMSim之所以可以准确模拟出延迟功耗,是因为针对每个时钟周期,都在这里找到了非常准确的update。所以这里是整个代码的核心部分!


    E.Rank.cpp:

        (b)rank中的update函数主要是记录更新rank的工作状态,依然这一块儿与DRAM的工作原理相关,但并不重要。


    F.AddressMapping.cpp:

        定义了addressMapping函数,主要功能是将输入指令的地址进行处理,确定它属于哪个channel,rank,bank,row,col。这里需要注意的是DRAMSim中给出了关于以上5个量的7种地址组合方式,可以在system.ini中进行选择。addressMapping函数在MemoryController.cpp中被引用。

    还有一些源代码,由于不是特别重要,这里就不提了。


(四)DRAMSim2模拟器的“改造”:

        我们利用DRAMSim2模拟器的目的,肯定是想要得到关于内存的延迟及功耗。但有时,我们需要改变内存的结构来进行模拟,或者改变内存的介质来进行模拟,甚至我们需要模拟一个混合内存系统。这时我们就要改写代码。通过我自己的实现经验,这里有两个改写建议:(a)通过上面的分析,我们发现其实一个内存系统就是一个MultiChannelMemorySystem类。我们可以实例化多个MultiChannelMemorySystem类来进行多内存系统模拟。(b)另一个就是利用命名空间。DRAMSim2的代码非常规范,它定义了DRAMSim这个命名空间。利用不同命名空间相同变量的定义也不会冲突的优点。我们可以定义其它命名空间,作为我们想要设计的内存系统,利用原本的代码,可以大大减少我们的工作量。当然这只是我个人观点,仅希望通过本文,可以让大家更快的了解DRAMSim2模拟器,完成自己的模拟工作。


你可能感兴趣的:(存储系统)