Chromium源码阅读:深入理解Mojo框架的设计思想,并掌握其基本用法(1)

Mojo简介

Mojo 是一个运行时库的集合,提供与平台无关的通用 IPC 原语抽象、消息 IDL 格式以及具有针对多种目标语言的代码生成的绑定库,以便于跨任意进程间和进程内边界传递消息。
Mojo 分为清晰分离的层,子组件的基本层次结构如下:

请添加图片描述

分析Mojo之前,我们的思考

笔者在阅读源码前,喜欢会去思考,“如果让我来设计一个类似的功能的模块,我会怎么设计?”。然后对比文档去思考为什么会出现思路的差异,这种方式可以让我快速掌握一个开源库的设计精髓。
这次也是一样,我们想想,如果是我们自己设计Mojo,这会是什么样的架构和过程。

  1. 首先,Mojo是跨平台的,那么必然有一层platform的平台差异屏蔽层
  2. 其次,跨进程通信在不同平台上的最佳实现方案可能不一样,例如有的平台是管道,有的平台是共享内存,具体如何选择取决于不同平台的性能差异,因此,我必须将跨平台通信细节进行抽象,提取出一些概念,用于描述通信的应用层细节
  3. 之后,两个进程之间的通信之前,需要先建立连接,因此必然也需要定义一组规则和概念,来描述连接的应用层细节
  4. 为了增加通信的灵活性,我们可以定义一组观察者或者过滤器的规则,可以实现对数据流的监测和量化,也能实现更灵活的扩展。因此我们需要定义一组规则和概念来实现这个目标
  5. 为了让跨平台通信框架更加易于使用,我们需要提供一套序列化和反序列化的框架,这样可以让通信以自定义结构体的形式进行,而非数据流。
  6. 如果存在大量的通信消息,那么我们需要解决不同进程共享头文件,使用自定义结构体的形式;另外,Mojo除了跨平台,还需要夸语言,那么,我们必然不能使用某种语言的结构体定义形式(例如C的结构体或者JavaScript友好的Json),而是需要定义一种新的规则,通过工具自动生成不同语言友好的结构体源码

上面的5和6本质上可以看作是同一个问题,并且我首先想到的是protobuffer库可以实现这两个问题的解法。

带着我们自己的设计思路,再去看看Mojo的设计方案。

的Mojo方案:

源码目录体积一览:

在这里插入图片描述
源码重要文件一览(排除test源文件,按大小排序,top的文件如下图):
在这里插入图片描述
一般来说,体积大的源文件表示的功能都比较核心,因此这些文件里面对应的功能和概念,很大概率是Mojo框架的核心,值得每一项进行标注理解。

Mojo的底层Channel

在Mojo中的源码中,可以很明显的发现关键字Channel是最底层跨进程通信的关键概念,并且能找到平台相关的抽象和实现,直接搜索文件名关键字就一览无余了:
在这里插入图片描述

channel.h

看了一眼mojo\core\channel.h可以发现,抽象基类是mojo::core::Channel, 并且定义了许多核心概念,包括Message(A message to be written to a channel.)、Delegate( // Delegate methods are called from the I/O task runner with which the Channel
was created (see Channel::Create). 关键回调OnChannelMessage)等。
不同平台通过继承抽象基类mojo::core::Channel实现平台相关的读写,以mojo\core\channel_win.cc的ChannelWin为例,使用Win32API的ReadFile、WriteFile从base::win::ScopedHandle句柄中读写数据,这个句柄是构造函数的参数ConnectionParams(TakeEndpoint().TakePlatformHandle().TakeHandle())传入的获取的。

