gem5 garnet 拓扑结构之port: NI CPU ROUTER L1 L2

简介

有Crossbar,CrossbarGarnet,Mesh_*,MeshDirCorners_XY,Pt2Pt等拓扑结构,我们主要关注mesh-xy。参考是https://www.gem5.org/documentation/general_docs/ruby/interconnection-network/

MESI TWO LEVEL与 mesh xy

数目

mesh-xy此拓扑要求目录数量等于 cpu 数量。路由器/交换机的数量等于系统中CPU的数量。每个路由器/交换机都连接到一个 L1、一个 L2(如果存在)和一个目录。网格中的行数必须由–mesh-rows指定。该参数也可以创建非对称网格。
Mesh_XY:具有 XY 路由的网格。所有 x 方向链接的偏置权重为 1,而所有 y 方向链接的偏置权重为 2。这强制所有消息在使用 Y 链接之前首先使用 X 链接。可以通过–topology=Mesh_XY从命令行调用它

simple-MI_example.py

我们研究noc一般是会用到共享 l2的,也就是说每个router都连上了l1 l2 和 dire。
我们之前的 gem5 RubyPort: mem_request_port作用与连接 simple-MI_example.py 描述了没有l2的互联,

simple-MI_example.py中定义的结构:
在这里插入图片描述

mesi-twolevel

我们现在看有l2之后,更复杂的互联,用的protocol是mesi-twolevel,也是x86 isa的默认协议。
先看官方的博客:https://www.gem5.org/documentation/general_docs/ruby/MESI_Two_Level/

协议概述

  1. 该协议对两级缓存层次结构进行建模。L1 缓存是核心私有的,而 L2 缓存是核心之间共享的。L1 Cache 分为指令缓存和数据缓存。
    L1 和 L2 缓存之间保持包含关系。
  2. 在高层,协议有四个稳定状态:M、E、 S和I。处于M状态的块意味着该块是可写的(即具有独占许可)并且已被弄脏(即它是片上唯一有效的副本)。E状态表示具有独占权限(即可写)但尚未写入的缓存块。S 状态意味着缓存块是只读的,并且它可能存在于多个私有缓存以及共享缓存中的多个副本。I的意思是缓存块无效。
  3. 片上缓存一致性通过目录一致性方案来维护,其中目录信息与共享二级缓存中的相应缓存块位于同一位置。
    该协议有四种类型的控制器——L1缓存控制器、L2缓存控制器、目录控制器和DMA控制器。L1缓存控制器负责管理L1指令和L1数据缓存。L1 缓存控制器的实例化数量等于模拟系统中的核心数量。L2 缓存控制器负责管理共享 L2 缓存并通过目录一致性方案维护片上数据的一致性。目录控制器充当存储器控制器/片外主存储器的接口,并且还负责跨多个芯片/以及来自 DMA 控制器的外部一致性请求的一致性。DMA 控制器负责满足相干 DMA 请求。
  4. 该协议的主要优化之一是,如果 L1 Cache 请求数据块(即使是读取权限),L2 Cache 控制器如果发现没有其他核心拥有该块,则会返回具有独占权限的缓存块。这是一项优化,预计读取的缓存块很快就会由同一核心写入,从而通过此优化节省额外的请求。这正是E状态存在的原因(即当缓存块可写但尚未写入时)。
  5. 该协议支持从私有 L1 缓存中静默驱逐干净的缓存块。这意味着未被写入且具有可读权限的缓存块只能从私有L1缓存中删除该缓存块,而无需通知L2缓存。此优化有助于减少 L2 缓存控制器的写回流量。

相关文件

src/mem/protocols
MESI_CMP_directory-L1cache.sm:L1缓存控制器规范
MESI_CMP_directory-L2cache.sm:L2缓存控制器规范
MESI_CMP_directory-dir.sm:目录控制器规范
MESI_CMP_directory-dma.sm:DMA控制器规范
MESI_CMP_directory-msg.sm:一致性消息类型规范。这定义了给定协议将使用的不同类型消息的不同字段
MESI_CMP_directory.slicc:容器文件

状态与功能

