当前位置: 首页 > news >正文

仓颉语言运行时轻量化实践

杨勇勇

华为语言虚拟机实验室架构师,目前负责仓颉语言静态后端的开发工作

仓颉语言运行时轻量化实践

仓颉Native后端(CJNative)是仓颉语言的高性能、轻量化实现。这里的“轻量化”意指仓颉程序运行过程中占用系统资源(内存、CPU等)较少。轻量化对于运行在资源受限场景(典型如嵌入式、移动设备等)的程序具有重要意义,占用资源越少,系统负载越小,程序运行更加平稳,用户体验更好。

本文简要谈谈仓颉语言运行时如何实现轻量化。如下图1描绘了仓颉Native后端架构,包含了仓颉程序编译和运行的流程,仓颉的轻量化运行时(libcangjie-runtime.so)是其中核心组件之一。

图 1 仓颉Native后端架构

仓颉源码被仓颉编译器(cjc)编译后生成仓颉可执行文件,仓颉库被编译后生成仓颉库文件,仓颉可执行文件及其依赖的库文件和仓颉轻量运行时三部分共同部署在执行环境中运行。仓颉轻量运行时的关键作用之一是帮助仓颉应用“轻量”地运行。

仓颉语言运行时的轻量化可以归纳为如下5个方面:

  1. 轻量对象布局
  2. 轻量类库和运行时库
  3. 轻量用户态线程
  4. 轻量CFFI
  5. 轻量回栈

下面分别一一介绍。

1 轻量对象布局

仓颉运行时采用精简的对象布局,图2示例了64位平台上的对象布局,起始处存放对象的类型信息,最多占用一个指针宽度的内存,且在64位平台上,可以压缩到4字节。

仓颉采用这个对象布局,可以最大程度减少内存用量。

这个精简布局的得以实施,收益于几个因素:

  1. 只有对象的类型信息才是对象头中必不可少的数据。除此以外,如果还有空余bit,可以用于辅助运行时优化。
  2. 仓颉语言规范里指定了专有数据类型用作并发编程解决数据竞争的手段,从而在对象头中不需要预留用于实现锁机制的数据区。这个设计的原因是绝大部分对象在其生命周期内都不会被用于并发编程。
  3. 仓颉采用了Tracing GC算法而不是基于引用计数实现的自动内存管理,不需要为引用计数预留存储位置。
  4. 仓颉的GC算法避免了采用转发指针,而是采用转发表记录存活对象被搬移后的新旧地址映射关系,从而对象头中不需要预留转发指针的存储位置。

2 轻量类库和运行时库

从图1可知,仓颉程序运行过程需要链接相关类库以及运行时库。仓颉程序执行的最小系统由仓颉的核心库(libcangjie-std-core.so)和仓颉轻量运行时(libcangjie-runtime.so)构成。仓颉的核心库经过精心的设计剪裁,只保留了仓颉程序启动过程中必不可少的如Array、String、Atomic、Object、Exception等类型定义构成了自包含的核心类库,其余仓颉类型被有序地组织在其它类库里。仓颉程序在编译链接的过程中,除核心类库必须链接外,其余类库都可实现按需链接加载。在静态链接模式下,仓颉程序可以实现函数级别的按需链接,最大程度减少仓颉程序依赖的库文件体积。

与此同时,下述软件工程手段也被积极用于运行时库的轻量化改造:

  1. 虽然仓颉运行时库采用C++语言开发,但是C++异常特性被禁止使用(-fno-exceptions),可以减少异常表等二进制数据。
  2. 进一步,禁用dynamic_cast等特性,可以配合-fno-rtti避免生成C++类型信息。
  3. 减少以及避免使用C++模板库,对于必不可少的常用数据类型(如string)采用自定义方式实现,可以避免依赖C++运行时库。C++运行时库对于代码体积和内存敏感的场景显得过大。
  4. 通过-Os编译选项以及链接时优化(LTO)可以进一步减少仓颉运行时库的文件体积。