channel相关的重点类解析

  • PlatformChannel
    是一个封装了两个交织在一起的端点的类或结构,这些端点属于特定平台的基本通信原语,比如在Windows上是管道,在Unix系统上是域套接字,在macOS上是Mach端口对。其中一个端点被指定为“本地”端点,由创建它的进程保留;另一个端点被指定为“远程”端点,应当传递给外部的进程。

  • PlatformChannel 可以用来在两个进程之间启动Mojo IPC(一种进程间通信机制)。通常情况下另一个进程是当前进程的子进程,PlatformChannel
    提供辅助方法来将端点以这种方式传递给子进程;但这种设置在所有平台上并不是强制性的。
    如果需要一个允许客户端通过名称来连接的通道(比如一个命名管道或者套接字服务器,这种类型仅在Windows和POSIX系统上被支持),那么可以参考
    NamedPlatformChannel。

  • PlatformChannelServer 这个类负责持有一个 PlatformChannelServerEndpoint 实例,并监听一个单一的来自客户端的连接请求。这个类不是线程安全的,必须在运行I/O消息泵(Message Pump)的线程上使用。

  • PlatformChannelServer 和PlatformChannel 对比: 简而言之,PlatformChannel 负责创建和管理进程间通信的通道,而 PlatformChannelServer
    则是在服务端监听和接受这些通道上的连接请求。PlatformChannel 可以看作是连接的“管道”,而 PlatformChannelServer 是“水龙头”,控制着连接的开启。

  • PlatformHandle平台句柄类,它带有一些额外的类型信息,用来表明它是一个通道端点(channel endpoint)。也就是说,它是一种句柄,可以被用来作为 MOJO_INVITATION_TRANSPORT_TYPE_CHANNEL
    发送或接收邀请到一个远程的 PlatformChannelEndpoint

结合调用堆栈:

Channel的创建:
在这里插入图片描述
Channel的发送消息:
在这里插入图片描述
通过调用堆栈可以发现,Channel的概念几乎是Mojo最底层的概念了,往上走有Router、InterfaceEndpointClient、Message、ChannelMojo概念等。以下是从最底层到更高层的一些核心概念及其作用的介绍:

  1. Channel:
    Channel 是 Mojo IPC 系统中最底层的抽象。它代表了一个底层的通信通道,负责在两个进程之间传输原始的字节数据。Channel 封装了操作系统级别的 IPC 机制(如套接字或共享内存),以便在不同的平台上提供一致的 API。它负责序列化和反序列化消息,保证数据的完整性,并处理底层的传输细节。

  2. Router:
    Router 位于 Channel 之上,是一个稍高层次的抽象。它负责将发出的消息路由到正确的 Channel,并从 Channel 接收消息。Router 还管理消息的生命周期,确保消息按照正确的顺序发送和接收,并可能处理流控制和重试逻辑。

  3. InterfaceEndpointClient:
    InterfaceEndpointClient 是 Mojo IPC 中的一个组件,它代表了 Mojo 接口的端点。它与 Router 配合,以便在 Mojo 接口上发送和接收消息。通常,每个 Mojo 接口都有一个或多个方法,这些方法对应于可以通过该端点发送的消息类型。InterfaceEndpointClient 会序列化这些方法调用为消息,并将它们传递给 Router 进行传输。

  4. Message:
    Message 是代表 IPC 系统中传递的一个消息实体。它通常包含要传输的数据(例如,方法调用的参数),以及可能的元数据(例如,消息类型或优先级)。在 Mojo IPC 中,Message 是发送和接收的基本单位,由 RouterChannel 处理。

  5. ChannelMojo:
    ChannelMojoChannel 的一个具体实现,它利用 Mojo 系统的底层管道(如 MessagePipe)来传输数据。ChannelMojo 提供了一个适应 Mojo 管道特性的 Channel 接口,使得上层的 RouterInterfaceEndpointClient 能够通过 Mojo 管道发送和接收消息。

这些概念共同构成了 Mojo IPC 系统的框架,其中每个层次都建立在下一个层次之上,提供了逐步更高级别的抽象和功能。开发者可以根据需要选择在哪个层次上与 IPC 系统交互,从直接使用 Channel 的字节级操作,到通过 InterfaceEndpointClient 的接口级调用。