跳过,我们只放一些截图大概了解一下。
在这里插入图片描述
在这里插入图片描述

SLICC 接口

https://www.gem5.org/documentation/general_docs/ruby/slicc/ 介绍的SLICC缓存一致性协议可以写很多部分写很久,我们这里先只看一下接口。

C++ to Slicc Interface - @note: What do each of these files do/define???

src/mem/protocol/RubySlicc_interaces.sm
RubySlicc_Exports.sm
RubySlicc_Defines.sm
RubySlicc_Profiler.sm
RubySlicc_Types.sm
RubySlicc_MemControl.sm
RubySlicc_ComponentMapping.sm
Variable Assignments

Use the := operator to assign members in class (e.g. a member defined in RubySlicc_Types.sm):
an automatic m_ is added to the name mentioned in the SLICC file.

learning gem5 part3 有slicc的讲解

https://www.gem5.org/documentation/learning_gem5/part3/cache-declarations/
Sequencer. 这是一个在 Ruby 中实现的特殊类,用于与 gem5 的其余部分交互。Sequencer 是一个MemObject带有从端口的 gem5,因此它可以接受来自其他对象的内存请求。定序器接受来自 CPU(或其他主端口)的请求,并将 gem5 数据包转换为RubyRequest. 最后,RubyRequest将其推入mandatoryQueue状态机。我们将重新审视mandatoryQueue港口部分。

接下来,有一个CacheMemory对象。这是保存缓存数据(即缓存条目)的内容。确切的实现、大小等可以在运行时配置。

接下来,同样在参数块中(即在第一个左括号之前),我们需要声明该状态机将使用的所有消息缓冲区。消息缓冲区是状态机和 Ruby 网络之间的接口。消息通过消息缓冲区发送和接收。因此,对于我们协议中的每个虚拟通道,我们需要一个单独的消息缓冲区。

MSI协议需要三个不同的虚拟网络。需要虚拟网络来防止死锁(例如,如果响应卡在停滞的请求后面,那就不好了)。在该协议中,最高优先级是响应(虚拟网络2),其次是转发请求(虚拟网络1),然后请求具有最低优先级(虚拟网络0)。参见索林等人。了解为什么需要这三个虚拟网络的详细信息。
以下代码声明了所有需要的消息缓冲区。

machine(MachineType:L1Cache, "MSI cache")
: Sequencer *sequencer;CacheMemory *cacheMemory;bool send_evictions;MessageBuffer * requestToDir, network="To", virtual_network="0", vnet_type="request";MessageBuffer * responseToDirOrSibling, network="To", virtual_network="2", vnet_type="response";MessageBuffer * forwardFromDir, network="From", virtual_network="1", vnet_type="forward";MessageBuffer * responseFromDirOrSibling, network="From", virtual_network="2", vnet_type="response";MessageBuffer * mandatoryQueue;
{
}

如前所述,该参数块被转换为 SimObject 描述文件。您在此块中放入的任何参数都将是可从 Python 配置文件访问的 SimObject 参数。如果你看一下生成的文件 L1Cache_Controller.py,你会发现它看起来非常熟悉。注意:这是一个生成的文件,您不应该直接修改生成的文件!
由此可见,这个sm文件变成了python文件L1Cache_Controller.py (实际上他叫l1_cache.py ,例如src/python/gem5/components/cachehierarchies/ruby/caches/mesi_two_level/l1_cache.py )。

由此我们发现,cache是没有c文件的,是slicc文件和转化的python文件作为接口。

举例 我们看看network.slave network.in_port 是什么端口

哪里用了network.slave network.in_port?这引起我们思考,哪里提供了供使用的network.in_port定义

src/python/gem5/components/cachehierarchies/ruby/caches/mesi_two_level/l1_cache.py

@overrides(AbstractL1Cache)def connectQueues(self, network):self.mandatoryQueue = MessageBuffer()self.requestFromL1Cache = MessageBuffer()self.requestFromL1Cache.out_port = network.in_portself.responseFromL1Cache = MessageBuffer()self.responseFromL1Cache.out_port = network.in_portself.unblockFromL1Cache = MessageBuffer()self.unblockFromL1Cache.out_port = network.in_portself.optionalQueue = MessageBuffer()self.requestToL1Cache = MessageBuffer()self.requestToL1Cache.in_port = network.out_portself.responseToL1Cache = MessageBuffer()self.responseToL1Cache.in_port = network.out_port
mesi_two_level/l1_cache.py 中这个network是啥