通过上述软件工程实践,仓颉的核心库libcangjie-std-core.so文件大小约为600KB,仓颉的运行时库libcangjie-runtime.so文件大小约为700KB.

3 轻量用户态线程

仓颉语言采用用户态轻量级线程模型。用户态线程是指由仓颉运行时实现的线程模型,线程创建过程不涉及系统调用、线程调度不需要进入内核态。用户态线程在运行过程中的内存开销和调度开销相比系统线程普遍更小,也是仓颉轻量运行时的重要特征之一。

在资源消耗上,仓颉轻量级线程可以由用户自定义仓颉线程栈大小,最低只需要KB级别。在切换调度上,仓颉轻量级线程避免用户态与内核态的频繁切入切出,单次切换开销仅需要100ns。因此,仓颉轻量级线程适用于高并发场景,对于每秒钟上千数万的单机访问量能够较快处理。

仓颉的轻量级线程模型包含了执行线程、工作任务、监视器(monitor)、处理器(processor)、与调度器等几个基本模块。各个模块承担不同的任务,使得仓颉进程可以在充分利用多核心硬件资源的基础上进一步地扩展其并发能力。

  1. 执行线程:用来执行真实调度任务的载体,依赖操作系统提供的线程接口。与C/C++等本地编程语言相比,仓颉语言不需要通过增加操作系统线程数来作为提高并发量的保证,只需要提供和硬件资源相当的线程即可。
  2. 工作任务:可在用户态调度的仓颉线程的实例,多个仓颉线程之间的调度关系是平等的,它们可以在任意执行线程间切换。未被执行的工作任务会保存在工作队列中,等待工作线程来执行。
  3. 处理器:负责处理执行线程与工作任务的绑定关系,每一个处理器都维护了一个本地工作队列,这个工作队列中记录了等待执行的工作任务,当某一个处理器的本地工作队列中没有任务时,它会尝试从全局队列中获取新的任务到本地队列。
  4. 调度器:负责处理仓颉线程的调度策略。调度器包含了工作任务的调度算法,保证工作任务可以被公平地切换与执行。
  5. 监视器:具有全局视野的独立线程,用来观察各个仓颉线程的状态,避免出现某一仓颉线程占据过长CPU时间片,或者事件到来时,正在等待的线程不能及时响应等情况。

程序启动后,随着仓颉轻量化线程的创建,承载工作任务的执行线程依次进入各个处理器的本地队列中等待被调度和执行。如果所有本地队列等待执行的线程数量达到上限,则后续再新增的线程被加入到全局队列中按序等待。每个处理器查找下一个可运行线程的顺序是优先从本地队列获取执行,若本地队列为空则从全局队列获取执行。为提高线程调度效率,仓颉并发模型进一步做出调度优化策略:①空闲的处理器可以“窃取”其他处理器本地队列中等待的线程;②监视器监控所有处理器的调度情况,每个处理器调度一定数量后,会从全局队列获取单个线程调度,防止全局队列中的线程被“饿死”;③监控器监控所有线程的执行情况,对于连续执行时间超过阈值的线程,则对其进行“抢占”,使其让出处理器。

仓颉轻量化线程可以在用户态完成仓颉任务的切换,因此,在高并发场景下,相比于操作操作系统线程,仓颉轻量化线程进入内核态的次数将大大降低。仓颉轻量化线程也具备私有的栈空间,并且具备动态栈扩容的能力。用户可以根据真实的业务场景,按需配置实始栈空间大小。除非是对性能极为敏感的业务场景,否则用户不需要将栈空间内存配置得很大,即便用户配置的初始栈空间大小不满足某一线程所需要的栈内存,栈扩容能力仍然可以保证业务可以正常地执行。

4 轻量CFFI (Foreign Function Interface for C)

C语言是广泛使用的系统编程语言,所以与C语言交互是所有受管语言必备的特性。在仓颉程序中,一个被foreign关键字修饰的仓颉函数对应着一个C函数的定义,这样的仓颉函数称为仓颉外部函数(Foreign Function),可以被仓颉语句直接调用,如下述示例:

foreign func foo():Unit

对应着C语言定义的函数如下

void foo();

仓颉程序调用方式如下:

// 调用foo的仓颉源码

  foo()

这是非常简洁易用的CFFI语法形式。

仓颉编译器在编译阶段为调用外部函数(如上述的foo函数)插入了调用桩函数的代码,通过该桩函数为调用外部函数做准备。仓颉编译器会根据外部函数的签名生成代码,把外部函数的入口地址和入参准备好传给桩函数,在桩函数中只需完成如下基本操作:

  1. 保护仓颉函数调用的上下文,用于回栈过程中构建仓颉函数调用链;
  2. 进入GC安全区,确保仓颉线程调用外部函数的过程中可以正常与GC线程同步;
  3. 设置外部函数的入参;
  4. 执行外部函数代码;
  5. 离开GC安全区;
  6. 重置仓颉函数调用的上下文;

在仓颉中调用外部函数还有更轻量的实现形态。如果与外部函数对应的C函数执行时间较短(耗时在微秒级或更少),从而不会显著影响仓颉线程与GC的同步,那么可以使用@FastNative修饰该外部函数,实现更快的CFFI调用性能。通过@FastNative修饰的外部函数在调用过程中不需要经过桩函数处理,仓颉编译器生成的代码直接调用对应的C函数,达到接近C语言本地调用函数的性能。

5 轻量回栈

回栈(Unwinding Stack)操作是指对一个正确的调用栈通过从被调函数反向查找其调用者、以此构建部分或者全部调用链的过程。回栈操作可以从满足条件的任意某个函数发起,通过读取该函数信息以及运行栈的其它相关信息,依次找到它的调用者、以及调用者的调用者,从而构建出来所需的调用链。回栈的使用场景包括打印调用栈信息、实现异常处理、帮助GC处理栈上的根引用等多种用途。

CJNative的回栈是基于帧指针(Frame Pointer, FP)实现的,即在运行中保持栈基址寄存器始终存储当前执行帧的栈基址,并在每次调用的起点将上一帧的栈基址写入本帧的起始位置,形成每个帧都记录着上一帧的链式结构。在回栈时每次根据本栈帧的基址找到上一帧的基址,层层向上,实现回栈流程。这种实现的优势在于回栈流程简洁,速度极快,对于实现低时延的并发GC尤为必要,劣势在于必须持续占用一个物理寄存器和以及维护帧指针的额外指令开销,要求编译器生成相应的指令。另一种业界常用的回栈方式是基于CFI信息的回栈。CFI信息是编译时生成的一组额外的伪指令,描述了回栈过程中如何根据寄存器和帧数据恢复栈基址和link register等与调用链有关的寄存器。通过CFI恢复寄存器的操作需要模拟CFI指令的演算过程,从而导致回栈性能开销大、速度较慢。

综合来看,基于的FP回栈操作流程简洁,计算开销低,耗时较短(基础回栈操作耗时约零点几个微秒),在性能上有着非常好的表现,满足轻量化运行时的回栈要求。

6 小结

上述罗列了CJNative轻量化设计及实现的几个主要方面,对于其它有益于仓颉语言“高性能、轻量化”实现的重要技术,将会另文描述。我们在内部典型嵌入式场景下评估,定制的仓颉语言运行时二进制文件约700KB,应用启动时间小于10ms,空载应用(main函数中调用sleep)占用内存约800KB,验证了仓颉语言可以帮助开发者编写更轻量的应用程序、让应用更轻量地部署及运行。

相关文章:

仓颉语言运行时轻量化实践