Mojo的Node

在源码一览中,我们发现node.cc是最大的源文件,我们以此为线索展开对Node的理解和阅读,Node相关文件有:
在这里插入图片描述
Node.h中,NodeChannel是一个核心,注释只有一句:Wraps a Channel to send and receive Node control messages.

// Wraps a Channel to send and receive Node control messages.
class MOJO_SYSTEM_IMPL_EXPORT NodeChannel: public base::RefCountedDeleteOnSequence<NodeChannel>,public Channel::Delegate {public:// .... 略

由此可见,Channel 是一种底层概念,用于抽象化和平滑处理不同平台之间的差异,而 Node 概念则在 Channel 的基础上进行了进一步的封装和抽象化。NodeChannel用于定义 Mojo 中与连接、广播、中介(Broker)、消息传输以及错误处理相关的实现细节。如果用计算机网络的术语进行类比,那么 Channel 类似于网络协议栈中的 IP 层,它提供了寻址和路由的能力;而 NodeChannel则相当于应用层的协议,例如 UDP,它在更高层次上处理数据的传输和相关逻辑。

那么,可以预见,Mojo的应用层概念将围绕Node为核心展开。

从上面代码中我们发现,NodeChannel中有个重要的嵌入类:Delegate。Delegate的概念在chromium广泛存在,其实可以理解为Delegate就是一组回调,在宿主对象处理逻辑的关键节点时,通过Delegate回调转移执行绪,以实现行为的定制和扩展的能力。 通过了解Delegate回调的函数组成,可以快速了解宿主类的主要功能和关键流程,是阅读源码的重要技巧。例如NodeChannel的Delegate的类定义如下:

 class Delegate {public:virtual ~Delegate() = default;virtual void OnAcceptInvitee(const ports::NodeName& from_node,const ports::NodeName& inviter_name,const ports::NodeName& token) = 0;virtual void OnAcceptInvitation(const ports::NodeName& from_node,const ports::NodeName& token,const ports::NodeName& invitee_name) = 0;virtual void OnAddBrokerClient(const ports::NodeName& from_node,const ports::NodeName& client_name,base::ProcessHandle process_handle) = 0;virtual void OnBrokerClientAdded(const ports::NodeName& from_node,const ports::NodeName& client_name,PlatformHandle broker_channel) = 0;virtual void OnAcceptBrokerClient(const ports::NodeName& from_node,const ports::NodeName& broker_name,PlatformHandle broker_channel,const uint64_t broker_capabilities) = 0;virtual void OnEventMessage(const ports::NodeName& from_node,Channel::MessagePtr message) = 0;virtual void OnRequestPortMerge(const ports::NodeName& from_node,const ports::PortName& connector_port_name,const std::string& token) = 0;virtual void OnRequestIntroduction(const ports::NodeName& from_node,const ports::NodeName& name) = 0;virtual void OnIntroduce(const ports::NodeName& from_node,const ports::NodeName& name,PlatformHandle channel_handle,const uint64_t remote_capabilities) = 0;virtual void OnBroadcast(const ports::NodeName& from_node,Channel::MessagePtr message) = 0;
#if BUILDFLAG(IS_WIN)virtual void OnRelayEventMessage(const ports::NodeName& from_node,base::ProcessHandle from_process,const ports::NodeName& destination,Channel::MessagePtr message) = 0;virtual void OnEventMessageFromRelay(const ports::NodeName& from_node,const ports::NodeName& source_node,Channel::MessagePtr message) = 0;
#endifvirtual void OnAcceptPeer(const ports::NodeName& from_node,const ports::NodeName& token,const ports::NodeName& peer_name,const ports::PortName& port_name) = 0;virtual void OnChannelError(const ports::NodeName& node,NodeChannel* channel) = 0;};

通过这个代理类,就能很直观地理解NodeChannel的功能和作用。

mojo的Port

在阅读NodeChannel类的时候,有一个关键字出现了多次,那就是port。在Mojo中,port是一个命名空间,也是一个重要概念,port这个类的头文件注释如下:

在 Mojo IPC 系统中,“Port”本质上是一个地址的循环列表中的一个节点。为了本文档的目的,这样的列表将被称为“路由”(route)。路由是所有 Node 事件流通的基本媒介,因此是所有 Mojo 消息传递的骨干。
每个 Port 都由一个节点(参见 node.h)内的 128 位地址唯一标识。Port 本身并不真正“做”任何事情:它是一系列状态的命名集合,而拥有它的 Node 管理所有事件的产生、传输、路由和处理逻辑。有关 Port 如何被用来传输任意用户消息以及其他 Ports 的更多细节,请参见 Node。
Ports 可以处于几种状态(见下面的 State),这些状态决定了它们如何响应以它们为目标的系统事件。在最简单和最常见的情况下,Ports 最初是作为一对纠缠在一起的状态(即由两个 Ports 组成的简单循环)创建的,都处于 kReceiving 状态。我们这里将这些 Ports 标为 |A| 和 |B|,它们可以使用 Node::CreatePortPair() 创建:

    +-----+          +-----+|     |--------->|     ||  A  |          |  B  ||     |<---------|     |+-----+          +-----+

|A| 通过 |peer_node_name| 和 |peer_port_name| 引用 |B|,同时 |B| 反过来引用
|A|。请注意,一个 Node 永远不会知道是谁向给定的 Port 发送事件;它只知道必须从给定的 Port 路由事件到哪里。
为了方便文档描述,我们将路由中的一个接收端 Port 称为另一个的“共轭”(conjugate)。一个接收端 Port 的共轭在初始创建时也是它的对端,但由于代理,这种关系可能随着时间而改变。 对这个数据结构的所有访问必须通过获取 |lock_|来进行保护,这只能通过 PortLocker 实现。PortLocker 确保在单个线程上重叠的 Port 锁获取总是以全局一致的顺序进行。

通过头文件注释,感觉似懂非懂,看看Port这个类的主要成员和方法吧:

Port 类是 Mojo IPC 系统中的一个核心组件,它代表了消息传递路径上的一个节点。这个类继承自
base::RefCountedThreadSafe<Port>,允许它在多个线程中安全地共享和管理其生命周期。以下是 Port
类的主要功能和特性:

  • State 枚举:定义了 Port 可能处于的状态,包括 kUninitialized(未初始化)、kReceiving(接收中)、kBuffering(缓冲中)、kProxying(代理中)和
    kClosed(已关闭)。
  • state 成员变量:存储当前 Port 的状态。
  • peer_node_namepeer_port_name 成员变量:指定了事件应该从该 Port 路由到哪个节点和端口的地址。
  • prev_node_nameprev_port_name 成员变量:跟踪当前发送消息到这个 Port 的上一个端口,用于验证发送方节点是否有权限发送消息到这个端口,同时保持接收消息的顺序。
  • pending_merge_peer 成员变量:标记这个端口是否准备合并。
  • 一系列的序列号成员变量(next_control_sequence_num_to_sendnext_sequence_num_to_send
    等):用于跟踪控制和用户消息事件的序列号。
  • message_queue 成员变量:存储该 Port 接收到的用户消息队列。此队列只为 kBufferingkReceiving 状态的 Port 提供服务。
  • control_message_queue 成员变量:在 Port 处于 kBuffering 状态时,暂存即将发送的控制消息。
  • send_on_proxy_removal 成员变量:在某些边缘情况下,如果这个(代理中的)Port 被销毁,它可能需要记得路由一个特殊的事件。
  • user_data 成员变量:附加到 Port 的任意用户数据。在 Mojo 中,这通常用于存储通知有关 Port 状态变化的观察者接口。
  • remove_proxy_on_last_messagepeer_closed 成员变量:标志位,用于指示 Port 的一些状态,如代理何时可以移除,以及它的对端 Port 是否已关闭。
  • Port 构造函数:用于初始化 Port,设置初始的序列号。
  • AssertLockAcquired 方法:用于调试中检查是否已获取 Port 的锁。
  • IsNextEvent 方法:检查给定的事件是否应该根据序列号和发送方节点接下来处理。
  • NextEvent 方法:获取下一个要处理的缓冲事件。
  • BufferEvent 方法:将事件缓存以供后续处理。>
  • TakePendingMessages 方法:清空等待节点验证的事件队列,并返回所有用户事件。
  • 私有析构函数 ~Port:确保 Port 只能通过引用计数安全地销毁。
  • 私有成员 lock_:用于确保对 Port 数据结构的线程安全访问。
  • PortLocker 友元类:用于确保在单个线程上以全局一致的顺序获取重叠的 Port 锁。
    该类的设计允许它在 Mojo IPC 系统中作为消息的发送和接收点,管理消息的顺序和状态,并确保消息在正确的路径上流动。

结合其他源码,发现Port和Dispatcher相关逻辑结合紧密,另外,Port存储了Event的序号等数据信息,支持插入事件,并且许多数据成员用于Node.cc中实现消息处理,可见Port这个类做的事情确实很难和已有的概念类比出来,也难怪通过这个类的注释难以一下理解其作用。简而言之,Port这个类即负责一部分事件排序和派发相关的逻辑处理,也承载了一个寻址的功能。

在 Mojo IPC 系统中,Node 通常代表了一个独立的参与者,如一个进程,它是消息传递路径上的一个物理节点。Node 可以是消息的最初发送者或最终接收者。相比之下,Port 是 Node 内部的逻辑上的虚拟节点,它负责管理消息的复杂路由、转发以及过滤等操作。每个 Port 都由其所属的 Node 管理,并且可以与其他 Node 中的 Port 形成连接,从而构成消息传递的网络。
之所以这样设计,是为了让两个Node之间可以出现多个连接,每个连接就是一对“共轭”的Port。这样每个连接各自的序号(seq)就不会互相干扰。所以,序号的数据就存储在Port类里,这也就不奇怪了。正因为Port代表了连接,所以数据的过滤和代理也必须面向连接进行,因此Port也和相关的类紧密联系。

说实话,如果把Port改名为Connection,也许会更直观一些。

这里面出现了NodeName和Port Name,也顺便看看定义:

struct COMPONENT_EXPORT(MOJO_CORE_PORTS) PortName : Name {constexpr PortName() : Name(0, 0) {}constexpr PortName(uint64_t v1, uint64_t v2) : Name(v1, v2) {}
};
struct COMPONENT_EXPORT(MOJO_CORE_PORTS) NodeName : Name {constexpr NodeName() : Name(0, 0) {}constexpr NodeName(uint64_t v1, uint64_t v2) : Name(v1, v2) {}
};

接下来

接下来,我们继续阅读Mojo模块的代码。了解消息的过滤和派发、序列化和反序列化、Mojom、等功能逻辑。
Chromium源码阅读:深入理解Mojo框架的设计思想,并掌握其基本用法(2)

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

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

相关文章

全氟己酮自动灭火材料表现亮眼!手把手教你自动灭火毯的使用方法

灭火毯的使用方法是什么&#xff1f;很多朋友在购买灭火毯之前&#xff0c;都比较关心这个问题。在这里&#xff0c;我们可以把灭火毯分为两种。一种是传统灭火毯&#xff0c;还有一种是近年来兴起的高科技产品—全氟己酮自动灭火毯。这两种灭火毯的使用方法大有不同&#xff0…

游戏服务器研究一:bigworld 开源代码的编译与运行

1. 前言 bigworld 已经开源了它的代码&#xff0c;而我对于大世界的 scale 很感兴趣&#xff0c;所以就尝试把代码跑起来研究。但是&#xff0c;整个过程比我原先预想的复杂得多。 虽然能找到一些官方的帮助文档&#xff0c;但这些文档要么过旧&#xff0c;要么过于详尽&…

表面声波滤波器——设计方案(4)

设计步骤 设计声表面波滤波器&#xff0c;首先需要分析器件的指标要求&#xff0c;如中心频率、使用带宽、插入损耗等&#xff0c;结合产线工艺水平&#xff0c;选择合适的衬底材料和换能器材料。确认可以满足器件性能需求的换能器设计方案&#xff0c;然后通过软件仿真。对叉…

python基础语法 002 - 4 字符串

1 字符串 字符串&#xff1a;引号括起来的数据类型 # 双引号 a "yuze wang"# 单引号 a ’yuze wang‘# 三引号 a ’‘’yuze‘‘’ a """yuze"""注意&#xff1a;所有格式表示都是半角&#xff0c;全角会报错 1.1 引号表示 …

IMU应用于体操训练

考虑到在艺术体操训练与竞赛中艺术体操的训练与比赛中&#xff0c;地板项目导致的伤率最高&#xff0c;最近&#xff0c;一个来自澳大利亚的科研团队利用IMU评估运动员执行基础翻腾技巧训练时&#xff0c;他们上肢与下肢所承受的冲击负荷。 本次实验共有十四名艺术体操运动员参…

Profibus协议转Modbus协议网关模块帮助PLC实现智能激光设备通讯

一、前言 Profibus转Modbus网关&#xff08;XD-MDPB100&#xff09;是一种工业通信协议转换设备&#xff0c;用于实现Profibus协议与Modbus协议之间的转换。Profibus转Modbus网关在工业自动化系统中具有广泛的应用&#xff0c;它解决了不同协议设备之间的通信问题。本文将深入…

《Nest系列 - 2. Nest 代码生成器,让你告别base代码书写!!!》

紧接上文我们做一些核心梳理 核心梳理&#xff1a; /controllers目录&#xff1a;存放控制器文件&#xff0c;每个控制器对应一组路由和请求处理方法。控制器处理来自客户端的HTTP请求&#xff0c;并返回相应的响应。/modules目录&#xff1a;存放模块文件&#xff0c;每个模块…

学会这几点,轻松制作引人入胜的电子期刊

随着数字化时代的到来&#xff0c;电子期刊已经成为了信息传播的重要载体。它以方便快捷、形式多样、互动性强等特点&#xff0c;受到了广泛的欢迎。那么&#xff0c;如何制作一份引人入胜的电子期刊呢&#xff1f;下面就来为大家分享几点制作电子期刊的小技巧。 1.选择合适的制…

Linux实现: 客户端(cli01)通过TCP(或UDP)连接到聊天服务器(serv)进行聊天?(伪代码版本)

&#x1f3c6;本文收录于「Bug调优」专栏&#xff0c;主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案&#xff0c;希望能够助你一臂之力&#xff0c;帮你早日登顶实现财富自由&#x1f680;&#xff1b;同时&#xff0c;欢迎大家关注&&收藏&&…

爬抖音直播间观众数据

打开抖音&#xff0c;稍微看了下买房直播间&#xff0c;突然很好奇是那些用户在观看&#xff0c;想拿下这些用户数据&#xff0c;再通过用户等级排序&#xff0c;筛选出优质客户。 现在用户数据已经拿到了&#xff0c;但是关键数据&#xff0c;抖音是打码加密的。目前最大的难…

前端面试项目细节重难点(已工作|做分享)(九)

面试官&#xff1a;请你讲讲你在工作中如何开发一个新需求&#xff0c;你的整个开发过程是什么样的&#xff1f; 答&#xff1a;仔细想想&#xff0c;我开发新需求的过程如下&#xff1a; &#xff08;1&#xff09;第一步&#xff1a;理解需求文档&#xff1a; 首先&#x…

Java——面向对象进阶(三)

前言&#xff1a; 抽象类&#xff0c;接口&#xff0c;内部类 文章目录 一、抽象类1.1 抽象方法1.2 抽象类1.3 抽象类的使用 二、 接口2.1 接口的定义和实现2.2 default 关键字2.3 实现接口时遇到的问题 三、内部类3.1 成员内部类3.2 静态内部类3.3 成员内部类3.4 匿名内部类&a…

6月17(信息差)

1.马斯克最新预测&#xff1a;未来不再需要手机 将被脑机芯片替代 当地时间6月17日&#xff0c;马斯克高仿号“Not Elon Musk”发帖称&#xff1a;“你会在你的大脑上安装一个Neuralink接口&#xff0c;让你通过思考来控制你的新X手机吗&#xff1f;”对此&#xff0c;马斯克本…

C++ 62 之 冒泡排序

#include <iostream> // #include <string> #include <cstring>using namespace std;// 冒泡排序:函数模板 template<typename T> void mySort(T arr[], int len){ // size参数是数组的个数&#xff0c;一定是int型的for (size_t i 0; i < len -1;…

88. 合并两个有序数组(简单)

88. 合并两个有序数组 1. 题目描述2.详细题解3.代码实现3.1 Python3.2 Java 1. 题目描述 题目中转&#xff1a;88. 合并两个有序数组 2.详细题解 两个数组均有序&#xff08;非递减&#xff09;&#xff0c;要求合并两个数组&#xff0c;直观的思路&#xff0c;借助第三个数…

利用AI云防护实现高效负载均衡

在当今高度数字化的世界里&#xff0c;保证网站和应用的高可用性和响应速度对企业的业务连续性和用户体验至关重要。传统的负载均衡技术虽然能够分发流量&#xff0c;但在面对突发流量、DDoS攻击或资源动态调整时往往力不从心。本文将探讨如何借助AI云防护服务&#xff0c;不仅…

超图论文细品——2019年AAAI《Hypergraph Neural Networks》

我是“导航” 1 摘要1.1 简介1.2 问题描述 2 超图2.1 图和超图对比 参考 1 摘要 1.1 简介 文章提出了一种名为超图神经网络的框架&#xff0c;用于高维数据的表示学习。 该方法英文称呼为 Hypergraph Neural Networks&#xff0c;简写为 HGNN。 1.2 问题描述 传统的 GNN 是…

大模型精调:实现高效迁移学习的艺术

在人工智能领域&#xff0c;大型预训练模型&#xff08;以下简称“大模型”&#xff09;已经取得了令人瞩目的成果。这些模型通过在海量数据上进行预训练&#xff0c;能够捕捉到丰富的特征信息&#xff0c;为各种下游任务提供强大的支持。然而&#xff0c;如何将这些大模型应用…

graalvm编译springboot3 native应用

云原生时代容器先行&#xff0c;为了更好的拥抱云原生&#xff0c;spring boot3之后&#xff0c;推出了graalvm编译boot项目&#xff0c;利用jvm的AOT&#xff08; Ahead Of Time &#xff09;运行前编译技术&#xff0c;可以将java源码直接构建成机器码二进制的文件&#xff0…

(资料收藏)王阳明传《知行合一》共74讲,王阳明知行合一音频讲解资料

今天给大家带来的不是软件&#xff0c;而是一份精神食粮——《知行合一》的教程福利。这可不是一般的教程&#xff0c;它关乎心灵&#xff0c;关乎智慧&#xff0c;关乎我们如何在纷繁复杂的世界中找到自己的位置。 咱们得聊聊王阳明&#xff0c;这位明代的大儒&#xff0c;他…