fs py 用了ruby creatysytem,其实调用的mesi two level 的 create_system。 mesi two level 的 create_system中 将l1 cache和 network slave相连。
例如

l1_cntrl.requestFromL1Cache.out_port = ruby_system.network.in_port

我们细看下什么是mesi_two_level。py 中的 network。

fs py 用了ruby creatysytem, ruby creatysytem 内嵌了 configs/network/Network.py 中 NetworkClass函数来 创建的network.
configs/network/Network.py

 network = NetworkClass(ruby_system=ruby,topology=options.topology,routers=[],ext_links=[],int_links=[],netifs=[],)

python src/mem/ruby/network/Network.py 一样说明了

    in_port = VectorResponsePort("CPU input port")slave = DeprecatedParam(in_port, "`slave` is now called `in_port`")
network = NetworkClass 本质是调用garnetwork的创建过程, 但是这个slaveport只用到了ruby 这一层的定义

创建的是garnetnetwork,但是继承了rubynetwork的 slave 或者叫in_port
下面是创建的garnetwork
src/mem/ruby/network/garnet/GarnetNetwork.py:

class GarnetNetwork(RubyNetwork):type = "GarnetNetwork"cxx_header = "mem/ruby/network/garnet/GarnetNetwork.hh"cxx_class = "gem5::ruby::garnet::GarnetNetwork"num_rows = Param.Int(0, "number of rows if 2D (mesh/torus/..) topology")ni_flit_size = Param.UInt32(16, "network interface flit size in bytes")vcs_per_vnet = Param.UInt32(4, "virtual channels per virtual network")buffers_per_data_vc = Param.UInt32(4, "buffers per data virtual channel")buffers_per_ctrl_vc = Param.UInt32(1, "buffers per ctrl virtual channel")routing_algorithm = Param.Int(0, "0: Weight-based Table, 1: XY, 2: Custom")enable_fault_model = Param.Bool(False, "enable network fault model")fault_model = Param.FaultModel(NULL, "network fault model")garnet_deadlock_threshold = Param.UInt32(50000, "network-level deadlock threshold")
garnetnetwork是继承的src/mem/ruby/network/Network.py 中定义的 slave 或者叫in_port

注意并不是 configs/network/Network.py 而是 src/mem/ruby/network/Network.py 中的class RubyNetwork(ClockedObject):
这是 src/mem/ruby/network/Network.py 中RubyNetwork 全部的python内容。

class RubyNetwork(ClockedObject):type = "RubyNetwork"cxx_class = "gem5::ruby::Network"cxx_header = "mem/ruby/network/Network.hh"abstract = Truetopology = Param.String("Not Specified", "the name of the imported topology module")number_of_virtual_networks = Param.Unsigned("Number of virtual networks ""used by the coherence protocol in use.  The on-chip network ""assumes the protocol numbers vnets starting from 0.  Therefore, ""the number of virtual networks should be one more than the ""highest numbered vnet in use.")control_msg_size = Param.Int(8, "")ruby_system = Param.RubySystem("")routers = VectorParam.BasicRouter("Network routers")netifs = VectorParam.ClockedObject("Network Interfaces")ext_links = VectorParam.BasicExtLink("Links to external nodes")int_links = VectorParam.BasicIntLink("Links between internal nodes")in_port = VectorResponsePort("CPU input port")slave = DeprecatedParam(in_port, "`slave` is now called `in_port`")out_port = VectorRequestPort("CPU output port")master = DeprecatedParam(out_port, "`master` is now called `out_port`")data_msg_size = Param.Int(Parent.block_size_bytes,"Size of data messages. Defaults to the parent ""RubySystem cache line size.",)