杨勇勇 华为语言虚拟机实验室架构师,目前负责仓颉语言静态后端的开发工作 仓颉语言运行时轻量化实践 仓颉Native后端(CJNative)是仓颉语言的高性能、轻量化实现。这里的“轻量化”意指仓颉程序运行过程中占用系统资源(内存、CPU等…...

深入理解Python中的subprocess模块

目录 subprocess模块简介常用函数执行外部命令管道通信子进程管理错误处理实际应用示例最佳实践 subprocess模块简介 subprocess模块是Python标准库的一部分,提供了一个跨平台的方法来生成新进程、连接其输入/输出/错误管道,并获取其返回码。该模块旨…...

从零开始搭建 EMQX 集群压测框架

从零开始搭建 EMQX 集群压测框架 架构 在设计以EMQX为中心的MQTT消息队列集群压力测试框架时,我们采用微服务架构模式。EMQX作为消息队列的核心,负责处理MQTT协议的消息发布和订阅。Nginx作为EMQX的反向代理,负责负载均衡和SSL/TLS终端。MQT…...

ArkUI基本介绍

ArkUI:提供HarmonyOS应用UI开发框架,几件开发、精致体验、跨设备/跨平台。 ArkUI(方舟UI框架)为应用的UI开发提供了完整的基础设施,包括简洁的UI语法、丰富的UI功能(组件、布局、动画以及交互事件&#xff…...

vue2+OpenLayers 天地图上打点并且显示相关的信息(2)

上次是在地图上打点 这次鼠标移动在图标上面显示相关的信息 首先有两个事件 鼠标移入 和 鼠标移出事件 pointermove pointerout 鼠标放上去之前 放上去后 代码如下 <template><div class"container"><div id"vue-openlayers" class&quo…...

c++继承(二)

一、友元函数的继承 友元函数不能被继承&#xff0c;就像爸爸的朋友不是你的朋友&#xff0c;如果要有友元函数&#xff0c;在子类重新定义一个。 二、静态成员的继承 静态成员的继承仍然是那个成员&#xff0c;普通成员的继承是不同的。 父类的静态成员属于当前类&#xf…...

低代码开发的崛起:机遇与挑战

近年来&#xff0c;“低代码”开发平台的迅速崛起&#xff0c;已经成为IT行业中不可忽视的趋势。这些平台承诺让非专业人士也能快速构建应用程序&#xff0c;通过减少代码编写的需求&#xff0c;大幅提高开发效率。对于许多企业而言&#xff0c;低代码开发工具成为了一个加速数…...

Json-JacksonUtils工具类

为了创建一个通用的 Jackson 工具类,我们可以定义一个名为 JacksonUtils 的工具类,该类将提 供多种方法来支持不同类型的 JSON 转换需求。下面是一个示例实现,包括基本的 JSON 到 Java 对象的转换、Java 对象到 JSON 的转换、以及更复杂的类型如 CommonResult 的转换。 C…...

svn客户端装完后没有svn.exe

如果SVN客户端&#xff08;如TortoiseSVN&#xff09;安装完成后&#xff0c;在预期的安装目录&#xff08;通常是bin目录&#xff09;中没有找到svn.exe文件&#xff0c;这通常是因为在安装过程中没有选择安装命令行客户端工具&#xff08;Command Line Client Tools&#xff…...

TinyWebserver的复现与改进(4):主线程的具体实现

GitHub - yzfzzz/MyWebServer: Linux高并发服务器项目&#xff0c;参考了TinyWebServer&#xff0c;将在此基础上进行性能改进与功能增加。为方便读者学习&#xff0c;附带详细注释和博客&#xff01; TinyWebserver的复现与改进&#xff08;1&#xff09;&#xff1a;服务器环…...

DaemonSet 不能帮助我们做什么事情?

DaemonSet 不能帮助我们做什么事情&#xff1f; A. 保证集群内每一个(或者一些)节点都运行一组相同的Pod B. 跟踪集群节点状态&#xff0c;保证新加入的节点自动创建对应的Pod C. 跟踪集群节点状态&#xff0c;保证移除的节点删除对应的Pod D. 能够设置Pod重试次数&#xff0c;…...

开源模型应用落地-LangChain高阶-记忆组件-RedisChatMessageHistory正确使用(八)

一、前言 LangChain 的记忆组件发挥着至关重要的作用,其旨在协助大语言模型(LLM)有效地留存历史对话信息。通过这一功能,使得大语言模型在对话过程中能够更出色地维持上下文的连贯性和一致性,进而能够像人类的记忆运作方式那样,进行更为自然、流畅且智能化的交互。 它仿佛…...

解决Openwrt 串口默认是没有密码的方法

将串口登录加入密码方法如下&#xff1a; 步骤一&#xff1a;配置busybox的登录&#xff0c;可以在.config文件中添加如下 CONFIG_BUSYBOX_CONFIG_LOGINy 添加后&#xff0c;需要重新编译busybox。 步骤二&#xff1a;修改target/linux/ramips/base-files/etc/inittab文件 将…...

【vue讲解:v-model 之 lazy、number、trim、与后端交互、小电影案例】

2 v-model 之 lazy、number、trim lazy&#xff1a;等待input框的数据绑定时区焦点之后再变化 number&#xff1a;数字开头&#xff0c;只保留数字&#xff0c;后面的字母不保留&#xff1b;字母开头&#xff0c;都保留 trim&#xff1a;去除首位的空格<!DOCTYPE html> …...

ECCV 2024 | 南洋理工三维数字人生成新范式:结构扩散模型

该论文作者均来自于新加坡南洋理工大学 S-Lab 团队&#xff0c;包括博士后胡涛&#xff0c;博士生洪方舟&#xff0c;以及计算与数据学院刘子纬教授&#xff08;《麻省理工科技评论》亚太地区 35 岁以下创新者&#xff09;。S-Lab 近年来在顶级会议如 CVPR, ICCV, ECCV, NeurIP…...

2024.8.13-算法学习(原创+转载)

一、什么是张量并行&#xff08;Tensor Parallelism&#xff09; &#xff1f; 张量并行&#xff08;Tensor Parallelism&#xff09; 是一种分布式矩阵算法。 随着模型越来越大&#xff0c;模型内的矩阵也越来越大。一个大矩阵的乘法可以拆分成多个小矩阵的运算&#xff0c;…...

beautifulsoup的简单使用

文章目录 beautifulsoup一. beautifulsoup的简单使用1、安装2、如何使用3、对象的种类 二、beautifulsoup的遍历文档树2.1 子节点.contents 和 .children descendants2.2 节点内容.string.text 2.3 多个内容.strings**.stripped_strings** 2.4 父节点.parent.parents 三、beaut…...

【Python】Jupyter Notebook的安装及简单使用

Jupyter Notebook的安装及简单使用1、安装2、language设置为中文3、Jupyter Notebook启动4、Jupyter Notebook的常用快捷方式5、将Notebook笔记转为其他文件格式保存 Jupyter Notebook的安装及简单使用 不安装AnaCoda&#xff0c;但需要使用Jupyter Notebook 1、安装 pip inst…...

中国自动驾驶出租车冲击网约车市场

近年来&#xff0c;中国的自动驾驶技术迅速发展&#xff0c;对传统网约车市场构成了越来越大的冲击。随着科技巨头百度旗下的萝卜快跑等公司加速推广无人驾驶出租车&#xff0c;这一趋势引发了广泛的讨论和担忧。 自动驾驶技术的迅猛发展 中国自动驾驶行业正处于快速发展阶段&…...

解决浏览器书签同步问题,极空间部署开源免费的跨平台书签同步工具『xBrowserSync』

解决浏览器书签同步问题&#xff0c;极空间部署开源免费的跨平台书签同步工具『xBrowserSync』 哈喽小伙伴们好&#xff0c;我是Stark-C~ 作为一个喜欢折腾的数码党&#xff0c;我平时上网冲浪使用的浏览器绝不会只限于一种&#xff0c;就比如说我在上班的地方只会用到Edge浏…...

地震勘探——干扰波识别、井中地震时距曲线特点

目录 干扰波识别反射波地震勘探的干扰波 井中地震时距曲线特点 干扰波识别 有效波&#xff1a;可以用来解决所提出的地质任务的波&#xff1b;干扰波&#xff1a;所有妨碍辨认、追踪有效波的其他波。 地震勘探中&#xff0c;有效波和干扰波是相对的。例如&#xff0c;在反射波…...

《Playwright:微软的自动化测试工具详解》

Playwright 简介:声明内容来自网络&#xff0c;将内容拼接整理出来的文档 Playwright 是微软开发的自动化测试工具&#xff0c;支持 Chrome、Firefox、Safari 等主流浏览器&#xff0c;提供多语言 API&#xff08;Python、JavaScript、Java、.NET&#xff09;。它的特点包括&a…...

1.3 VSCode安装与环境配置

进入网址Visual Studio Code - Code Editing. Redefined下载.deb文件&#xff0c;然后打开终端&#xff0c;进入下载文件夹&#xff0c;键入命令 sudo dpkg -i code_1.100.3-1748872405_amd64.deb 在终端键入命令code即启动vscode 需要安装插件列表 1.Chinese简化 2.ros …...

DBAPI如何优雅的获取单条数据

API如何优雅的获取单条数据 案例一 对于查询类API&#xff0c;查询的是单条数据&#xff0c;比如根据主键ID查询用户信息&#xff0c;sql如下&#xff1a; select id, name, age from user where id #{id}API默认返回的数据格式是多条的&#xff0c;如下&#xff1a; {&qu…...

GitHub 趋势日报 (2025年06月08日)

&#x1f4ca; 由 TrendForge 系统生成 | &#x1f310; https://trendforge.devlive.org/ &#x1f310; 本日报中的项目描述已自动翻译为中文 &#x1f4c8; 今日获星趋势图 今日获星趋势图 884 cognee 566 dify 414 HumanSystemOptimization 414 omni-tools 321 note-gen …...

12.找到字符串中所有字母异位词

&#x1f9e0; 题目解析 题目描述&#xff1a; 给定两个字符串 s 和 p&#xff0c;找出 s 中所有 p 的字母异位词的起始索引。 返回的答案以数组形式表示。 字母异位词定义&#xff1a; 若两个字符串包含的字符种类和出现次数完全相同&#xff0c;顺序无所谓&#xff0c;则互为…...

【SSH疑难排查】轻松解决新版OpenSSH连接旧服务器的“no matching...“系列算法协商失败问题

【SSH疑难排查】轻松解决新版OpenSSH连接旧服务器的"no matching..."系列算法协商失败问题 摘要&#xff1a; 近期&#xff0c;在使用较新版本的OpenSSH客户端连接老旧SSH服务器时&#xff0c;会遇到 "no matching key exchange method found"​, "n…...

基于Springboot+Vue的办公管理系统

角色&#xff1a; 管理员、员工 技术&#xff1a; 后端: SpringBoot, Vue2, MySQL, Mybatis-Plus 前端: Vue2, Element-UI, Axios, Echarts, Vue-Router 核心功能&#xff1a; 该办公管理系统是一个综合性的企业内部管理平台&#xff0c;旨在提升企业运营效率和员工管理水…...

wpf在image控件上快速显示内存图像

wpf在image控件上快速显示内存图像https://www.cnblogs.com/haodafeng/p/10431387.html 如果你在寻找能够快速在image控件刷新大图像&#xff08;比如分辨率3000*3000的图像&#xff09;的办法&#xff0c;尤其是想把内存中的裸数据&#xff08;只有图像的数据&#xff0c;不包…...

【前端异常】JavaScript错误处理:分析 Uncaught (in promise) error

在前端开发中&#xff0c;JavaScript 异常是不可避免的。随着现代前端应用越来越多地使用异步操作&#xff08;如 Promise、async/await 等&#xff09;&#xff0c;开发者常常会遇到 Uncaught (in promise) error 错误。这个错误是由于未正确处理 Promise 的拒绝&#xff08;r…...