其中port1的部分出现了。

    in_port = VectorResponsePort("CPU input port")slave = DeprecatedParam(in_port, "`slave` is now called `in_port`")out_port = VectorRequestPort("CPU output port")master = DeprecatedParam(out_port, "`master` is now called `out_port`")

也就是说,l1cache 的一个port现在和rubynetwork的一个 port,在python里相等了。

理解 VectorResponsePort(“CPU input port”)

src/python/m5/params.py 中说明了 VectorResponsePort(VectorPort)是怎么创建的。他是继承自VectorPort,创建的时候字符串“CPU input port”作为desc。传递进来。
然后调用super().init(“GEM5 RESPONDER”, desc) 也就是调用 super().init(“GEM5 RESPONDER”, “CPU input port”)

class VectorResponsePort(VectorPort):# VectorResponsePort("description")def __init__(self, desc):super().__init__("GEM5 RESPONDER", desc)

super()让您避免显式引用基类,这很好。但主要优点是多重继承,可以发生各种有趣的事情。 https://stackoverflow.com/questions/576169/understanding-python-super-with-init-methods

其实就是基类VectorPort的初始化, 传递进来了 “GEM5 RESPONDER” 和 “CPU input port”

src/python/m5/params.py 中:并没有需要这个初始化的参数传递,而是给了一个新函数。因此我们扫一眼,继续看VectorPort的基类也就是port的初始化。

# VectorPort description object.  Like Port, but represents a vector
# of connections (e.g., as on a XBar).
class VectorPort(Port):def makeRef(self, simobj):return VectorPortRef(simobj, self.name, self.role, self.is_source)
最后还是python文件中 port的初始化

src/python/m5/params.py 中:我们先看代码再解读。

# Port description object.  Like a ParamDesc object, this represents a
# logical port in the SimObject class, not a particular port on a
# SimObject instance.  The latter are represented by PortRef objects.
class Port(object):# Port("role", "description")_compat_dict = {}@classmethoddef compat(cls, role, peer):cls._compat_dict.setdefault(role, set()).add(peer)cls._compat_dict.setdefault(peer, set()).add(role)@classmethoddef is_compat(cls, one, two):for port in one, two:if not port.role in Port._compat_dict:fatal("Unrecognized role '%s' for port %s\n", port.role, port)return one.role in Port._compat_dict[two.role]def __init__(self, role, desc, is_source=False):self.desc = descself.role = roleself.is_source = is_source# Generate a PortRef for this port on the given SimObject with the# given namedef makeRef(self, simobj):return PortRef(simobj, self.name, self.role, self.is_source)# Connect an instance of this port (on the given SimObject with# the given name) with the port described by the supplied PortRefdef connect(self, simobj, ref):self.makeRef(simobj).connect(ref)# No need for any pre-declarations at the moment as we merely rely# on an unsigned int.def cxx_predecls(self, code):passdef pybind_predecls(self, code):cls.cxx_predecls(self, code)# Declare an unsigned int with the same name as the port, that# will eventually hold the number of connected ports (and thus the# number of elements for a VectorPort).def cxx_decl(self, code):code("unsigned int port_${{self.name}}_connection_count;")

代码看完了,总算看到初始化了! 下面是核心代码。我们传递进来的 “GEM5 RESPONDER” 是role 和 “CPU input port” 是 desc,is_source没传递,默认是False。

def __init__(self, role, desc, is_source=False):

这个python 中的 src/mem/ruby/network/Network.py 的 in_port 是 VectorResponsePort,它现在也初始化了 自己知道了自己的desc和role和 is_source。 但是我们依旧不知道它自己到底是怎么和cache或者router怎么相连的。 我们甚至还不知道它和cpp的port是怎么对应的

		self.desc = descself.role = roleself.is_source = is_source

但是没关系, 核心的对应关系就在这个desc这里。这描述了 “CPU input port” 字符串不会是白用的 好吧原来关键是"GEM5 RESPONDER"。
src/python/m5/params.py中 对port进行了一次操作 Port.compat(“GEM5 REQUESTOR”, “GEM5 RESPONDER”)。

class Port(object):。。。
Port.compat("GEM5 REQUESTOR", "GEM5 RESPONDER")

Port.compat建立两种不同类型端口之间的兼容性:“GEM5 REQUESTOR”和“GEM5 RESPONDER”。这种兼容性对于确保只有可以逻辑地相互连接的端口才被允许。
看上去也没特别的。
线索到这里断了,我们来看
l1_cntrl.requestFromL1Cache.out_port = ruby_system.network.in_port
中的l1_cntrl.requestFromL1Cache.out_port 。
我们现在可以推测,具体的定义是在 slicc文件中定义的,而python中像是提供一个接口。

python中其他的代码需要访问这个slicc详细定义的out_port,是看不到的。这些python代码可以访问的是 ruby_system.network.in_port 。
查找一些资料:
Ruby 中内存请求的生命周期
在本节中,我们将高度概述 Ruby 作为一个整体如何服务内存请求以及它通过 Ruby 中的哪些组件。不过,有关每个组件内的详细操作,请参阅前面单独描述每个组件的部分。

来自 gem5 的核心或硬件上下文的内存请求通过RubyPort::recvTiming 接口(在 src/mem/ruby/system/RubyPort.hh/cc 中)进入 Ruby 的管辖范围。模拟系统中 Rubyport 实例化的数量等于硬件线程上下文或核心的数量(在 非多线程核心的情况下)。每个核心侧面的端口都与相应的 RubyPort 绑定。
内存请求以 gem5 数据包形式到达,RubyPort 负责将其转换为 RubyRequest 对象,该对象可以被 Ruby 的各个组件理解。它还查明请求是否针对某些 PIO,并操纵数据包以纠正 PIO。最后,一旦它生成了相应的 RubyRequest 对象并确定该请求是正常的内存请求(不是 PIO 访问),它就会将该请求传递给 带有端口的附加 Sequencer 对象的Sequencer::makeRequest接口(变量ruby​​_port保存该指针)到它)。请注意,Sequencer 类本身是 RubyPort 类的派生类。
正如在描述 Ruby Sequencer 类的部分中提到的,模拟系统中的 Sequencer 对象与硬件线程上下文的数量一样多(也等于系统中 RubyPort 对象的数量),并且有一个 - Sequencer 对象和硬件线程上下文之间的一对一映射。一旦内存请求到达Sequencer::makeRequest,它就会对该请求进行各种统计和资源分配,最后将请求推送到 Ruby 的一致缓存层次结构以满足请求,同时考虑服务中的延迟。在考虑 L1 缓存访问延迟后,通过将请求排队到强制队列,将请求推送到缓存层次结构。强制队列(变量名称 m_mandatory_q_ptr)有效地充当 Sequencer 和 SLICC 生成的缓存一致性文件之间的接口。
L1 缓存控制器(由 SLICC 根据一致性协议规范生成)将请求从强制队列 中出列并查找缓存,进行必要的一致性状态转换和/或根据要求将请求推送到缓存层次结构的下一级。SLICC 生成的 Ruby 代码的不同控制器和组件通过 Ruby 的MessageBuffer类 (src/mem/ruby/buffers/MessageBuffer.cc/hh)的实例化在它们之间进行通信,该类可以充当有序或无序的缓冲区或队列。此外,为满足内存请求而服务的不同步骤中的延迟也相应地用于调度入队和出队操作。如果可以在 L1 缓存中找到所请求的缓存块并且具有所需的一致性许可,则请求得到满足并立即返回。否则,请求将通过MessageBuffer推送到缓存层次结构的下一级。请求可以一直到达 Ruby 的内存控制器(在许多协议中也称为目录)。一旦请求得到满足,它就会通过MessageBuffer在层次结构中向上推送。
消息缓冲区还充当所建模的片上互连的一致性消息的入口点。MesageBuffers 根据指定的互连拓扑进行连接。因此,一致性消息相应地通过该片上互连。
一旦请求的缓存块在具有所需一致性权限的 L1 缓存中可用,L1 缓存控制器就会根据请求的类型通过调用其readCallback或 “writeCallback ”方法来通知相应的 Sequencer 对象。请注意,当 Sequencer 上的这些方法被调用时,服务请求的延迟已被隐式地考虑在内。
然后,Sequencer 会清除相应请求的记帐信息,然后调用 RubyPort::ruby_hit_callback方法。这最终将请求结果返回到前端(gem5)的核心/硬件上下文的相应端口。

slicc文件中的 requestFromL1Cache

src/mem/ruby/protocol/MESI_Two_Level-L1cache.sm 中 定义了详细的 MessageBuffer。

// Message Queues// From this node's L1 cache TO the network// a local L1 -> this L2 bank, currently ordered with directory forwarded requestsMessageBuffer * requestFromL1Cache, network="To", virtual_network="0",vnet_type="request";// a local L1 -> this L2 bankMessageBuffer * responseFromL1Cache, network="To", virtual_network="1",vnet_type="response";MessageBuffer * unblockFromL1Cache, network="To", virtual_network="2",vnet_type="unblock";// To this node's L1 cache FROM the network// a L2 bank -> this L1MessageBuffer * requestToL1Cache, network="From", virtual_network="2",vnet_type="request";// a L2 bank -> this L1MessageBuffer * responseToL1Cache, network="From", virtual_network="1",vnet_type="response";
dsc 为 "CPU input port"的 python port是怎么对应cpp的? 和其他python 对象的互联结构是怎么样的?

cpp代码

其对应的cpp如下

GarnetNetwork::GarnetNetwork(const Params &p): Network(p)
{m_num_rows = p.num_rows;m_ni_flit_size = p.ni_flit_size;m_max_vcs_per_vnet = 0;m_buffers_per_data_vc = p.buffers_per_data_vc;m_buffers_per_ctrl_vc = p.buffers_per_ctrl_vc;m_routing_algorithm = p.routing_algorithm;m_next_packet_id = 0;m_enable_fault_model = p.enable_fault_model;if (m_enable_fault_model)fault_model = p.fault_model;m_vnet_type.resize(m_virtual_networks);for (int i = 0 ; i < m_virtual_networks ; i++) {if (m_vnet_type_names[i] == "response")m_vnet_type[i] = DATA_VNET_; // carries data (and ctrl) packetselsem_vnet_type[i] = CTRL_VNET_; // carries only ctrl packets}// record the routersfor (std::vector<BasicRouter*>::const_iterator i =  p.routers.begin();i != p.routers.end(); ++i) {Router* router = safe_cast<Router*>(*i);m_routers.push_back(router);// initialize the router's network pointersrouter->init_net_ptr(this);}// record the network interfacesfor (std::vector<ClockedObject*>::const_iterator i = p.netifs.begin();i != p.netifs.end(); ++i) {NetworkInterface *ni = safe_cast<NetworkInterface *>(*i);m_nis.push_back(ni);ni->init_net_ptr(this);}// Print Garnet versioninform("Garnet version %s\n", garnetVersion);
}
```####
### SLICC的CPP接口
SM文件是STATE MACHINE状态机文件,我们不如看看熟悉的cpp文件开始
src/mem/ruby/slicc_interface/RubySlicc_ComponentMapping.hh 

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

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

相关文章

ASP.NET Core基础之定时任务(二)-Quartz.NET入门

阅读本文你的收获 了解任务调度框架QuartZ.NET的核心构成学会在ASP.NET Core 中使用QuartZ.NET 在项目的开发过程中&#xff0c;难免会遇见需要后台处理的任务&#xff0c;例如定时发送邮件通知、后台处理耗时的数据处理等&#xff0c;上次分享了ASP.NET Core中实现定时任务的…

STM32G4x FLASH 读写(修改HAL库驱动)

主要工作就是把HAL的超时用LL库延时替代&#xff0c;保留了中断擦写模式、轮询等待擦写&#xff0c;待验证哈。 笔者用的芯片为STM32G473CBT6 128KB Flash&#xff0c;开环环境为CUBEMXMDK5.32&#xff0c;因为G4已经没有标准库了&#xff0c;笔者还是习惯使用标准库的开发方式…

Flink 状态管理与容错机制(CheckPoint SavePoint)的关系

一、什么是状态 无状态计算的例子&#xff1a; 例如一个加法算子&#xff0c;第一次输入235那么以后我多次数据23的时候得到的结果都是5。得出的结论就是&#xff0c;相同的输入都会得到相同的结果&#xff0c;与次数无关。 有状态计算的例子&#xff1a; 访问量的统计&#x…

微信小程序开发系列-01创建一个最小的小程序项目

本文讲述了通过微信开发者工具&#xff0c;创建一个新的小程序项目&#xff0c;完全从零开始&#xff0c;不依赖开发者工具的模板。目的是为了更好的理解小程序工程项目的构成。 文章目录 创建一个空项目app.json全局配置pagessitemapLocation app.js 创建一个空项目 打开微信…

【C语言】指针详解(一)

目录 1.内存和地址 1.1内存 1.2如何理解编址 2.指针变量和地址 2.1取地址操作符&#xff08;&&#xff09; 2.2指针变量和解引用操作符&#xff08;*&#xff09; 2.2.1指针变量 2.2.2拆解指针类型 2.2.3解引用操作符 2.3指针变量大小 1.内存和地址 1.1内存 在讲内…

大型医院PACS系统源码,影像存储与传输系统源码,支持多种图像处理及三维重建功能

PACS系统是医院影像科室中应用的一种系统&#xff0c;主要用于获取、传输、存档和处理医学影像。它通过各种接口&#xff0c;如模拟、DICOM和网络&#xff0c;以数字化的方式将各种医学影像&#xff0c;如核磁共振、CT扫描、超声波等保存起来&#xff0c;并在需要时能够快速调取…

模型实战(18)之C++ - tensorRT部署GAN模型实现人脸超分辨重建

模型实战(18)之C++ - tensorRT部署GAN模型实现人脸超分辨重建 一个实现人脸超分辨率重建的demo支持StyleGAN: GPEN or GFPGAN通过C++ - tensorrt 快速部署,推理速度每帧 在RTX3090上5.5ms+,RTX3050上10ms+下边是实现效果(图片来源于网络search,如若侵权,联系删除) 下边…

先进制造身份治理现状洞察:从手动运维迈向自动化身份治理时代

在新一轮科技革命和产业变革的推动下&#xff0c;制造业正面临绿色化、智能化、服务化和定制化发展趋势。为顺应新技术革命及工业发展模式变化趋势&#xff0c;传统工业化理论需要进行修正和创新。其中&#xff0c;对工业化水平的判断标准从以三次产业比重标准为主回归到工业技…

Flink系列之:Savepoints

Flink系列之&#xff1a;Savepoints 一、Savepoints二、分配算子ID三、Savepoint 状态四、算子五、触发Savepoint六、Savepoint 格式七、触发 Savepoint八、使用 YARN 触发 Savepoint九、使用 Savepoint 停止作业十、从 Savepoint 恢复十一、跳过无法映射的状态恢复十二、Resto…

gem5 RubyPort: mem_request_port作用与连接 simple-MI_example.py

简介 回答这个问题&#xff1a;RubyPort的口下&#xff0c;一共定义了六个口&#xff0c;分别是mem_request_port&#xff0c;mem_response_port&#xff0c;pio_request_port&#xff0c;pio_response_port&#xff0c;in_ports, interrupt_out_ports&#xff0c;他们分别有什…

2023 年人工智能研究与技术排名前 10 的国家

人工智能研究是一项全球性的工作。虽然美国和中国因其对人工智能的贡献而备受关注&#xff0c;但事实是&#xff0c;世界各国都在涉足这项技术&#xff0c;尝试新的突破&#xff0c;并吸引投资者的关注。 斯坦福大学的《2023年人工智能报告》估计&#xff0c;到 2022 年&#…

深入理解网络 I/O:mmap、sendfile、Direct I/O

&#x1f52d; 嗨&#xff0c;您好 &#x1f44b; 我是 vnjohn&#xff0c;在互联网企业担任 Java 开发&#xff0c;CSDN 优质创作者 &#x1f4d6; 推荐专栏&#xff1a;Spring、MySQL、Nacos、Java&#xff0c;后续其他专栏会持续优化更新迭代 &#x1f332;文章所在专栏&…